5

I'm trying to build a custom camera app. I have a class which extends SurfaceView for the camera preview. When the device changes orientation, this surface is destroyed and recreated, leading to a noticeable "glitch" which diminishes user experience.

What I'd like to do is to prevent this camera preview from having to be recreated on orientation change. In addition, I'd like to have some buttons overlaying this preview (e.g. flash, choice of front/back camera etc.) which WOULD rotate on orientation change. This is similar to what the stock camera app does:

Stock camera in landscape mode.

Stock camera in portrait mode. Note that buttons rotate on orientation change.

Best approach I found (described, for example, here) is to force the activity orientation to landscape using the manifest file and, once the picture is taken, rotate the resulting picture depending on the real device orientation. The drawback of this approach, as I understand it, is that this will prevent rotation of the overlaying buttons as well. Alternatively, as I've read a bit about using fragments and retaining fragment instances, I am wondering whether I could accomplish my goal by retaining the fragment containing the camera preview (yes, I'm a noob).

What would be the proper way to do this?

Community
  • 1
  • 1
nerdelicious
  • 53
  • 1
  • 4

1 Answers1

5

If your manifest does not declare that the activity handles orientation changes, and is not locked to a single orientation, then rotation of the device will cause the whole activity to get unloaded and reloaded again. Isn't this what you experience now?

If you specify android:configChanges="orientation|screenSize" for your activity, then the user experience will be much smoother. But still, you cannot keep the camera preview "intact" after such rotation; the layout will be rebuilt.

Finally, the approach of the stock camera app is to set fixed (usually landscape) activity orientation and fake the portrait orientation by changing the buttons and other UI elements. But if you look for the system notifications or navigation (soft) buttons on the bottom, you will understand that actually device is locked in landscape, only controls are redrawn. You will also notice that camera apps avoid standard text widgets, because such widgets cannot be easily rotated.

By the way, there is another misunderstanding in your question. If you want to store the picture as portrait, you will need (or not need) manual rotation of the JPEG regardless of your activity orientation. On most devices, camera can only produce landscape JPEG files. Camera API includes setRotation() method, but usually, this only sets an EXIF flag in the JPEG header. Not all image viewers respect this flag. You may need manual rotation of the image if you don't want to compromize.

Alex Cohn
  • 56,089
  • 9
  • 113
  • 307
  • Thanks. You say that the stock camera app "fakes the portrait orientation by changing the buttons and other UI elements", how do they do that? I guess they set `android:screenOrientation="landscape"` for this activity in the manifest, but how to rotate the UI elements then? – nerdelicious Mar 09 '16 at 08:48
  • They have an OrientationListener, and when they detect that the device has been turned to portrait orientation, they rotate the button icons. You can find this both in the open sourced stock [Camera app](https://android.googlesource.com/platform/packages/apps/Camera.git) and in the [Open camera](https://github.com/almalence/OpenCamera) – Alex Cohn Mar 09 '16 at 12:35
  • The stock camera with the listener seems to be the best approach. But, is there any available solution to rotate the status bar (notifications, battery, etc) with the OrientationListener? – Blo Mar 08 '17 at 22:25
  • @Fllo no it's impossible. You will notice that the preinstalled camera app is fullscreen and hides the system UI elements. – Alex Cohn Mar 09 '17 at 05:48
  • Yes, I saw it, but on Nougat, the native camera app has the same behavior as a stock camera and the status bar is well-oriented with the device. I didn't find how they do it. – Blo Mar 09 '17 at 06:24
  • 2
    I just found a way with [this answer](http://stackoverflow.com/a/21165739/2668136). Using the orientation listener and the `configChanges` option, I'm able to reorganize the views without the default rotation animation. I set the proper positions in `onLayout` and call `invalidate`. Anyway, thanks ;) – Blo Mar 13 '17 at 23:25
  • @Fllo Were you able to reposition the status and navigation bars properly after orientation change? Could you please share a snippet? – nitrico Jun 18 '17 at 13:47
  • @nitrico If I understand correctly, you cannot disable rotation animation if you have status and/or navigation bars. The linked solution only works for full screen Activity. – Alex Cohn Jun 18 '17 at 19:12
  • @AlexCohn Google Camera app has a translucent navigation bar and even though the status bar is not visible you can pull from the top of the screen and see that it is there. If you rotate the device while the status bar is visible you can see that it changes its position (apparently) without recreating the activity. Exactly what I want to achieve. – nitrico Jun 18 '17 at 19:17
  • 2
    @nitrico Maybe these questions will help you to find what you need: [How to create a BottomBar as StickyBottomCaptureLayout in camera2 Android api?](https://stackoverflow.com/questions/42612692/how-to-create-a-bottombar-as-stickybottomcapturelayout-in-camera2-android-api) and [Redraw multiple Paths at same positions from previous layout orientation](https://stackoverflow.com/questions/42986817/redraw-multiple-paths-at-same-positions-from-previous-layout-orientation) – Blo Jun 19 '17 at 09:42