This post will help you install my Material Navigation Drawer template on Android Studio. This will help you create applications using material design faster than any other template.


 First download the latest version of the template on my github:


https://github.com/kanytu/Android-studio-material-template

  • Next copy the folder that's inside the root of the zip file, named `MaterialNavigationDrawerActivity`, to the directory located in :
`{Android Studio location}\plugins\android\lib\templates\activities`

Your directory should now look like this:



  • Now restart your Android Studio and click on
`File > New > New Project...`


Change the name of your application. Click next and set your minimum SDK. This library will only work for API level greater or equal than 7.

  • Select Material Drawer Activity


  • Now change the parameters according to your needs.

On Navigation Style there are 3 different styles:



Drawer bellow toolbar with a darken effect on the toolbar.

Drawer bellow toolbar

Drawer on top of toolbar. Uses ScrimInsetsFrameLayout if API>19


And finally on Drawer Style there are 2 types:


Simple drawer with just a RecyclerView

Google material navigation drawer. Methods like setUserData will be available


Once you click `Finish` Android Studio will start building the project. After all the tasks are finished you can run your project and see your new application.

You can start working from here. Here are some TODO guide lines to help you:

Change NavigationDrawerFragment.getMenu() method in order to add, remove or change the contents of the menu:
public List getMenu() {
        List items = new ArrayList();
        items.add(new NavigationItem(getString(R.string.search), getResources().getDrawable(R.drawable.ic_action_search)));
        items.add(new NavigationItem(getString(R.string.stats), getResources().getDrawable(R.drawable.ic_action_trending_up)));
        items.add(new NavigationItem(getString(R.string.myaccount), getResources().getDrawable(R.drawable.ic_action_account_box)));
        items.add(new NavigationItem(getString(R.string.settings), getResources().getDrawable(R.drawable.ic_action_settings)));
        return items;
    }



Change colors.xml to change your theme colors:
    <color name="myPrimaryColor">#00BCD4</color>
    <color name="myPrimaryDarkColor">#0097A7</color>
    <color name="myAccentColor">#CDDC39</color>



Change onNavigationDrawerItemSelected to change your menu behavior:
    @Override
    public void onNavigationDrawerItemSelected(int position) {
        Fragment fragment;
        switch (position) {
            case 0: //search//todo
                break;
            case 1: //stats
                fragment = getFragmentManager().findFragmentByTag(StatsFragment.TAG);
                if (fragment == null) {
                    fragment = new StatsFragment();
                }
                getFragmentManager().beginTransaction().replace(R.id.container, fragment, StatsFragment.TAG).commit();
                break;
            case 2: //my account //todo
                break;
            case 3: //settings //todo
                break;
        }
    }

Add items to main.xml to create a custom menu:
<menu xmlns:android="http://schemas.android.com/apk/res/android"
      xmlns:app="http://schemas.android.com/apk/res-auto">
    <item
        android:id="@+id/action_search"
        android:title="@string/action_search"
        app:showAsAction="always"
        android:icon="@drawable/ic_action_search_white"/>
    <item
        android:id="@+id/action_filter"
        android:title="@string/action_filter"
        app:showAsAction="ifRoom"
        android:icon="@drawable/ic_action_filter_white"/>
</menu>



Keep adapting the code to your needs and happy programming :)

Example program code can be found here: https://github.com/kanytu/template_example

Note: The template might change once in a while. Make sure to check my github to keep your template up-to-date.

Note 2: Android Studio will remove the template on each update. You need to install it after each update.








Do you know you may be forcing your GPU to dra pixels multiple times without having to do so? Overdraw is a common performance issue associated with the amount of times a pixel needs to be drawn on the screen. Colt McAnlis did a great job explaining this issue in this video:



Overdraw is quite easy to solve once you get used to it. However here are some tips that may come in handy:

Fragment Hierarchy:

