Android native question - Button positing with device scaling

Hi guys.

I am developing a native android app and I have a question about button positing.

My problem: I have a background image and a transparent button that is correctly position (relative to the background) when I am using the Nexus 1 device. When I change the device to Nexus 5, the background adjust scales automatically, but the button position does not follow it. On Nexus 7, I also notice that the button width/height is not being scaled. (see image attached).

How do I make my transparent button to stay in a specific position relative to the background and make its width/height auto scales?

Here is my xml layout file:

\<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/container" android:layout\_width="match\_parent" android:layout\_height="match\_parent" android:background="@drawable/scene1" tools:context="com.mypackage.app.MainActivity" tools:ignore="MergeRootFrame" \> \<Button android:id="@+id/button1" android:layout\_width="220dip" android:layout\_height="60dip" android:layout\_alignParentBottom="true" android:layout\_alignParentLeft="true" android:layout\_marginBottom="192dp" android:onClick="button1Handler" /\> \</RelativeLayout\>

I’m moving this to the Enterprise forum.

Rob

So, after looking a lot I came to the conclusion that there is not simple way of having a Corona-style content scaling.

I found a good paper that talks about the different approaches that can be used. The paper is available at: http://www.vanteon.com/downloads/Scaling_Android_Apps_White_Paper.pdf

There isn’t a simple means of doing this on Android.  Google expects you to position your views using layouts such as the RelativeLayout, FrameLayout, LinearLayout, etc. which positions your views relative to other views in the activity… which also means that they’ll be positioned in various positions on devices having different resolutions.

If you’re trying to set your button’s position to a very particular pixel position, relative to what appears in Corona, then your only option is to use Android’s AbsoluteLayout.  That layout allows you to set the (x,y) pixel position of your view onscreen.  I haven’t done this in XML before, but I do know how to do this in Java.  What you need to do is create your own AbsoluteLayout object that will serve as the container for your button and add that layout to the layout returned by CoronaActivity.getOverlayView().  You would then add your button to your layout via AbsoluteLayout .addView(yourButton, parameters) where “parameters” is an instance of the “AbsoluteLayout.LayoutParams” inner class which allows you to set the x/y position in pixels.  You can fetch the pixel width and height of Corona’s OpenGL view via the CoronaActivity.getContentWidthInPixels() and CoronaActivity.getContentHeightInPixels() methods, which should help you position your views relative to what is rendered in Corona.

Here are some links to help you out…

http://docs.coronalabs.com/native/android/html/com/ansca/corona/CoronaActivity.html#getOverlayView()

http://docs.coronalabs.com/native/android/html/com/ansca/corona/CoronaActivity.html#getContentHeightInPixels()

http://developer.android.com/reference/android/widget/AbsoluteLayout.html

http://developer.android.com/reference/android/widget/AbsoluteLayout.LayoutParams.html

Also check out our “ExtendingUI” sample app that is included with Corona Enterprise.  It shows you how to add Android views on top of the CoronaActivity.  It doesn’t do the same thing that you’re after, but there are other bits of gold in its Java code that might help you out.

Hi Joshua.

Thanks for the reply.

Yes, you are right. To make something similar to Corona I would have to use the Absolute Layout and them set my objects position via code (For others wanting to know how to do it, please check the paper that I linked in the post above). The only downside on that approach is that I think the xml layout preview will not reflect these dynamic positions (I think, I didn’t test it).

In my case, since the button was transparent, I replaced the button with a LinearLayout (which resizes automatically with the device screen) and it worked.

Thanks!

Does it have to be a button in the middle of the screen?

Our “ExendingUI” sample project shows you how to display a button bar docked at the top and bottom of the activity using Android’s UI classes.  We just add them to Corona’s FrameLayout and dock them by setting the gravity to top or bottom.  Just trying to find you a simpler alternative.

Thanks for you help Josua. I really appreciate it.

The whole problem is that you guys from Corona made me spoiled with all the easiness of the platform. Now when I go to native, it is all unproductive :slight_smile:

