Monday, 26 December 2016

Android: Creating a Custom Navigation Drawer

The navigation drawer is a panel that displays the app’s main navigation options on the left edge of the screen. It is hidden most of the time, but is revealed when the user swipes a finger from the left edge of the screen or, while at the top level of the app, the user touches the app icon in the action bar.
This post describes how to implement a Navigation Drawer using DrawerLayout a few simple steps.

1. Create a Drawer Layout:
    Create a file under res/layout/ folder named as activity_home.xml:

 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/top_parent"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true">

    <include
        android:id="@+id/toolbar"
        layout="@layout/layout_toolbar" />

    <android.support.v4.widget.DrawerLayout
        android:id="@+id/drawer_layout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_below="@+id/toolbar">

        <!-- The main content view -->
        <FrameLayout
            android:id="@+id/content_frame"
            android:layout_width="match_parent"
            android:layout_height="match_parent">

        </FrameLayout>

        <!-- The navigation drawer -->
        <ListView
            android:id="@+id/left_drawer"
            android:layout_width="240dp"
            android:layout_height="match_parent"
            android:layout_gravity="start"
            android:background="@color/colorPrimaryDark"
            android:choiceMode="singleChoice"
            android:divider="@android:color/white"
            android:dividerHeight="0.5dp" />

    </android.support.v4.widget.DrawerLayout>
</RelativeLayout>

2. Create a Toolbar layout for your app's actionbar:
    Create a file under res/layout/ folder named as layout_toolbar.xml to show the home button animation as suggested in Material design:

<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.Toolbar xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/toolbar"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="?attr/colorPrimaryDark">

    <TextView
        android:id="@+id/toolbar_title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Title"
        android:textAlignment="center"
        android:textAppearance="@android:style/TextAppearance.Material.Widget.Toolbar.Title"
        android:textColor="@android:color/white" />
</android.support.v7.widget.Toolbar>

3. Initialize your drawer list:
    Create a class named as HomeActivity.java and use the following code snippet:

public class HomeActivity extends AppCompatActivity {
    private DrawerLayout mDrawerLayout;
    private ListView mDrawerList;
    private ActionBarDrawerToggle mDrawerToggle;
    ArrayList<NavigationDrawerItemPOJO> pojoArrayList;
    Toolbar toolbar;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_home);
     
        mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
        mDrawerList = (ListView) findViewById(R.id.left_drawer);

        pojoArrayList = new ArrayList<>();
        NavigationDrawerItemPOJO nav = new NavigationDrawerItemPOJO();
        nav.setIcon(R.mipmap.ic_launcher);
        nav.setName("Name 1");
        pojoArrayList.add(nav);
       
        mDrawerList.setAdapter(new NavigationDrawerAdapter(this, R.layout.item_navigation_drawer, pojoArrayList));

        // Set the list's click listener
        mDrawerList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> adapterView, View view, int position, long l) {
                Toast.makeText(getApplicationContext(), "Item clicked: " + position, Toast.LENGTH_SHORT).show();
                mDrawerLayout.closeDrawer(mDrawerList);
            }
        });
       .. .. ..
    }
}


4. Setup your drawer layout:

public class HomeActivity extends AppCompatActivity {
    private DrawerLayout mDrawerLayout;
    private ListView mDrawerList;
    private ActionBarDrawerToggle mDrawerToggle;
    ArrayList<NavigationDrawerItemPOJO> pojoArrayList;
    Toolbar toolbar;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_home);
        initToolbar();

.........

        mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
        mDrawerToggle = new ActionBarDrawerToggle(this, mDrawerLayout, toolbar, R.string.drawer_open, R.string.drawer_close) {

            /** Called when a drawer has settled in a completely closed state. */
            public void onDrawerClosed(View view) {
                super.onDrawerClosed(view);
                invalidateOptionsMenu(); // creates call to onPrepareOptionsMenu()
                syncState();
            }

            /** Called when a drawer has settled in a completely open state. */
            public void onDrawerOpened(View drawerView) {
                super.onDrawerOpened(drawerView);
                invalidateOptionsMenu(); // creates call to onPrepareOptionsMenu()
                syncState();
            }
        };

        // Set the drawer toggle as the DrawerListener
        mDrawerLayout.addDrawerListener(mDrawerToggle);
        mDrawerToggle.syncState();
    }

    //Set the custom toolbar
    public void initToolbar() {
        toolbar = (Toolbar) findViewById(R.id.toolbar);
        if (toolbar != null) {
            setSupportActionBar(toolbar);
        }
        getSupportActionBar().setDisplayHomeAsUpEnabled(true);
        getSupportActionBar().setHomeButtonEnabled(true);
        getSupportActionBar().setDisplayShowTitleEnabled(false);
        TextView txtvw = (TextView) findViewById(R.id.toolbar_title);
        txtvw.setText("Nishant");
    }