You might have a flow with multiple fragments added to the `BackStack` (for example a insertion process with multiple phases). You might have noticed that, in some cases (for example if you're using add instead of replace), your fragment is still visible, and the one you're adding is on top of it:

              
Fragment2 (on the right) added on top of Fragment1(on the left). 
getFragmentManager().beginTransaction().add(R.id.container,new Fragment2(),Fragment2.TAG).commit()

A common and quick solution is to set the background of the fragment you're showing to `@android:color/white` or whatever background you're using on your application. Even though that will work it will add a new layout of colour to your view, resulting in colour overdraw.


As you can see, the blue tells us that that pixel is getting drawn 1 extra time. And those red ones are getting drawn 3 times. One way to eliminate this issue is to hide the other fragment instead of painting on top of it. So if you're adding your fragment in a container view, just find the fragment that's currently attached to it and hide it on the same transaction:
Fragment currentFragment;
FragmentTransaction transaction = getFragmentManager().beginTransaction();
transaction.add(R.id.container, new Fragment2());
if ((currentFragment = getFragmentManager().findFragmentById(R.id.container)) != null) {
    transaction.hide(currentFragment);
}
transaction.commit();
 

Fragment background override:

Sometimes you'll need to have a different background on your fragment, however the `windowBackground` will get drawn too. To remove the background colour from the window you can use:
getActivity().getWindow().setBackgroundDrawable(null);

This will remove the background from your theme but it will not restore it once you detach your fragment. You can use this method to restore the window's background :
public static Drawable getWindowBackground(Context context) {
    TypedValue typedValue = new TypedValue();
    Drawable drawable;
    context.getTheme().resolveAttribute(android.R.attr.windowBackground, typedValue, true);
    if (typedValue.type >= TypedValue.TYPE_FIRST_COLOR_INT && typedValue.type <= TypedValue.TYPE_LAST_COLOR_INT) {
        drawable = new ColorDrawable(typedValue.data);
    } else {
        drawable = context.getResources().getDrawable(typedValue.resourceId);
    }
    return drawable;
}

Just call it on your fragment's `onDetach` method:
getActivity().getWindow().setBackgroundDrawable(Utils.getWindowBackground(getActivity()));

Finally you should avoid inserting ViewGroups inside ViewGroups. Try using RelativeLayout for that purpose. If you fail to remove the overdraw problems from your application don't get too disappointed and look at this screenshot of Google Messenger application and it's massive overdraw in such a simple view:


Edit: They fixed their app already so there is no massive overdraw anymore :)

Full code available on my GitHub: https://github.com/kanytu/Overdraw_example






Probably most of you already used the known `ViewHolder` pattern. If not then you should. I've seen some implementations here and there, however many of them are being wrongly implemented.

Let's have a look at this implementation of a `ViewHolder` on a simple `ArrayAdapter`:

public class ExampleAdapter extends ArrayAdapter<String> {
    private List<String> mData;

    public ExampleAdapter(Context context, List<String> objects) {
        super(context, 0, objects);
        mData = objects;
    }

    @Override
    public View getView(final int position, View convertView, ViewGroup parent) {
        ViewHolder holder;
        if (convertView == null) {
            convertView = LayoutInflater.from(getContext()).inflate(R.layout.simple_list_item_1, null);
            holder = new ViewHolder();
            holder.textView = (TextView) convertView;
            convertView.setTag(holder);
        } else {
            holder = (ViewHolder) convertView.getTag();
        }
        holder.textView.setText(mData.get(position));
        holder.textView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Toast.makeText(getContext(), "Clicked in '" + mData.get(position) + "'", Toast.LENGTH_SHORT).show();
            }
        });

        return convertView;
    }

    private class ViewHolder {
        public TextView textView;
    }
}

Did you spot any mistake? If you didn't I don't blame you. I made some of them a couple of times too.

So let's start with the most obvious one:
LayoutInflater.from(getContext()).inflate(R.layout.simple_list_item_1, null);
Avoid passing null as the parent layout whenever you inflate a view. Otherwise your inflated view might have an incorrect layout display. Also when you pass the parent view you need to specify if you want the inflater to attach it to the parent or not. In this case that's not necessary so you should pass false:
LayoutInflater.from(getContext()).inflate(R.layout.simple_list_item_1, parent, false);

Now that we've fixed this one let's have a look on another one:
private class ViewHolder
Inner classes or non-static nested classes will keep a reference to the outer class. This means that for each `ViewHolder` object you create, it will contain a reference to the whole Adapter. This could lead to some serious memory problems so you should always declare your `ViewHolder` as static unless you create a new class file for it.
static class ViewHolder

Another common mistake we have here is to create and set listeners, like `View.OnClickListener`, every time we load a new view. The views are recycled so it means the listeners will get recycled too. So why not use them instead of overriding them every time we load the view?

