Android - Make Sliding Menu by Navigation Drawer with Material Design style

    NavigationDrawer, from appearing, always become a most interesting widget in Android SDK. Over time with API levels, it has been designed better. Now, with Material Design, navigation drawer gives smooth animation when you swipe across the screen from left edge. You may see this and think about how to implement same functionality in older versions, but now not to worry because using android appcompat support library, we can give same effect to older versions of android app.
    In Material Design, it's layout be come more flexible with animations and developers can customizing it's easily with our own styles. For example, this is toggle menu animation:
In this post, I will make a NavigationDrawer with Toolbar in a simple project. By this, you can deep understanding the way how to implement it.

Create main activity layout

    In running activity, we have a DrawerLayout at root. It's has 2 children layouts (main layout and sliding panel). Please look at this layout structure:
    So, we have a simple activity layout below:
    Note: In this layout, I put Toolbar inside DrawerLayout, so this NavigationDrawer will occult Toolbar when it's appear:
If you want it appear below Toolbar, please include Toolbar layout outside DrawerLayout and output will be like this:

Making Sliding Panel

    As you can see above, in Sliding Panel, we have a ListView. We must setup it's components. In this project, each row is a SocialNetwork name and icon. Layout for ListView row:
   Create a model (Java object):
package devexchanges.info.navigationdrawer;

/**
 * Created by Hong Thai.
 */
public class SocialNetwork {
    private int imageId;
    private int bigImageId;
    private String name;
    private String country;

    public SocialNetwork(int imageId, int bigImageId, String name, String country) {
        this.country = country;
        this.bigImageId = bigImageId;
        this.imageId = imageId;
        this.name = name;
    }

    public int getImageId() {
        return imageId;
    }

    public int getBigImageId() {
        return bigImageId;
    }

    public String getCountry() {
        return country;
    }

    public String getName() {
        return name;
    }
}
Now, we create a customizing a ListView adapter extends from ArrayAdapter<T>, in this, I use a ViewHolder class to make it's view more smoothly. Source code for adapter:
package devexchanges.info.navigationdrawer;

import android.app.Activity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;

import java.util.ArrayList;

import devexchanges.info.materialdesignnavigationdrawer.R;

public class ListViewAdapter extends ArrayAdapter<Socialnetwork> {

    private MainActivity activity;
    private ArrayList<Socialnetwork> socialNetworks;

    public ListViewAdapter(MainActivity activity, int resource, ArrayList<Socialnetwork> objects) {
        super(activity, resource, objects);
        this.activity = activity;
        this.socialNetworks = objects;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {

        ViewHolder holder = null;
        LayoutInflater inflater = (LayoutInflater) activity
                .getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
        // If holder not exist then locate all view from UI file.
        if (convertView == null) {
            // inflate UI from XML file
            convertView = inflater.inflate(R.layout.item_listview, parent, false);
            // get all UI view
            holder = new ViewHolder(convertView);
            // set tag for holder
            convertView.setTag(holder);
        } else {
            // if holder created, get tag from view
            holder = (ViewHolder) convertView.getTag();
        }

        holder.name.setText(getItem(position).getName());
        holder.icon.setImageResource(getItem(position).getImageId());

        //handling each item on click
        //update main layout by this action
        convertView.setOnClickListener(onClickListener(position));

        return convertView;
    }

    private View.OnClickListener onClickListener(final int position) {
        return new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                activity.updateMainLayout(getItem(position));
            }
        };
    }

    private class ViewHolder {
        private ImageView icon;
        private TextView name;

        public ViewHolder(View v) {
            icon = (ImageView) v.findViewById(R.id.icon);
            name = (TextView) v.findViewById(R.id.name);
        }
    }
}
    In activity, set adapter for ListView when initializing Sliding Panel. Set a listener to be notified of drawer events though initDrawerlayout() method and call it in onCreate():
private void initDrawerLayout() {
        setListViewData();
        setListViewHeader();
        //Mount listview with adapter
        adapter = new ListViewAdapter(this, R.layout.item_listview, socialNetworks);
        listView.setAdapter(adapter);

        drawerToggle = new ActionBarDrawerToggle(this, drawerLayout, toolbar,
                R.string.drawer_open, R.string.drawer_close) {

            @Override
            public void onDrawerClosed(View drawerView) {
                super.onDrawerClosed(drawerView);
            }

            @Override
            public void onDrawerOpened(View drawerView) {
                super.onDrawerOpened(drawerView);

            }
        };
        drawerLayout.setDrawerListener(drawerToggle);
    }
    After that, synchronize the state of the drawer indicator/affordance with the linked DrawerLayout by invoke syncState(). I overrided onPostCreate() and onConfigurationChanged() methods:
@Override
    protected void onPostCreate(Bundle savedInstanceState) {
        super.onPostCreate(savedInstanceState);
        drawerToggle.syncState();
    }

    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        drawerToggle.onConfigurationChanged(newConfig);
    }
    We also add a header for ListView to make our Sliding Panel more beautiful (like GooglePlus app):
 private void setListViewHeader() {
        LayoutInflater inflater = getLayoutInflater();
        View header = inflater.inflate(R.layout.header_listview, listView, false);
        listView.addHeaderView(header, null, false);
    }

