This post is a part of a series of posts showing Android implementation of INSTAGRAM with Material Design concept. Today we’ll create flow for photo capturing. This functionality is presented between the 38th and 41st second of the concept video. We’ll omit some details (like FAB button animation, colors, icons) because of a little more work with camera implementation. We’ll back to them in next (probably the last) post of series.
According to previous post, I have to mention that InstaMaterial application has been removed from Google Play Store because of violation of the intellectual property. I fully undertand this reason and in close future I’ll probably prepare version with different layout which is completely different from Instagram application (but still has all features presented in concept video).
APK file built from code described in today’s post is available here.
Now let’s back to this post – here is the final effect which we want to achieve today:
Almost all solutions and animations described today were presented in previous posts. That’s why I won’t focus on detailed descriptions. But also there will be completely new element – camera. Those who have worked with it before know that this compoment is a little bit problematic for implementation. Why? In short – Android as an opened platform can handle very wide range of devices with completely different hardware (especially camera) specification. So if you are starting your work with picture capturing here is short list of things you should care:
- Different preview and captured images sizes
Every device has a finite number of supported sizes (for both – preview and captured image). You should keep in mind that for example some devices don’t handle sizes with 1:1 ratio (squared image) – actually Nexus 5 is one of that devices. So be prepared for dealing with bitmap operations.
- Back/front camera
Every device can have back and front camera. Of course there are devices with only one – back, but keep in mind that some of them has only front camera (i.e. Nexus 7 2012). Of course each of them can have different supported sizes list.
- Different supported features
Some of devices don’t support autofocus, another don’t have zoom. Before us start using any feature you should check if it exists.
- Handling camera operations is very expesive
You should keep in mind that images (espacially those taken in big resolutions) are very expesive to maintain by device. Image processing isn’t the lightest thing so you should take care about memory management and computation outside of main UI thread. Another minus is that camera isn’t ready immediately after calling for it – sometimes you should wait for initialization.
According to some points described above, instead of writing my own implementation I decided to use external library for handling camera operations. I picked CWAC-Camera library which is also a little bit complicated for implementation but it takes care about a lot of edge cases. Even if author plans to completely rewrite it, in my opinion there isn’t any better start point to deal with photo/video capturing.
And the last thing before we start. Camera implemetation in InstaMaterial app is very limited. There is no logic for picture file handling and captured image displaying is very tricky. A lot of work should be done before this code will be ready for production (especially with bitmap cropping, picking right size for image etc.). But I trust that it could be good codebase for more complicated projects. 😄
As always let’s add some resources and less important boilerplate. Also some libraries have been updated. From now project has also new structure:
Don’t treat it as a final reference, but in real projects this is my favorite structure where ui/logic/data has separated trees (another option is to spearate trees by functionality but in smaller projects it can be redundant).
Here is list of commits:
Camera library installation is very simple. All we have to do is add new maven repository in
and new dependency in
Finally we have to add new permissions in
Photo capture screen
Now we can start implementation of capture screen. I considered two ways of implementation – two different activities for taking photo and editing it or one activity with these states. First option is more correct in my opinion (and probably I would use it in production code) but finally I took second one. Why? Because there is no simple way (but it isn’t impossible!) to make transition from camera preview to taken photo preview without little layout hickup. By default camera saves taken picture on external storage and another activity should read it from that place. Unfortunately it generates a little delay so we would have to figure out some solution for passing bitmap between two activities (something more complex than sending bitmap via Intent’s bundle or saving bitmap as a static field).
Let’s start from intro animation. For Activity transition we can use
RevealBackgroundView introduced in one of the previous posts. One more time we have to pass starting location (taken from FAB button this time). The only difference is that we have to add new style which hides status bar in
and set it in
Commit with all described changes is available here.
Now let’s prepare layout for capture screen. As I mentioned it should handle two states – capturing and configuring photo. That’s why we should implement elements transitions. For this we’ll use
ViewSwitcher which can handle animation between his children (in our project we used
TextSwitcher for likes counter animation which extends
Before we start layout implementation we should prepare background for capture button:
and capture options:
First one can be created in .xml file. We can use
layer-list with two circles and circular stroke on top of them:
Second one is even simpler – it’s only simple circular stroke (inside images are provide in project’s resources).
Remember that both of them don’t handle onClick and enable/disable states. You can do it on your own. 😄
Now we have all required elements for TakePhotoActivity layout. Implementation is simple but a little bit complex. That’s why I don’t show source code. Instead of this here is the visualisation of its layout:
Top and bottom panels slide from the right side of screen (thanks to
ViewSwitcher parent). Taken image (white square on the right) appears on top of CameraView when photo is taken.
Here is the full source code of .xml file.
Photo capturing view implementation
Finally we can implement the most important thing – photo capturing. In short here is the list of requirements:
- Intro animation (sliding-in top and bottom panels right after background reveal animation)
- Capturing photo from camera
- Animating between two states – capturing and editing photo
First point is pretty straightforward. Just hide top and bottom panels on Acitivity start and show them when background reveal animation finishes:
Now we have to configure
CameraView. According to CWAC-Camera documentation we have to integrate this view with Activity lifecycle (onResume() and onPause() mehtods):
If we use
CameraView in our layout (instead of using
CameraFragment) our Activity must implement
CameraHostProvider interface. It provides
getCameraHost() method which should return
CameraHost implementation. This interface is resposible for our camera configuration. For our needs we create very simple
MyCameraHost inner class:
In short it takes care about putting the same size to preview and captured image (thanks to that taken image won’t be stretched in compare to preview).
useFullBleedPreview() means almost the same as centerCrop scale type in ImageView. So if
CameraView preview has different size than view layout params (in our example it has!) preview will be cropped and fill whole available area.
saveImage() method is called at the end of capture operation (so even if we modify given bitmap it won’t affect on file saved on phone memory). But keep in mind that
saveImage() method will be called only when we call
takePicture()with proper parameters.
I won’t deep into rest of
CameraHost implementation. Just check the CWAC-Camera documentation for more informations.
All we have to do now is to call
takePicture() methods. It has two boolean arguments:
needByteArray. If first is set to true
saveImage(PictureTransaction xact, final Bitmap bitmap) will be called in our CameraHost implementation. Second calls
saveImage(PictureTransaction xact, byte image).
Of course if we want to see shutter effect we have to implement it on our own. Here is how it looks in our project:
And the last thing – we should handle taken photo and show it. In our example it works in this way: on top of CameraView we have hidden ImageView. If only we get new bitmap from
saveImage() method we put it into
ImageView and show it on top of
CameraView. After pressing back ImageView is hidden and user can see CameraView again. Here is how it looks in our code:
Here is the full source code of TakePhotoActivity.
And that’s all for today. Thank you all for the reading! 😄
Full source code of described project is available on Github repository.