Start by moving the listener routine inside the `convertView==null` condition. This will remove the constant allocation of `onClick` listeners. However, this wont refresh the listeners of our view. Which means that we'll end up with incorrect click events because the position is not updated. So how can we fix this? By saving the position of the adapter in the `ViewHolder`. Create a position integer on your `ViewHolder`
static class ViewHolder {
        public TextView textView;
        public int position;
}
and save it every time you reload the view.
holder.position = position;
(...)

Then, every time you need to get the position you clicked just grab the position from the `ViewHolder`:
holder.textView.setOnClickListener(new View.OnClickListener() {

    @Override
    public void onClick(View v) {
        Toast.makeText(getContext(), "Clicked in '" + mData.get(holder.position) + "'", Toast.LENGTH_SHORT).show();
    }
});

Now you may ask... What about `RecyclerView.ViewHolder`? Well... The position hack was already added so every time you need to refer to the position inside a listener just use `viewHolder.getPosition();` `viewHolder.getAdapterPosition()`.  However you might want to check if you're not setting your listeners on `onBindViewHolder` instead of `onCreateViewHolder`. Same logic applies. The bind method will get called when a view get's recycled so you don't want to set your listener on this method. Use `onCreateViewHolder` and `ViewHolder.getPosition();` and you are ready to go.


Finally, you should always avoid allocate big objects on the bind/recycling view method.

Implementing `ViewHolder` is a must-have on adapters and if you're going to do it, try doing it right.

The code for the fixed adapter can be found here: https://gist.github.com/kanytu/343adf3d246f49d683c9








You might wonder how that cool effect on the attachments of Google Messenger app is made. This post will guide through all the steps to get a cool looking ImageView just like that.

Well let's break it through. This is a view hierarchy dump of that app.


As you can see this is a compound view wrapped in one `FrameLayout`. It contains one `ImageButton`, which is the close button you see in the left upper corner, and another `FrameLayout` containing the actual `ImageView` you're attaching.

So far nothing too complicated. Let's get started:


public class ImageWindow extends RelativeLayout {
    private final int CLOSE_SIZE = 32; //google messenger approx 27
    private ImageView mImageView;

    public ImageWindow(Context context) {
        super(context);
        init();
    }

    public ImageWindow(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public ImageWindow(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init();
    }

    private void init() {
        float density = getResources().getDisplayMetrics().density;
        ImageView closeButton = new ImageView(getContext());
        closeButton.setLayoutParams(new RelativeLayout.LayoutParams((int) (CLOSE_SIZE * density), (int) (CLOSE_SIZE * density)));
        closeButton.setBackgroundResource(R.drawable.close_button_drawable);
        closeButton.setImageResource(R.drawable.ic_action_close);
        mImageView = new CustomImageView(getContext(), CLOSE_SIZE);
        RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
        int margin = (int) (10 * density);
        params.setMargins(margin, margin, 0, 0);
        mImageView.setLayoutParams(params);
        mImageView.setAdjustViewBounds(true);
        addView(mImageView);
        addView(closeButton);
    }
}

So we start our `init()` function by saving the current screen density so we don't have to make constant calls to our resources. Then we create the close button which is just a simple `ImageView`. The current layout width and height of the close button are the `CLOSE_SIZE` we set as a field. We will use 32dp. Messenger uses approx. 27dp but we want a bigger touch area. Next we set the resources for both background and image. The background resource is a simple selector drawable with two colors for pressed and not pressed states. Next we create the `ImageView` we want to display. Notice the `CustomImageView`, but for now let's assume it's a normal `ImageView`. Next we set the layout parameters for our image and a top and left margin (10dp should be good). Finally we add the views to our `RelativeLayout`. First our image and next our close button which will stay on top of the image. Finally we create a method that returns our `ImageView` so we can set it outside of this scope by doing `myImageWindow().getImageView()`.

close_button_drawable.xml

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_pressed="true">
        <shape android:shape="oval">
            <solid android:color="#ffa25437"/>
        </shape>