Coding Main Layout

    As description above (ListViewAdapter class), when click at each Sliding Panel item, we will update main layout by replace a Fragment to Activity. Put this code to main activity:
    /**
     * Replace fragment to Main layout
     * @param socialNetwork
     */
    public void updateMainLayout(SocialNetwork socialNetwork) {
        FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();

        transaction.replace(R.id.container, ContentFragment.newInstance(socialNetwork));
        transaction.commit();

        //close navigation drawer after replace fragment
        drawerLayout.closeDrawers();
    }
    And source for ContentFragment is simple. Put/get data by a Bundle object and update data to views by getArguments():
package devexchanges.info.navigationdrawer;

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;

import devexchanges.info.materialdesignnavigationdrawer.R;

public class ContentFragment extends Fragment {

    private TextView socialNetworkName;
    private TextView country;
    private ImageView image;

    public static ContentFragment newInstance(SocialNetwork nw) {
        Bundle args = new Bundle();
        ContentFragment fragment = new ContentFragment();
        args.putString("SN_NAME", nw.getName());
        args.putInt("SN_LOGO", nw.getBigImageId());
        args.putString("SN_COUNTRY", nw.getCountry());
        fragment.setArguments(args);
        return fragment;
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment_content, container, false);
    }

    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        socialNetworkName = (TextView)view.findViewById(R.id.social_network);
        country = (TextView)view.findViewById(R.id.country);
        image = (ImageView)view.findViewById(R.id.icon);

        updateData();
    }

    private void updateData() {
        socialNetworkName.setText(getArguments().getString("SN_NAME"));
        image.setImageResource(getArguments().getInt("SN_LOGO"));
        country.setText(getArguments().getString("SN_COUNTRY"));
    }
}
Coming here, we have complete our project core. This is full code of main activity (the most important file):
package devexchanges.info.navigationdrawer;

import android.support.v4.app.FragmentTransaction;
import android.content.res.Configuration;
import android.os.Bundle;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.ActionBarDrawerToggle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.View;
import android.widget.ListView;

import java.util.ArrayList;

import devexchanges.info.materialdesignnavigationdrawer.R;

public class MainActivity extends AppCompatActivity {

    private Toolbar toolbar;
    private DrawerLayout drawerLayout;
    private ActionBarDrawerToggle drawerToggle;
    private ListView listView;
    private ArrayList<Socialnetwork> socialNetworks;
    private ListViewAdapter adapter;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        socialNetworks = new ArrayList<>();
        findViewById();
        setSupportActionBar(toolbar);

        initDrawerLayout();
    }

    private void findViewById() {
        listView = (ListView) findViewById(R.id.list);
        toolbar = (Toolbar) findViewById(R.id.toolbar);
        drawerLayout = (DrawerLayout) findViewById(R.id.drawerLayout);
    }

    private void initDrawerLayout() {
        setListViewData();
        setListViewHeader();
        //Mount listview with adapter
        adapter = new ListViewAdapter(this, R.layout.item_listview, socialNetworks);
        listView.setAdapter(adapter);

        drawerToggle = new ActionBarDrawerToggle(this, drawerLayout, toolbar,
                R.string.drawer_open, R.string.drawer_close) {

            @Override
            public void onDrawerClosed(View drawerView) {
                super.onDrawerClosed(drawerView);
            }

            @Override
            public void onDrawerOpened(View drawerView) {
                super.onDrawerOpened(drawerView);

            }
        };
        drawerLayout.setDrawerListener(drawerToggle);
    }

    private void setListViewHeader() {
        LayoutInflater inflater = getLayoutInflater();
        View header = inflater.inflate(R.layout.header_listview, listView, false);
        listView.addHeaderView(header, null, false);
    }

    private void setListViewData() {
        socialNetworks.add(new SocialNetwork(R.mipmap.zalo_small, R.mipmap.zalo_big, "Zalo", "VIETNAM"));
        socialNetworks.add(new SocialNetwork(R.mipmap.fb_small, R.mipmap.fb_big, "Facebook", "USA"));
        socialNetworks.add(new SocialNetwork(R.mipmap.tw_small, R.mipmap.tw_big, "Twitter", "USA"));
        socialNetworks.add(new SocialNetwork(R.mipmap.pin_small, R.mipmap.pin_big, "Pinterest", "USA"));
        socialNetworks.add(new SocialNetwork(R.mipmap.gp_small, R.mipmap.gp_big, "Google+", "USA"));
        socialNetworks.add(new SocialNetwork(R.mipmap.baidu_small, R.mipmap.baidu_big, "Baidu", "CHINA"));
    }

    @Override
    protected void onPostCreate(Bundle savedInstanceState) {
        super.onPostCreate(savedInstanceState);
        drawerToggle.syncState();
    }

    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        drawerToggle.onConfigurationChanged(newConfig);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.menu_main, menu);

        return super.onCreateOptionsMenu(menu);
    }

    /**
     * Replace fragment to Main layout
     * @param socialNetwork
     */
    public void updateMainLayout(SocialNetwork socialNetwork) {
        FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();

        transaction.replace(R.id.container, ContentFragment.newInstance(socialNetwork));
        transaction.commit();

        //close navigation drawer after replace fragment
        drawerLayout.closeDrawers();
    }
}

Some necessary features/layouts

    ContentFragment layout in xml, display ListView elements details:
    Layout for ListView header:
    Customizing a theme based on "Theme.Appcompat.Light.NoActionBar" and apply for whole project:
    Toolbar layout:
    Colors resource:
    Remember add support libraries dependency in your local build.gradle file:
compile 'com.android.support:appcompat-v7:22.2.0'
compile 'com.android.support:support-v4:22.2.0'

Running application



Share


Previous post
« Prev Post
Next post
Next Post »