No, the buttons don’t need to be in the center. I actually was creating a mockup, and used the layout image as the background and want to create an transparent button at the same position where it is displayed in the background image.  

But, Android scales the background but not the position of the objects, so my transparent button ended misplaced when different devices.

For the mockup purposes I solved using LinearLayouts. For the final approach, I was actually thinking in using Corona Cards, going Android native only when I needed to access the resource that Corona does not provide (front/back camera inside the app).

On Android, Corona Cards/Enterprise actually has an undocumented “CameraView” Java class that supports both the front/rear camera.  We use it as a fallback mechanism in case we can’t find a Camera app on the device (should never happen; keyword is “should”) or if your app does not have the required Android permissions.  This CameraView can easily be embedded within an Activity.  You’re welcome to use it on Android, with the one disclaimer being that since it is undocumented we reserve the right to change it in the future.

Wow, simply amazing. Definitively going to use it.

No problem in being undocumented, the app is a promotional marketing tool for a client and will only be live for 1 month.

Oh and one bit of warning, our “CameraView” on Android is a native UI Java control and is not rendered in OpenGL… but the advantage of that is it’ll work on Android 2.x, because only Android 4.0 and higher supports rendering camera out to OpenGL.

If you want to see it in action in a Corona Simulator app, then build our Camera sample app without the CAMERA permission.  It should switch to our Corona made CameraActivity and CameraView… with the one issue being is that the CameraActivity stretches the camera output to fill the screen, but that’s just an issue with the activity.  In native code, you would set the width and height of the CameraView to match the aspect ratio of the camera’s resolution.

Joshua, you are the man.

A friend was testing the mockup yesterday and he complained about the camera making him thinner. It was basically what you mentioned above, the CameraPreview being used on full screen and not on its original aspect radio.

Thanks man.

Hi Joshua.

As you instructed, I tried to build the Corona Camera Sample app without the permission but when I ran the app on my Android, it shows me the text “Camera not available”. (Didn’t use CoronaCards or Enterprise)

Now, using CoronaCards, for some reason, I am not being grant access to the Camera, even with me requesting them in the AndroidManifest.xml