    </item>
    <item>
        <shape android:shape="oval">
            <solid android:color="#ffff7b57"/>
        </shape>
    </item>
</selector>

Now we need to add those round corners we see on the image. Let's see how Messenger is doing that by doing some reverse engineering. This is the `onDraw` of the attachment's `ImageView`.


protected void onDraw(Canvas paramCanvas)
  {
    if (this.zS > 0)
    {
      int i = getWidth();
      int j = getHeight();
      if ((this.zU != i) || (this.zV != j))
      {
        RectF localRectF = new RectF(0.0F, 0.0F, i, j);
        this.zT.reset();
        this.zT.addRoundRect(localRectF, this.zS, this.zS, Path.Direction.CW);
        this.zU = i;
        this.zV = j;
      }
      int k = paramCanvas.getSaveCount();
      paramCanvas.save();
      paramCanvas.clipPath(this.zT);
      super.onDraw(paramCanvas);
      paramCanvas.restoreToCount(k);
      return;
    }
    super.onDraw(paramCanvas);
  }

Hum....Messenger is using a `clipPath` method. And the path is a round rectangle. But this doesn't perform anti alias which means that we can have round corners that are not that round:

Another thing to notice is that there is no `clipPath` with the circle that makes the close border on the left upper corner. What does this mean? Let's check what is the drawable of that `ImageView`:

Hum this is awkward. So Messenger is using an image with a gray border? This means that if the background of the app needs to be changed they'll have to create a new image. We don't want that in our application. So, instead of clipping the path and using a close image with a border, we'll do better. Let's start by creating our `CustomImageView`. Since the `ImageView` needs to know the size of the close button so it can draw a circle with the correct size we will pass our `CLOSE_SIZE` to our constructor.
We will also create our fields. We'll need a `Paint` so we can erase our corners and the close button border, a rectangle path and rectangle the size of our image. Our rectangle will have round corners so we might want to set this corners as a field too and 7dp should do the job.


class CustomImageView extends ImageView {

        private float mCloseSize;
        private Paint mEraser;
        private RectF mRectangle;
        private Path mRectanglePath;
        private int RECTANGLE_CORNER = 7;
        private float mDensity;

        public CustomImageView(Context context, float closeSize) {
            super(context);
            mCloseSize = closeSize;
            init();
        }

        ...
 }

Next on our `init()` method we allocate our fields:


private void init() {
            mEraser = new Paint();
            mEraser.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
            mEraser.setAntiAlias(true);
            mRectanglePath = new Path();
            mDensity = getResources().getDisplayMetrics().density;
            RECTANGLE_CORNER = (int) (RECTANGLE_CORNER * mDensity);
 }

Since our `Paint` will erase content we need it's `Xferode` to be `PorterDuff.Mode.CLEAR`. Enable `AntiAlias` so we don't run into those ugly corners.

Now, as you see, our rectangle is still empty. But since we don't know the size of the image on `onCreate` and we don't want to set it on `onDraw` either we'll use `onSizeChanged` for that:


protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    if (w != oldw || h != oldh) {
        mRectanglePath.reset();
        mRectangle = new RectF(0, 0, getWidth(), getHeight());
        mRectanglePath.addRoundRect(mRectangle, RECTANGLE_CORNER, RECTANGLE_CORNER, Path.Direction.CW);
        mRectanglePath.setFillType(Path.FillType.INVERSE_EVEN_ODD);
    }
    super.onSizeChanged(w, h, oldw, oldh);
}

So here we set the rectangle and our path only when the view changes size. We set it's fill type to `INVERSE_EVEN_ODD` because we don't want to erase the content of the rounded rectangle, we want to erase the inverse. Next let's code our `onDraw`.


@Override
protected void onDraw(Canvas canvas) {
    canvas.saveLayerAlpha(0, 0, canvas.getWidth(), canvas.getHeight(), 255, Canvas.HAS_ALPHA_LAYER_SAVE_FLAG);
    super.onDraw(canvas);
    canvas.drawPath(mRectanglePath, mEraser);
    //3 is the margin of the close circle
    canvas.drawCircle((int) ((mCloseSize * 0.5) * mDensity - ((LayoutParams) getLayoutParams()).leftMargin),
            (int) ((mCloseSize * 0.5) * mDensity - ((LayoutParams) getLayoutParams()).topMargin),
            (int) (((mCloseSize * 0.5) + 3) * mDensity), mEraser);
}

Let me explain this logic. First we call `saveLayerAlpha`. This will allocate an offset bitmap with alpha. Why we do this? Because our view may be opaque and our image may not have an alpha. Since we will erase content we don't want to run into a black background. Next we call `super.onDraw` so our actual image gets drawn on the view. Next we erase our rectangle path for round corners and finally we erase a circle the size of the close button plus 3dp. 3dp should be enough but you can always change this value for a bigger border.

The result is this awesome view:



This concludes my explanation. What can you do next to improve this? Well, you can start by adding a callback to your close button so you can know when the user clicked it for example. I'll leave it up to you to change it to fulfill your needs.



The full code is available on my github,