.....
}

5. Create your drawer adapter and drawer item POJO:

public class NavigationDrawerAdapter extends ArrayAdapter<NavigationDrawerItemPOJO> {
        private final Context context;
        private final int layoutResourceId;
        private ArrayList<NavigationDrawerItemPOJO> arrayList;

        public NavigationDrawerAdapter(Context context, int layoutResourceId,
                                                     ArrayList<NavigationDrawerItemPOJO> arrayList) {
            super(context, layoutResourceId, arrayList);
            this.context = context;
            this.layoutResourceId = layoutResourceId;
            this.arrayList = arrayList;
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            LayoutInflater inflater = ((Activity) context).getLayoutInflater();
            View v = inflater.inflate(layoutResourceId, parent, false);
            ImageView imageView = (ImageView) v.findViewById(R.id.navDrawerImageView);
            TextView textView = (TextView) v.findViewById(R.id.navDrawerTextView);
            NavigationDrawerItemPOJO choice = arrayList.get(position);
            imageView.setImageResource(choice.getIcon());
            textView.setText(choice.getName());
            return v;
        }
    }

    public class NavigationDrawerItemPOJO {
        public int getIcon() {
            return icon;
        }

        public void setIcon(int icon) {
            this.icon = icon;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public int icon;
        public String name;

        public NavigationDrawerItemPOJO() {
        }
   }

That's all..!!
After this your home activity will look something like this:

HomeActivity.java
 
public class HomeActivity extends AppCompatActivity {
    private DrawerLayout mDrawerLayout;
    private ListView mDrawerList;
    private ActionBarDrawerToggle mDrawerToggle;
    ArrayList<NavigationDrawerItemPOJO> pojoArrayList;
    Toolbar toolbar;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_home);
        initToolbar();
        mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
        mDrawerList = (ListView) findViewById(R.id.left_drawer);

        pojoArrayList = new ArrayList<>();
        NavigationDrawerItemPOJO nav = new NavigationDrawerItemPOJO();
        nav.setIcon(R.mipmap.ic_launcher);
        nav.setName("Name 1");
        pojoArrayList.add(nav);
        NavigationDrawerItemPOJO nav1 = new NavigationDrawerItemPOJO();
        nav1.setIcon(R.mipmap.ic_launcher);
        nav1.setName("Name 2");
        pojoArrayList.add(nav1);
        mDrawerList.setAdapter(new NavigationDrawerAdapter(this, R.layout.item_navigation_drawer, pojoArrayList));

        // Set the list's click listener
        mDrawerList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> adapterView, View view, int position, long l) {
                Toast.makeText(getApplicationContext(), "Item clicked: " + position, Toast.LENGTH_SHORT).show();
                mDrawerLayout.closeDrawer(mDrawerList);
            }
        });

        mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
        mDrawerToggle = new ActionBarDrawerToggle(this, mDrawerLayout, toolbar, R.string.drawer_open, R.string.drawer_close) {

            /** Called when a drawer has settled in a completely closed state. */
            public void onDrawerClosed(View view) {
                super.onDrawerClosed(view);
                invalidateOptionsMenu(); // creates call to onPrepareOptionsMenu()
                syncState();
            }

            /** Called when a drawer has settled in a completely open state. */
            public void onDrawerOpened(View drawerView) {
                super.onDrawerOpened(drawerView);
                invalidateOptionsMenu(); // creates call to onPrepareOptionsMenu()
                syncState();
            }
        };

        // Set the drawer toggle as the DrawerListener
        mDrawerLayout.addDrawerListener(mDrawerToggle);
        mDrawerToggle.syncState();
    }

    //Set the custom toolbar
    public void initToolbar() {
        toolbar = (Toolbar) findViewById(R.id.toolbar);
        if (toolbar != null) {
            setSupportActionBar(toolbar);
        }
        getSupportActionBar().setDisplayHomeAsUpEnabled(true);
        getSupportActionBar().setHomeButtonEnabled(true);
        getSupportActionBar().setDisplayShowTitleEnabled(false);
        TextView txtvw = (TextView) findViewById(R.id.toolbar_title);
        txtvw.setText("Nishant");
    }

    public class NavigationDrawerAdapter extends ArrayAdapter<NavigationDrawerItemPOJO> {
        private final Context context;
        private final int layoutResourceId;
        private ArrayList<NavigationDrawerItemPOJO> arrayList;

        public NavigationDrawerAdapter(Context context, int layoutResourceId, ArrayList<NavigationDrawerItemPOJO> arrayList) {
            super(context, layoutResourceId, arrayList);
            this.context = context;
            this.layoutResourceId = layoutResourceId;
            this.arrayList = arrayList;
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            LayoutInflater inflater = ((Activity) context).getLayoutInflater();
            View v = inflater.inflate(layoutResourceId, parent, false);
            ImageView imageView = (ImageView) v.findViewById(R.id.navDrawerImageView);
            TextView textView = (TextView) v.findViewById(R.id.navDrawerTextView);
            NavigationDrawerItemPOJO choice = arrayList.get(position);
            imageView.setImageResource(choice.getIcon());
            textView.setText(choice.getName());
            return v;
        }
    }

    public class NavigationDrawerItemPOJO {
        public int getIcon() {
            return icon;
        }

        public void setIcon(int icon) {
            this.icon = icon;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public int icon;
        public String name;

        public NavigationDrawerItemPOJO() {
        }
    }
}