I created a new thread about it (http://forums.coronalabs.com/topic/47441-not-accessing-camera-on-android/)

Do you have any ideas of what it may be?

I also tried adding the camera permissions to the build.settings (Corona code) and also even tried deleting the build.settings file (I don’t know if it would create conflict with the native AndroidManifest.xml), but both attempts algo gave the same error (Camera not accessible).

UPDATE: I solved the problem of the permission. It was my fault.

Regarding using Corona’s built-in CameraActivity/CameraView, you have to remove the WRITE_EXTERNAL_STORAGE permission.  Without this permission, there would be no means for Corona to access the photo from the device’s default camera app.  You are still required to use the CAMERA permission or else Android won’t allow your application to access the camera at all.

Regarding Corona Cards, I’m not the developer for it, but my understanding is that it can’t support the camera because it would require access to the Activity.onActivityResult() method to know if a photo was taken or not and where the photo was saved to, and the CoronaView does not have access to the activity.  That is, on Android, only an activity can perform this operation, not a view.

Now, Corona Enterprise can definitely support the camera because you would use our CoronaActivity class instead of the CoronaView, which does have access to the onActivityResult() method.  I’m quite positive that this would work.  You can modify one of our Corona Enterprise sample projects to do exactly this quite easily.

Thanks.

About CoronaCards, I am trying to use it only for showing the Camera. I would use Corona display.captureBounds to “take the photo”. So, maybe it would work…  I fixed the permission problem to get the Camera, but now I reached a point where the app is not wanting to create the SurfaceHolder for the CameraPreview.  It appears something related to UI/background threads…  ( Log: "Runtime error 05-01 18:47:07.078: I/Corona(14334): java.lang.RuntimeException: Can’t create handler inside thread that has not called Looper.prepare() )

Still investigating it…  

You can try using our hidden CameraView.java class that I mentioned up above instead.  It would be a lot easier than trying to implement it yourself.  Seriously, it took me about a week to get it just right when I implemented over 2 years ago.  Trying to handle this natively, especially for all Android versions and across all devices handling different camera mounting orientations is not as simple as it looks.

I believe the Eclipse and Android Studio IDEs should show you all of the hidden methods available in our CamerView Java class via reflection.

Actually, I already created my CameraPreview class and it is working fine. I agree, handling the camera orientation and setting the correct aspect ratio took some work for me also.

My situation is: need to create an app that takes a photo and uploads it to server. Simple as that. The “problem” is that the camera needs to show a frame above the camera view, so I need to access the camera from inside the app and not simply redirects the user to the default camera app.

I already made a Android native code that does that. But I was trying to use the Corona code that I made for the iOS version, since Corona handles the different devices screen in a much better way. In addition, the corona code is running much more smooth than the Android code.

So I tried yesterday and today to use CoronaCards. My ideia was: I show the camera in a view and show the CoronaCards above that view. Finally made CoronaCords work today and now I reached the point that the Camera don’t show up. I am giving up, I will go all natively (unfortunately).

I would glad try to use Corona Enterprise and play around even trying to make the camera fill shape for Android, but I am afraid I would not have enough time to learn CoronaEnterprise integration in the short time available before the deadline.

Thanks for the help.

I see.  Well, Corona Enterprise for Android effectively provides the same class framework as Corona Cards.  In fact, I’m pretty sure that our CoronaActivity Java class is available in Corona Cards, but I can understand if you don’t have time to switch to that.

There are definitely some pluses and minuses between Corona Enterprise and Corona Cards.  The biggest advantage with Corona Enterprise is that it provides a CoronaActivity which has access to *all* features, but you are limited to one instance of the Corona runtime at a time.

Corona Cards allows you to run multiple instances of Corona at the same time and display them as resizable views, but since they don’t have access to the activity, the features they can provide is limited.  But, you’re still able to use Corona’s primary features such as rendering and audio, which is what Corona is mostly used for.

Hi Joshua.

I finished developing the app using native (Java) and I still have 2 weeks to deliver it, so I decided to learn how to use Corona Enterprise and maybe create a plugin to access the android camera.

I already learned the basics here and I am successfully building my “own camera plugin” using my CameraPreview class. (Later I will try to use the Corona built-in CameraView). 

The app is running fine for 1 little thing that I think maybe you may know what it is.

My plugin is simple:   plugin.show()  to show the camera inside the app (taking the maximum screen while keeping aspect ratio) and plugin.hide() to hide the camera view.  Both are working fine.

The problem is: When I first open the app and call the plugin.show(), nothing is displayed. But if the app is suspended and then resumed, the camera will start to show normally.

I was thinking if that problem may be to caused due to some layout z-position.  What do you think?  Is there any trick regarding View position that we may be aware of?

PS: I am adding my camera view (mPreview) to the screen using:

getParent().addView(mPreview);

If your camera preview derives from Android’s SurfaceView, then you will indeed run into some quirky/buggy z-ordering issues with it.  You see, it’s a view that is rendered on a separate thread and the Android OS has a bad habit of rendering too soon or too late, making it render in what appears to be in the wrong z-order.  Google offers some APIs to help work-around this behavior via their setZOrderMediaOverlay() and setZOrderOnTop() methods.

   http://developer.android.com/reference/android/view/SurfaceView.html#setZOrderMediaOverlay(boolean

But that said, I’m not sure if that’s the appropriate solution or not.  Especially since the GLSurfaceView that Corona renders to is a threaded SurfaceView as well.  Your camera preview and our OpenGL view *might* render at the wrong order, even though the z-order within the activity is correct.  You’ll have to experiment with it on your end.

One more thing.  You’re expected to add your view to the layout returned from the CoronaActivity.getOverlayView() method.  This ensures that your view is always on top of Corona’s view (ie: our GLSurfaceView and any views you’ve created via the native APIs), but also puts your view behind the splashscreen view if your app has any Default.png files in it (probably not, but just in case).

Perfect. I will try both (using the z functions and also using the getOverlayView). Thanks!