Run it and enjoy your app with a drawer that has material design drawer animation.
In this post your drawer will slide below the actionBar, just to show the animation. You can have it over the actionBar as well.

Share, Comment and Reply if any issues.


Friday, 16 December 2016

Android: Implement "Press BACK again to exit"

You might have noticed a pattern in many android applications these days in which on pressing the back button to exit the application it gives you a message which says "Please press BACK again to exit" .
This post is regarding implementing this feature in your android app.
To implement this, you just need to do the following things:

1. In your activity in which you want to implement this feature, add a variable as follows:
                         
                                 private boolean isBackPressedOnce = false;  

2. Now add the following code in the onBackPressed() method of your activity as follows:

    @Override
    public void onBackPressed() {
            if (isBackPressedOnce) {
                super.onBackPressed();
                return; // exit the appliactaion
            }

            this.isBackPressedOnce = true;
            Toast.makeText(this, "Please press BACK again to exit", Toast.LENGTH_SHORT).show();

            new Handler().postDelayed(new Runnable() {
                @Override
                public void run() {
                    isBackPressedOnce = false;
                    // Runnable to change the value of variable isBackPressedOnce to true after 2 
                    // seconds if you take more than 2 seconds of time in pressing the back button again.
                }
            }, 2000);

    }

That's all. Now on pressing the back button in your app it will show you the message "Please press BACK again to exit".

Share & comment if any issues.


Sunday, 11 December 2016

Android Animations: Expand & Collapse

In reference to my previous post here, this post is regarding some other styles of animation that you can use in your app to make it look more subtle.
As you know Animations in android can add subtle visual cues that notify users about what's going on in your app and improve their mental model of your app's interface. Animations are especially useful when the screen changes state, such as when content loads or new actions become available. Animations can also add a polished look to your app, which gives your app a higher quality feel.

So lets get started now: This tutorial shows you simple way to add Expand & Collapse style animation in your app in just a few steps.
The Expand animation is somewhat like unfolding a view step by step or you can say opening a view from 0 to 100 slowly.
So to add the expand animation in your app just add the following method in your code:

public static void expandAnimation(final View v) {
        v.measure(WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.WRAP_CONTENT);
        final int targetHeight = v.getMeasuredHeight();
        // Older versions of android (pre API 21) cancel animations for views with a height of 0.
        v.getLayoutParams().height = 1;
        v.setVisibility(View.VISIBLE);
        Animation a = new Animation() {
            @Override
            protected void applyTransformation(float interpolatedTime, Transformation t) {
                v.getLayoutParams().height = interpolatedTime == 1
                        ? WindowManager.LayoutParams.WRAP_CONTENT
                        : (int) (targetHeight * interpolatedTime);
                v.requestLayout();
            }
            @Override
            public boolean willChangeBounds() {
                return true;
            }
        };
        // 1dp/ms
        a.setDuration(300);
        v.startAnimation(a);
    }
          

Now to set this animation on any view, just call this method and pass that view which you want to expand, as the parameter to this method as follows:

                    expandAnimation(findViewById(R.id.myview));       

Now after expanding the view you also need to close it or you can say you'll need to collapse it.
Don't worry, closing it is also as simple as expanding it.
Just add the following method in your code to collapse your view.    

public static void collapseAnimation(final View v) {
        final int initialHeight = v.getMeasuredHeight();
        Animation a = new Animation() {
            @Override
            protected void applyTransformation(float interpolatedTime, Transformation t) {
                if (interpolatedTime == 1) {
                    v.setVisibility(View.GONE);
                } else {
                    v.getLayoutParams().height = initialHeight - (int) (initialHeight * interpolatedTime);
                    v.requestLayout();
                }
            }
            @Override
            public boolean willChangeBounds() {
                return true;
            }
        };
        // 1dp/ms
        a.setDuration(300);
        v.startAnimation(a);
    }
        

Now to set this collapse animation on any view, just call this method as you called the method to expand it and pass that view which you want to collapse, as the parameter to this method as follows:

                       collapseAnimation(findViewById(R.id.myview));    

That's all. Use it and have fun.
Comment if any issues and share.