View Pager With Circular Indicator (without any library)

There are number of libraries available to create circular indicator for view pager. But i think you don't require that all heavy weight libraries. They all filled with so much congested and non used code in it. It may happen you may come across some odd issue with those libs. So to save your time and even with easy to understand code i have written this tutorial. Go through full tutorial and you find how easy it is.

I have created View pager for introduction of the application (Current trend of application start screen almost in all Google apps). But you can use it as per your requirement.

viewpager demo


Steps

Step -1
Create activity (welcome screen) which holds pager to display images and give proper name to it. In this tutorial it is ViewPagerDemo Activity. now put below code inside into xml file (layout file).

activity_viewpager_demo.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    tools:context="com.androprogrammer.tutorials.samples.ViewPagerDemo">

    <android.support.v4.view.ViewPager
        android:id="@+id/pager_introduction"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:fitsSystemWindows="true"
        tools:listitem="@layout/pager_item" />

    <RelativeLayout
        android:id="@+id/viewPagerIndicator"
        android:layout_width="match_parent"
        android:layout_height="55dp"
        android:layout_alignParentBottom="true"
        android:layout_marginTop="5dp"
        android:gravity="center">

        <LinearLayout
            android:id="@+id/viewPagerCountDots"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_centerHorizontal="true"
            android:gravity="center"
            android:orientation="horizontal" />

        <ImageButton
            android:id="@+id/btn_next"
            android:layout_width="42dip"
            android:layout_height="42dip"
            android:layout_alignParentRight="true"
            android:layout_marginRight="15dip"
            android:background="@drawable/btn_round_semitransperant"
            android:src="@mipmap/ic_navigation_arrow_forward" />

        <ImageButton
            android:id="@+id/btn_finish"
            android:layout_width="42dip"
            android:layout_height="42dip"
            android:layout_alignParentRight="true"
            android:layout_marginRight="15dip"
            android:background="@drawable/btn_round_semitransperant"
            android:contentDescription="Let's start"
            android:src="@mipmap/ic_navigation_check"
            android:visibility="gone" />

    </RelativeLayout>

</RelativeLayout>


Step - 2
Above code will show error but don't worry it is for drawable files. so below are drawable files put the code in appropriate file and your layout is set.

pager_item.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:fitsSystemWindows="true"
    android:layout_height="match_parent">

    <ImageView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:src="@mipmap/abc1"
        android:id="@+id/img_pager_item"
        android:scaleType="fitXY"
        android:adjustViewBounds="true"
        android:clickable="false"/>

</LinearLayout>


drawable/selecteditem_dot.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="oval" android:useLevel="true"
    android:dither="true">

    <size android:height="12dip" android:width="12dip"/>

    <solid android:color="@color/primary"/>
</shape>


drawable/nonselecteditem_dot.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="oval" android:useLevel="true"
    android:dither="true">

    <size android:height="6dip" android:width="6dip"/>

    <solid android:color="@color/activity_bg"/>
</shape>




Step - 3
For View pager, layout part is done. you can find sample images from here. now let's move to code part where i have created circular dots on the basis of data size. So if you have let say 10 images it will create 10 dots with first one as selected by default. put the code in to activity and make changes as per your project implementation.

ViewPagerDemo.java
package com.androprogrammer.tutorials.samples;

import android.os.Bundle;
import android.support.v4.view.ViewPager;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.LinearLayout;

import com.androprogrammer.tutorials.R;
import com.androprogrammer.tutorials.activities.Baseactivity;
import com.androprogrammer.tutorials.adapters.ViewPagerAdapter;

public class ViewPagerDemo extends Baseactivity implements ViewPager.OnPageChangeListener, View.OnClickListener{

    protected View view;
    private ImageButton btnNext, btnFinish;
    private ViewPager intro_images;
    private LinearLayout pager_indicator;
    private int dotsCount;
    private ImageView[] dots;
    private ViewPagerAdapter mAdapter;

    private int[] mImageResources = {
            R.mipmap.abc1,
            R.mipmap.abc2,
            R.mipmap.abc3,
            R.mipmap.abc4,
            R.mipmap.abc5
    };

    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        // To make activity full screen.
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
                WindowManager.LayoutParams.FLAG_FULLSCREEN);

        super.onCreate(savedInstanceState);

        setReference();

        toolbar.setVisibility(View.GONE);

    }

    @Override
    public void setReference() {
        view = LayoutInflater.from(this).inflate(R.layout.activity_viewpager_demo,container);

        intro_images = (ViewPager) view.findViewById(R.id.pager_introduction);
        btnNext = (ImageButton) view.findViewById(R.id.btn_next);
        btnFinish = (ImageButton) view.findViewById(R.id.btn_finish);

        pager_indicator = (LinearLayout) view.findViewById(R.id.viewPagerCountDots);

        btnNext.setOnClickListener(this);
        btnFinish.setOnClickListener(this);

        mAdapter = new ViewPagerAdapter(ViewPagerDemo.this, mImageResources);
        intro_images.setAdapter(mAdapter);
        intro_images.setCurrentItem(0);
        intro_images.setOnPageChangeListener(this);
        setUiPageViewController();
    }

    private void setUiPageViewController() {

        dotsCount = mAdapter.getCount();
        dots = new ImageView[dotsCount];

        for (int i = 0; i < dotsCount; i++) {
            dots[i] = new ImageView(this);
            dots[i].setImageDrawable(getResources().getDrawable(R.drawable.nonselecteditem_dot));

            LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
                    LinearLayout.LayoutParams.WRAP_CONTENT,
                    LinearLayout.LayoutParams.WRAP_CONTENT
            );

            params.setMargins(4, 0, 4, 0);

            pager_indicator.addView(dots[i], params);
        }

        dots[0].setImageDrawable(getResources().getDrawable(R.drawable.selecteditem_dot));
    }


    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btn_next:
                intro_images.setCurrentItem((intro_images.getCurrentItem() < dotsCount)
                        ? intro_images.getCurrentItem() + 1 : 0);
                break;

            case R.id.btn_finish:
                finish();
                break;
        }
    }

    @Override
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {

    }

    @Override
    public void onPageSelected(int position) {
        for (int i = 0; i < dotsCount; i++) {
            dots[i].setImageDrawable(getResources().getDrawable(R.drawable.nonselecteditem_dot));
        }

        dots[position].setImageDrawable(getResources().getDrawable(R.drawable.selecteditem_dot));

        if (position + 1 == dotsCount) {
            btnNext.setVisibility(View.GONE);
            btnFinish.setVisibility(View.VISIBLE);
        } else {
            btnNext.setVisibility(View.VISIBLE);
            btnFinish.setVisibility(View.GONE);
        }
    }

    @Override
    public void onPageScrollStateChanged(int state) {

    }
}


This is code for view pager adapter.which fills all images into view pager and display like gallery.
adapters/ViewPagerAdapter.java
package com.androprogrammer.tutorials.adapters;

import android.content.Context;
import android.support.v4.view.PagerAdapter;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;

import com.androprogrammer.tutorials.R;

/**
 * Created by Wasim on 11-06-2015.
 */
public class ViewPagerAdapter extends PagerAdapter {

    private Context mContext;
    private int[] mResources;

    public ViewPagerAdapter(Context mContext, int[] mResources) {
        this.mContext = mContext;
        this.mResources = mResources;
    }

    @Override
    public int getCount() {
        return mResources.length;
    }

    @Override
    public boolean isViewFromObject(View view, Object object) {
        return view == ((LinearLayout) object);
    }

    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        View itemView = LayoutInflater.from(mContext).inflate(R.layout.pager_item, container, false);

        ImageView imageView = (ImageView) itemView.findViewById(R.id.img_pager_item);
        imageView.setImageResource(mResources[position]);

        container.addView(itemView);

        return itemView;
    }

    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        container.removeView((LinearLayout) object);
    }
}


That's it from my side. now run the app and see the result. and at the end i am replacing next button with finish button. if you have any query please let me know in below comment box.

Source code

Keep coding...

Android Wear: How to send data from Android Wear to Mobile Device ?

Android Wear or Let say Smart Watches are new and trending technology. Android apps are updating and they all comes with Android wear support. if you want it is in your app you can find simple Helloworld wear app tutorials easily on Google. but what if you want to explore it more let say you want to display a List view in android wear or get some kind of input from android wear app. so these tutorial is for you. In this tutorial i have created simple list in android wear app in which user click on list item and an app in mobile device gets open. for example you want to display list of songs in android wear app and on the click of that you want to change song in your device or let say you can display to do list on android wear and on the click of list item you can open to do item details inside your mobile app. this list can be infinite and its totally depends on you and apps in android.

Android Wear sample


Steps

Step - 1
First of let i start with adding a android wear module in development. If you have already added android wear module in you app you can skip this step. To add right click on your app root folder (name of your app folder) and go to New > Module. Select Android Wear Module and click on next. Give same name in Application name (Mobile app name) and in Module Name write wear. so you can differentiate them. Make sure you give same package name also. because when you send some data through mobile app or wear app MessageApi will check for the same package and pass that data. so if you give different name you won't be able to send or receive data. On the next screen select Blank Wear Activity and on the next screen give proper name to it.

Note - Above steps are for Android studio wizard. Sorry for eclipse developers (Please migrate to AS)

Step - 2
So our android wear module is added. I hope you have no issue in above steps. in the main activity or launcher activity i have created a List (basically list of my tutorials). For learning purpose you can start with same and after that you can change it is as you want. so put below code in appropriate layout files.

wear/layout/activity_list.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.wearable.view.WatchViewStub 
   xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/watch_view_stub"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#e4e5e5"
    app:rectLayout="@layout/rect_activity_list"
    app:roundLayout="@layout/round_activity_list"
    tools:context=".ListActivity"
    tools:deviceIds="wear">

</android.support.wearable.view.WatchViewStub>


wear/layout/rect_activity_list.xml & wear/layout/round_activity_list.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.wearable.view.BoxInsetLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    tools:context=".ListActivity"
    tools:deviceIds="wear_round">

        <android.support.wearable.view.WearableListView
            android:id="@+id/listView1"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:padding="5dp"
            android:overScrollMode="never" />

        <TextView
            android:id="@+id/tv_header"
            android:layout_width="wrap_content"
            android:layout_height="40dp"
            android:layout_gravity="top|center"
            android:textSize="30sp"
            android:layout_marginTop="10dp"
            android:textColor="#52bf90"
            android:fontFamily="sans-serif-light"
            android:text="@string/app_name"/>

</android.support.wearable.view.BoxInsetLayout>



Step - 3
List layout is done in Step -2. now let's jump to coding part where i have initialize WearableListView with Array adapter. Using GoogleApiClient i have checked if there is a connection to a android wear from an app or not. we can get all connected nodes or devices using Wearable.NodeApi.getConnectedNodes method. check below code and make sure you understand or go through it for one time so you know what is actually going on. otherwise you will get confused.

wear/java/ListActivity.java
package com.androprogrammer.tutorials;

import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.support.wearable.view.WatchViewStub;
import android.support.wearable.view.WearableListView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import android.widget.TextView;
import android.widget.Toast;

import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.common.api.ResultCallback;
import com.google.android.gms.wearable.MessageApi;
import com.google.android.gms.wearable.Node;
import com.google.android.gms.wearable.NodeApi;
import com.google.android.gms.wearable.Wearable;

import java.util.ArrayList;

public class ListActivity extends Activity implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener,WearableListView.ClickListener {

    private WearableListView mListView;

    private ArrayList<String> listItems;

    Node mNode; // the connected device to send the message to
    GoogleApiClient mGoogleApiClient;
    private boolean mResolvingError=false;

    public static String SERVICE_CALLED_WEAR = "WearListClicked";
    public static String TAG = "WearListActivity";


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

        //Connect the GoogleApiClient
        mGoogleApiClient = new GoogleApiClient.Builder(this)
                .addApi(Wearable.API)
                .addConnectionCallbacks(this)
                .addOnConnectionFailedListener(this)
                .build();

        initializeListItems();
        final WatchViewStub stub = (WatchViewStub) findViewById(R.id.watch_view_stub);
        stub.setOnLayoutInflatedListener(new WatchViewStub.OnLayoutInflatedListener() {
            @Override
            public void onLayoutInflated(WatchViewStub stub) {
                mListView = (WearableListView) stub.findViewById(R.id.listView1);

                mListView.setAdapter(new MyAdapter(ListActivity.this, listItems));
                mListView.setClickListener(ListActivity.this);

            }
        });
    }

    private void initializeListItems() {

        listItems = new ArrayList<>();
        listItems.add("Async File Read");
        listItems.add("Battery Status");
        listItems.add("Volume Setting");
        listItems.add("Frame Animation");
        listItems.add("Video Player");
        listItems.add("Circular Image View");
        listItems.add("Track User Location");
        listItems.add("Take Image");
        listItems.add("Image Grid View");
        listItems.add("Image Switcher");
        listItems.add("Tabs with Toolbar");
        listItems.add("Icon Tabs with Toolbar");
        listItems.add("Push Notification");


    }

    @Override
    protected void onStart() {
        super.onStart();
        if (!mResolvingError) {
            mGoogleApiClient.connect();
        }
    }

    /**
     * Resolve the node = the connected device to send the message to
     */
    private void resolveNode() {

        Wearable.NodeApi.getConnectedNodes(mGoogleApiClient)
                .setResultCallback(new ResultCallback<NodeApi.GetConnectedNodesResult>() {
                    @Override
                    public void onResult(NodeApi.GetConnectedNodesResult nodes) {
                        for (Node node : nodes.getNodes()) {
                    mNode = node;
                }
            }
        });
    }

    @Override
    public void onConnected(Bundle bundle) {
        resolveNode();
    }

    @Override
    public void onConnectionSuspended(int i) {

    }

    @Override
    public void onConnectionFailed(ConnectionResult connectionResult) {

    }

    /**
     * Send message to mobile handheld
     */
    private void sendMessage(String Key) {

        if (mNode != null && mGoogleApiClient!= null && mGoogleApiClient.isConnected()) {
            Log.d(TAG, "-- " + mGoogleApiClient.isConnected());
            Wearable.MessageApi.sendMessage(
                    mGoogleApiClient, mNode.getId(), SERVICE_CALLED_WEAR + "--" + Key, null).setResultCallback(

                    new ResultCallback<MessageApi.SendMessageResult>() {
                        @Override
                        public void onResult(MessageApi.SendMessageResult sendMessageResult) {

                            if (!sendMessageResult.getStatus().isSuccess()) {
                                Log.e(TAG, "Failed to send message with status code: "
                                        + sendMessageResult.getStatus().getStatusCode());
                            }
                        }
                    }
            );
        }

    }

    @Override
    public void onClick(WearableListView.ViewHolder viewHolder) {
        TextView view = (TextView) viewHolder.itemView.findViewById(R.id.row_tv_name);
        String Key = view.getText().toString();
        Log.d(TAG, Key);
        sendMessage(Key);
    }

    @Override
    public void onTopEmptyRegionClick() {
        Toast.makeText(this, "You tapped on Top empty area", Toast.LENGTH_SHORT).show();
    }


    private class MyAdapter extends WearableListView.Adapter {
        private final LayoutInflater mInflater;
        private ArrayList<String> data;

        private MyAdapter(Context context, ArrayList<String> listItems) {
            mInflater = LayoutInflater.from(context);
            data = listItems;
        }

        @Override
        public WearableListView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            return new WearableListView.ViewHolder(
                    mInflater.inflate(R.layout.row_wear_list, null));
        }

        @Override
        public void onBindViewHolder(WearableListView.ViewHolder holder, int position) {
            TextView view = (TextView) holder.itemView.findViewById(R.id.row_tv_name);
            view.setText(data.get(position));
            holder.itemView.setTag(position);
        }

        @Override
        public int getItemCount() {
            return data.size();
        }
    }
}


wear/layout/row_wear_list.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="80dp"
    android:orientation="horizontal"
    android:padding="8dp"
    android:gravity="center">

    <TextView
        android:textAppearance="?android:attr/textAppearanceMedium"
        android:text="Medium Text"
        android:textStyle="bold"
        android:ellipsize="end"
        android:maxEms="15"
        android:padding="10dp"
        android:ems="15"
        android:background="@android:color/white"
        android:textColor="@android:color/black"
        android:fontFamily="sans-serif-condensed-light"
        android:gravity="center"
        android:layout_height="match_parent"
        android:layout_width="wrap_content"
        android:textSize="16sp"
        android:id="@+id/row_tv_name" />

</LinearLayout>

wear/AndroidManifest.xml
<meta-data android:name="com.google.android.gms.version"
            android:value="@integer/google_play_services_version" />

Please add above lines of code in manifest file of wear module. because we are using google play service to communicate. otherwise it will give error.

Step - 4
Now android wear module is ready. you can run it separately on emulator and check it. now we require a bridge which can pass android wear message to android mobile app. so when user click on list item in android wear a service get some sort of message and on the basis of that message we start appropriate activity. Using WearableListenerService we will create service which will do same for our app.

mobile/java/WearListCallListenerService.java
package com.androprogrammer.tutorials.services;

import android.content.Intent;
import android.util.Log;

import com.androprogrammer.tutorials.activities.Listactivity;
import com.google.android.gms.wearable.MessageEvent;
import com.google.android.gms.wearable.WearableListenerService;

/**
 * Created by Wasim on 08-05-2015.
 */
public class WearListCallListenerService extends WearableListenerService {

    public static String SERVICE_CALLED_WEAR = "WearListClicked";


    @Override
    public void onMessageReceived(MessageEvent messageEvent) {
        super.onMessageReceived(messageEvent);

        String event = messageEvent.getPath();

        Log.d("Listclicked", event);

        String [] message = event.split("--");

        if (message[0].equals(SERVICE_CALLED_WEAR)) {

            startActivity(new Intent((Intent) Listactivity.getInstance().tutorials.get(message[1]))
                    .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
        }
    }
}


Step - 5
In Mobile app put below code to register your service.

mobile/AndroidManifest.xml
<service android:name=".services.WearListCallListenerService">
            <intent-filter>
                <action android:name="com.google.android.gms.wearable.BIND_LISTENER" />
            </intent-filter>
        </service>

That's it...
Now run wear module in android wear emulator and mobile module in device. make sure you are connected. I hope you understand the full tutorial. you can get full source code from my Github repo. if you find it helpful please share this tutorial.

Keep coding...

Material Design: Tabs with Toolbar

Action bar is deprecated in android 5.0 known as Android Lollipop and new toolbar is introduced by Android(Google) developers. Toolbar is also a simple view like others but to enhance UI and UX of apps they have introduced it with some enhancement. In Android 5.0 Tabs are also recreated with some enhancement. because toolbar doesn't support direct tabs like actionbar. In action bar you can add tabs using method called addTab() but with tabs you can't use same method. now you even don't require Tab host or Tab widget to add tabs. so how ?

Well go through full tutorial and you can implement nice tabs like google play, google music and many other apps have. Tabs are mostly used to display two different views in same page or activity. most of the developer load different fragments in tabs so they can have control over tabs and views associated with it. Tabs can be of two types.

1. Tabs with label
2. Tabs with icons




Steps

Step - 1
Create activity which holds tabs inside view part and give proper name to it. In this tutorial it is TabViewDemo Activity. To create tabs first we have to create SlidingTabLayout and SlidingTabStripe custom view. So put below both class code in your project.
Get it from github

SlidingTabLayout.java
SlidingTabStripe.java

Step - 2 a)
Now put below code in activity which you have created in Step - 1. this activity is Demo activity which will display tabs with labels(type -1). Below you can find layout and class file code put the code inside your project.

activity_tabview_demo.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingTop="?attr/actionBarSize"
    tools:context="com.androprogrammer.tutorials.samples.TabViewDemo">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <com.androprogrammer.tutorials.customviews.SlidingTabLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:id="@+id/tab_layout_header"
            android:elevation="8dp"/>

        <android.support.v4.view.ViewPager
            android:id="@+id/tab_layout_container"
            android:layout_width="match_parent"
            android:layout_height="0px"
            android:layout_weight="1"
            android:background="@color/activity_bg" />

    </LinearLayout>

</RelativeLayout>



TabViewDemo.java
package com.androprogrammer.tutorials.samples;

import android.app.ActionBar;

import android.app.FragmentTransaction;
import android.graphics.Color;
import android.os.Build;
import android.support.v4.app.Fragment;
import android.os.Bundle;
import android.transition.Slide;
import android.transition.Transition;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;


import com.androprogrammer.tutorials.R;
import com.androprogrammer.tutorials.activities.Baseactivity;
import com.androprogrammer.tutorials.adapters.TabFragmentAdapter;
import com.androprogrammer.tutorials.customviews.SlidingTabLayout;
import com.androprogrammer.tutorials.fragments.FragmentOne;
import com.androprogrammer.tutorials.fragments.FragmentThree;
import com.androprogrammer.tutorials.fragments.FragmentTwo;

import java.util.ArrayList;

public class TabViewDemo extends Baseactivity implements ActionBar.TabListener {

    protected View view;
    protected SlidingTabLayout tabs_header;
    protected android.support.v4.view.ViewPager mPager;
    protected ArrayList<fragment> fragments;
    protected TabFragmentAdapter mAdapter;

    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        if(android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
        {
            getWindow().requestFeature(android.view.Window.FEATURE_CONTENT_TRANSITIONS);

            //set the transition
            Transition ts = new Slide();
            ts.setDuration(3000);
            getWindow().setEnterTransition(ts);
            getWindow().setExitTransition(ts);

        }

        super.onCreate(savedInstanceState);

        setReference();

        setToolbarSubTittle(this.getClass().getSimpleName());

        setToolbarElevation(0);

        getSupportActionBar().setDisplayHomeAsUpEnabled(true);

        fragments.add(new FragmentOne());
        fragments.add(new FragmentTwo());
        fragments.add(new FragmentThree());

        String[] titles = {"One", "Two", "Three"};

        mAdapter = new TabFragmentAdapter(getSupportFragmentManager(), fragments, titles);

        mPager.setAdapter(mAdapter);

        tabs_header.setBackgroundColor(getResources().getColor(R.color.primary));
        tabs_header.setTitleColor(Color.WHITE);
        tabs_header.setFittingChildren(true);
        tabs_header.setTabType(SlidingTabLayout.TabType.TEXT);
        tabs_header.setViewPager(mPager);
    }

    @Override
    public void setReference() {

        view = LayoutInflater.from(this).inflate(R.layout.activity_tabview_demo,container);

        tabs_header = (SlidingTabLayout) view.findViewById(R.id.tab_layout_header);
        mPager = (android.support.v4.view.ViewPager) view.findViewById(R.id.tab_layout_container);
        fragments = new ArrayList<fragment>();

        SetTabSelector();
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        switch (item.getItemId()) {
            // Respond to the action bar's Up/Home button
            case android.R.id.home:
                finish();
                break;
        }

        return super.onOptionsItemSelected(item);
    }

    private void SetTabSelector()
    {
        tabs_header.setCustomTabColorizer(new SlidingTabLayout.TabColorizer() {
            @Override
            public int getIndicatorColor(int position)
            {
                int ColorId = 0;

                switch (position)
                {
                    case 0:
                        ColorId = Color.WHITE;
                        break;

                    case 1:
                        ColorId = Color.MAGENTA;
                        break;
                    case 2:
                        ColorId = Color.RED;
                        break;
                }

                return ColorId;

            }

            @Override
            public int getDividerColor(int position) {
                return 0;
            }
        });

    }

    @Override
    public void onTabSelected(ActionBar.Tab tab, FragmentTransaction ft) {
        mPager.setCurrentItem(tab.getPosition());
    }

    @Override
    public void onTabUnselected(ActionBar.Tab tab, FragmentTransaction ft) {

    }

    @Override
    public void onTabReselected(ActionBar.Tab tab, FragmentTransaction ft) {

    }
}


Step - 2 b)
This is second sub part of tutorial. if you want to create tabs with icons(type -2) then put below code for tabs with simple text put above code in your activity. to create tabs with icon you have to give name to images like this, ic_one, ic_two etc. because i have fetched images from drawable using tab title. check below code.

private int getResourceIdByName(String resourceName) {
        // get images from mipmap folder
        return getResources().getIdentifier(resourceName, "mipmap", getContext().getPackageName());
    }

if your project consist old drawable pattern then replace mipmap with drawable in SlidingTabLayout file.

ImageTabViewDemo.java
package com.androprogrammer.tutorials.samples;

import android.app.ActionBar;
import android.app.FragmentTransaction;
import android.graphics.Color;
import android.os.Build;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.transition.Slide;
import android.transition.Transition;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;

import com.androprogrammer.tutorials.R;
import com.androprogrammer.tutorials.activities.Baseactivity;
import com.androprogrammer.tutorials.adapters.TabFragmentAdapter;
import com.androprogrammer.tutorials.customviews.SlidingTabLayout;
import com.androprogrammer.tutorials.fragments.FragmentOne;
import com.androprogrammer.tutorials.fragments.FragmentThree;
import com.androprogrammer.tutorials.fragments.FragmentTwo;

import java.util.ArrayList;

public class ImageTabViewDemo extends Baseactivity implements ActionBar.TabListener {

    protected View view;
    protected SlidingTabLayout tabs_header;
    protected android.support.v4.view.ViewPager mPager;
    protected ArrayList<fragment> fragments;
    protected TabFragmentAdapter mAdapter;

    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
        {
            getWindow().requestFeature(android.view.Window.FEATURE_CONTENT_TRANSITIONS);

            //set the transition
            Transition ts = new Slide();
            ts.setDuration(3000);
            getWindow().setEnterTransition(ts);
            getWindow().setExitTransition(ts);


        }

        super.onCreate(savedInstanceState);

        setReference();

        setToolbarSubTittle(this.getClass().getSimpleName());

        setToolbarElevation(0);

        getSupportActionBar().setDisplayHomeAsUpEnabled(true);

        fragments.add(new FragmentOne());
        fragments.add(new FragmentTwo());
        fragments.add(new FragmentThree());

        String[] titles = {"one", "two", "three"};

        mAdapter = new TabFragmentAdapter(getSupportFragmentManager(), fragments, titles);

        mPager.setAdapter(mAdapter);

        tabs_header.setBackgroundColor(getResources().getColor(R.color.primary));
        tabs_header.setTitleColor(Color.WHITE);
        tabs_header.setFittingChildren(true);
        tabs_header.setTabType(SlidingTabLayout.TabType.ICON);
        tabs_header.setViewPager(mPager);
    }

    @Override
    public void setReference() {

        view = LayoutInflater.from(this).inflate(R.layout.activity_tabview_demo,container);

        tabs_header = (SlidingTabLayout) view.findViewById(R.id.tab_layout_header);
        mPager = (android.support.v4.view.ViewPager) view.findViewById(R.id.tab_layout_container);
        fragments = new ArrayList<fragment>();

        SetTabSelector();
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        switch (item.getItemId()) {
            // Respond to the action bar's Up/Home button
            case android.R.id.home:
                finish();
                break;
        }

        return super.onOptionsItemSelected(item);
    }

    private void SetTabSelector()
    {
        tabs_header.setCustomTabColorizer(new SlidingTabLayout.TabColorizer() {
            @Override
            public int getIndicatorColor(int position)
            {
                int ColorId = 0;

                switch (position)
                {
                    case 0:
                        ColorId = Color.WHITE;
                        break;

                    case 1:
                        ColorId = Color.MAGENTA;
                        break;
                    case 2:
                        ColorId = Color.RED;
                        break;
                }

                return ColorId;

            }

            @Override
            public int getDividerColor(int position) {
                return 0;
            }
        });

    }

    @Override
    public void onTabSelected(ActionBar.Tab tab, FragmentTransaction ft) {
        mPager.setCurrentItem(tab.getPosition());
    }

    @Override
    public void onTabUnselected(ActionBar.Tab tab, FragmentTransaction ft) {

    }

    @Override
    public void onTabReselected(ActionBar.Tab tab, FragmentTransaction ft) {

    }
}



Resources of my project is available at Github so please get images from there. to load three different views i created adapter which extends FragmentPagerAdapter class. below is code of adapter of view pager.

TabFragmentAdapter.java
package com.androprogrammer.tutorials.adapters;

import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;

import java.util.ArrayList;

/**
 * Created by Wasim on 28-04-2015.
 */
public class TabFragmentAdapter extends FragmentPagerAdapter {

    private ArrayList<Fragment> fragments;
    private String[] titles;

    public TabFragmentAdapter(FragmentManager fm, ArrayList <Fragment> fragments, String [] title) {
        super(fm);
        this.fragments = fragments;
        this.titles = title;
    }

    @Override
    public Fragment getItem(int position) {
        return fragments.get(position);
    }

    @Override
    public int getCount() {
        return fragments.size();
    }

    @Override
    public int getItemPosition(Object object) {
        return super.getItemPosition(object);
    }

    @Override
    public CharSequence getPageTitle(int position) {
        return titles[position];
    }
}


Step - 3
In this tutorial i have even used three fragments to load on three different tabs.the fragments have no logic or code so i don't put that code here. you can find code from here.

That's it. I hope you its clear to you. now run your app. hope it will work fine. if you have any query
or have problem in code implementation write it in below comment box.

Keep coding...

Track user location in android

In android you can get user's latitude and longitude using two different providers.
  1. Using Network
  2. Using GPS
Using Location Listener interface you can get perfect location. but there is no method which actually tells you which provider is best so its good practice to use both. Network provide lat long with good accuracy so i have used it as first option and after that gps.
This tutorial is comprise of get user location and also display it in map. so if you want both the thing go through full tutorial or if you want only users latitude and longitude then skip step 2. which is actually about getting Google map key and setup it in manifest file.



gps in android application, androprogrammer.com


Steps 

Step - 1 : First of all we will create Location listener which actually gives us user location. It is service class which runs in background and give us data on the basis of location change. you can get user location every seconds but it will increase app crash chances so it is better to define time interval and distance interval. i have used 1 min as time interval and 20 meter distance interval so my app won't make call for location each and every seconds. so now create a new class file and put below code inside it.

GetUserLocation.java 

package com.androprogrammer.tutorials.services;

import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.os.IBinder;
import android.util.Log;

/**
 * Created by Wasim on 21-03-2015.
 */
public class GetUserLocation extends Service implements LocationListener
{
    private Context context;

    private static final String TAG = "GPSGetUserLocation";

    // The maximum time that should pass before the user gets a location update.
    private static final long DEFAULT_DURATION = 1000 * 60 * 1; // 1 min

    // The default search radius when searching for places nearby.
    private static final long DEFAULT_RADIUS = 20; // 20 meters

    private LocationManager mLocationManager = null;

    private double latitude;
    private double longitude;

    // flag for GPS status
    //private boolean isGPSEnabled = false;

    // flag for network status
    //private boolean isNetworkEnabled = false;


    private Location location; // location values


    public GetUserLocation(Context context, boolean isGPSEnabled, boolean isNetworkEnabled)
    {
        this.context = context;
        getUserLocation(isGPSEnabled, isNetworkEnabled);
    }


    public GetUserLocation() {
    }

    public Location getUserLocation(boolean isGPSEnabled, boolean isNetworkEnabled)
    {
        try {

            if (mLocationManager == null)
            {
                mLocationManager = (LocationManager) context
                        .getSystemService(Context.LOCATION_SERVICE);
            }

                // First get location from Network Provider
                if (isNetworkEnabled)
                {
                    mLocationManager.requestLocationUpdates(
                            LocationManager.NETWORK_PROVIDER,
                            DEFAULT_DURATION,
                            DEFAULT_RADIUS, this);

                    Log.d(TAG, "Network");

                    if (mLocationManager != null) {
                        location = mLocationManager
                                .getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
                        if (location != null) {
                            setLatitude(location.getLatitude());
                            setLongitude(location.getLongitude());
                        }
                    }
                }

                // if GPS Enabled get lat/long using GPS Services
                if (isGPSEnabled) {
                    if (location == null)
                    {
                        mLocationManager.requestLocationUpdates(
                                LocationManager.GPS_PROVIDER,
                                DEFAULT_DURATION,
                                DEFAULT_RADIUS, this);

                        Log.d(TAG, "GPS Enabled");

                        if (mLocationManager != null) {
                            location = mLocationManager
                                    .getLastKnownLocation(LocationManager.GPS_PROVIDER);
                            if (location != null) {
                                setLatitude(location.getLatitude());
                                setLongitude(location.getLongitude());
                            }
                        }
                    }
                }

        } catch (Exception e) {
            e.printStackTrace();
        }

        return location;
    }

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onLocationChanged(Location location)
    {
        if (location != null)
        {
            setLongitude(location.getLongitude());
            setLatitude(location.getLatitude());
        }
    }

    @Override
    public void onStatusChanged(String provider, int status, Bundle extras) {

    }

    @Override
    public void onProviderEnabled(String provider) {

    }

    @Override
    public void onProviderDisabled(String provider) {

    }

    public double getLatitude() {
        return latitude;
    }

    public void setLatitude(double latitude) {
        Log.d(TAG, "" + latitude);
        this.latitude = latitude;
    }

    public double getLongitude() {
        return longitude;
    }

    public void setLongitude(double longitude) {
        Log.d(TAG, "" + longitude);
        this.longitude = longitude;
    }

    @Override
    public void onDestroy()
    {
        Log.e(TAG, "onDestroy");
        super.onDestroy();

        if (mLocationManager != null)
        {
            // Remove update listener when you don't require
            mLocationManager.removeUpdates(this);
            mLocationManager = null;
        }
    }
}


Step - 2 : To display Google map fragment inside your app you have to register your app at Google Developer console. and then get Map key. you need to set this key inside Android manifest file. otherwise your app may crash. For complete setup of Google Map api v2 you can find here.


if you have already registered your project you can find Map key at
Project > Apis & auth > Credentials.

google map key
Please do not use this key


Step - 3 : Now we need a class which actually start this service. so create a activity which start this service and get location data.in this activity i have used Google map fragment to show user location if you don't want to display lat long then toast it so you can verify that service is working and you are getting user location data.

TrackUserDemo.java 

package com.androprogrammer.tutorials.samples;

import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.location.LocationManager;
import android.os.Build;
import android.os.Bundle;
import android.transition.Slide;
import android.transition.Transition;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.SubMenu;
import android.view.View;
import android.view.Window;
import android.widget.Toast;

import com.androprogrammer.tutorials.R;
import com.androprogrammer.tutorials.activities.Baseactivity;
import com.androprogrammer.tutorials.services.GetUserLocation;
import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.MapFragment;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.MarkerOptions;

public class TrackUserDemo extends Baseactivity
{

    protected View view;

    // Google Map Object
    private GoogleMap googleMap;

    private LatLng userLatLng;

    private boolean IsFirstTimeLoad = true;

    private LocationManager mLocationManager;

    private static final String TAG = "TrackUserDemo";

    private static String RefreshMenu = "Refresh Map";


    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        if(android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
        {
            getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS);

            //set the transition
            Transition ts = new Slide();
            ts.setDuration(5000);
            getWindow().setEnterTransition(ts);
            getWindow().setExitTransition(ts);
        }

        super.onCreate(savedInstanceState);

        setReference();

 getSupportActionBar().setDisplayHomeAsUpEnabled(true);

        setToolbarTittle(this.getClass().getSimpleName());

        mLocationManager = (LocationManager) getSystemService(LOCATION_SERVICE);

        initilizeMap();

    }

    @Override
    protected void onResume()
    {
        super.onResume();
        if (!IsFirstTimeLoad)
        {
            if(!mLocationManager.isProviderEnabled(android.location.LocationManager.GPS_PROVIDER)) {
                showGpsAlertDialog();
            }
            else {
                Intent intent = new Intent(this, GetUserLocation.class);
                startService(intent);
                initilizeMap();
            }
        }
        else {
            IsFirstTimeLoad = false;
        }
    }

    private void initilizeMap()
    {
        try {
  // Display Toast message if you don't want to display google map using service.getLatitude(), service.getLongitude() methods
            if (googleMap == null) {
                googleMap = ((MapFragment) getFragmentManager().findFragmentById(R.id.map)).getMap();
                // check if map is created successfully or not
                if (googleMap == null)
                {
                    Toast.makeText(TrackUserDemo.this, "Sorry! unable to create maps", Toast.LENGTH_LONG).show();
                }
            }

            // getting GPS status
            boolean isGPSEnabled = mLocationManager
                    .isProviderEnabled(LocationManager.GPS_PROVIDER);

            // getting network status
            boolean isNetworkEnabled = mLocationManager
                    .isProviderEnabled(LocationManager.NETWORK_PROVIDER);

            GetUserLocation service = new GetUserLocation(this,isGPSEnabled,isNetworkEnabled);

            if (!isGPSEnabled)
            {

                showGpsAlertDialog();

            }

            userLatLng = new LatLng(service.getLatitude(), service.getLongitude());

            MarkerOptions usermarker = new MarkerOptions().position(userLatLng);

            // To remove old marker
            googleMap.clear();

            googleMap.addMarker(usermarker);

            googleMap.moveCamera(CameraUpdateFactory.newLatLngZoom(
                    new LatLng(service.getLatitude(), service.getLongitude()),
                    10.0f));

            googleMap.getUiSettings().setZoomControlsEnabled(true);

        } catch (Exception e) {
            Log.e(TAG, e.getMessage());
            //e.printStackTrace();
        }
    }

    private void showGpsAlertDialog() {

        AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(TrackUserDemo.this);
        alertDialogBuilder.setTitle("Gps Setting");
        alertDialogBuilder.setMessage("Location data provider is disabled.\nPlease enable it to track location.");

        alertDialogBuilder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which)
            {
                Intent myIntent = new Intent(android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS);
                startActivity(myIntent);
            }
        });


        alertDialogBuilder.setNegativeButton(android.R.string.cancel,
                new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which)
                    {
                        dialog.dismiss();
                    }
                });

        AlertDialog alertDialog = alertDialogBuilder.create();

        alertDialog.show();
    }

    @Override
    public void setReference()
    {
        view = LayoutInflater.from(this).inflate(R.layout.activity_trackuser_demo,container);

    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu)
    {
        // To display refresh button
        SubMenu btnRefresh = menu.addSubMenu(RefreshMenu);
        btnRefresh.setIcon(android.R.drawable.ic_popup_sync);
        btnRefresh.getItem().setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);

        super.onCreateOptionsMenu(menu);
        return true;
    }


    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle Toolbar item clicks here. The Toolbar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        switch (item.getItemId()) {
            // Respond to the action bar's Up/Home button
            case android.R.id.home:
                finish();
                break;
        }


        if (item.getTitle() == RefreshMenu)
        {
            initilizeMap();
        }

        return super.onOptionsItemSelected(item);
    }
}

activity_trackuser_demo.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="?attr/actionBarSize"
    android:paddingBottom="@dimen/activity_vertical_margin"
    tools:context="com.androprogrammer.tutorials.samples.TrackUserDemo">

    <fragment
        android:id="@+id/map"
        class="com.google.android.gms.maps.MapFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

</RelativeLayout>


Step - 4 : well now add/define service and google play meta data in AndroidManifest.xml file. put below code inside it.

AndroidManifest.xml
 
<!-- IMPORTANT ! DO NOT FORGET THIS META-DATA !!! -->
        <meta-data
            android:name="com.google.android.gms.version"
            android:value="@integer/google_play_services_version" />
        <meta-data
            android:name="com.google.android.maps.v2.API_KEY"
            android:value="your_key" />

        <uses-feature
            android:glEsVersion="0x00020000"
            android:required="true" />
<service android:name=".services.GetUserLocation" />

Step - 5 : One More thing you have to add dependencies in app > build.gradle file. so put below code in it.

build.gradle 

dependencies {
 // To add toolbar and material theme.
compile 'com.android.support:appcompat-v7:21.0.3'
compile fileTree(include: ['*.jar'], dir: 'libs')
 // To use Google map and other services.
compile 'com.google.android.gms:play-services:6.1.71'

well now run the app if you are testing in emulator then make sure you have Google apps inside it. other wise Google Map will not work. i hope you understand full tutorial. if you have any questions let me know in below comment box. if you like this share it with your friends.

Get code from Github

Keep coding...

Creating New App... WAIT !


Model-View-Controller (MVC) have made our work so much easy. before this coding were too much congested. business logic and design were in same file. imagine how it was. well now we have MVC Structure in all(Many of them) programming language. In which we have different files for different purpose. but still few things you have to consider if you want clean and nit code.

well, what are those things ?


If your app have user interface mobile or web interface the first thing i think most important for you.

1. Font Size

Define font size for Header, labels and detail text. what ever app you have you may require decent and same look through out the app. so this will help you. one more thing define them in Constant Class(where you can define all static and Constant variables) so you can use them through out the app. and please use even proper name also. like header_text_size or text_header_size so you can even recognize for long time.

2. Padding / Margin

One more thing you have to consider for app style and for its decent look. 

well you are thinking how ?


androprogrammer.com


3. Style

After above steps create a style according to you app colors (Theme). so if your client want to change theme you just have to change at one place no more
Ctrl + f for old color code and replace it with new.

4. Define Constants or Strings
 

You may require text like "OK" , "Save" , "Yes/No" at many place so why to write it again and again. define them once and use them through out the app. this will even help when your app is in two or more languages. so according to language you can change them.



5. Use access specifier

Private, protected, public these are the access specifier which acts like a guard. how ?
In hacking or spiffing hackers use programs which can change values of your app variables. but if you have defined access specifier before variable declaration, they are not acceessible by out side the class. So they can not change the value of that. so if you know scope of the variable define them with appropriate access specifiers.

6. Comments

Give or write proper comments for each class or method that you have defined or created. now a days editor have so many plugins which auto generate comment block so use them. you are not only working on the app or you may not work it on life time so your code must be understandable by others. so that proper comments require which clarify what it actually do.

7. Create proper directory structure

Now a days MVC have already separated our files but still we require some files to put in different folders. like in web app we have java script and css file so put them in different folders.
in mobile app create different folders for activities and services and for all others so if any error or change comes you can directly go there and find problem/change.

8. Background colors

Don't use dark or gradient colors as background use light or single color as a background. mostly we use white or light gray as a background. you can give all effects with this color and even it is more acceptable. as it will effect user experience and you may see dig ration in your app popularity.

Well these all are the points which is based on Kiss(Keep it Simple stupid). i hope you know it.
well i think i have covered all basics that you need to consider before creating any app weather it is web app or mobile app. if i forgot any point let me know in below comment box. i am really happy to know your thoughts on this article.

If you liked my blog please share and subscribe with it.

Navigation Drawer With Transparent Status bar

It is second part of  Let's Start with Material Design Series. In this series i will explain how you can give Material over haul to your existing app or how you can create it with Material design. in first part i have created the main layout of the app so if you want to know about it head over to it. Part -1 is about using Recycler view and Toolbar in Pre - Lollipop device using appcompat v21 library. In this part i am going to explain about navigation drawer. how you can implement navigation drawer as per material design guidelines.This tutorial even includes hamburger animation used in Google apps.

According to android documentation If you have hierarchy of views(More than 3 top-level views) then only you have to implement nav drawer in your app. and all top level views must be displayed in nav drawer for quick jumps to the top-level of your app. As nav drawer can be used from all views of app, you can incorporate or display important lower-level views also. but make sure child view indicate its parent view so user won't get confused.

You can use list view or expandable list view for navigation drawer but make sure for each row you use only one text view. for this tutorial i am going to create nav drawer as you can see in google apps (not the same). but as per Material design guidelines, as guidelines suggest drawer should spans the full height of the screen and the drawer should below the status bar. to span full space we need to customize frame layout. That actually Making DrawerLayout behind the system bars using fitSystemWindows property. one more thing we are using toolbar in place of action bar (that look same as action bar) that's how we will overlapping nav drawer above the action bar. as i am using appcompat library this app will look like same on pre-lollipop android device also.

As per doc the width of the side nav is equal to the width of the screen minus the height of the action bar, or 56dp from the right edge of the screen.
Mobile: Width = screen width - app bar height (No wider than 320dp on phones and 400dp on tablets).

Step -1 : As we have already created project in Part -1 we are directly going to change the main layout of project. so put below code inside Main.xml file. as it consist ScrimInsetsFrameLayout which will give error but don't worry it is customized frame layout. as you go through full tutorial you will get class file for it also. The same is used in Google I/O app. why they have given such name even i can't understand.  you can give name what ever you want. Below xml files are layout for main(content view) and navigation drawer view.
Main.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/drawer_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    tools:context=".MainActivity">

    <!-- Setting this one to true makes the status bar
        to have a non-transparent shitty background -->

    <!--
         As the main content view, the view below consumes the entire
         space available using match_parent in both dimensions.
    -->

    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:fitsSystemWindows="false">

        <include layout="@layout/actionbarlayout"></include>

        <android.support.v7.widget.RecyclerView
            android:id="@+id/my_recycler_view"
            android:layout_marginTop="?attr/actionBarSize"
            android:layout_marginRight="4dp"
            android:layout_marginLeft="4dp"
            android:elevation="5dp"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />

        <Button
            android:id="@+id/addButton"
            android:text="+"
            android:textSize="25sp"
            android:textColor="@android:color/white"
            android:textAlignment="center"
            android:layout_marginRight="15dp"
            android:layout_marginBottom="15dp"
            android:background="@drawable/circlebtn"
            android:layout_width="56dp"
            android:layout_height="56dp"
            android:stateListAnimator="@animator/anim"
            android:elevation="4dp"
            style="?android:buttonStyleSmall"
            android:layout_gravity="right|bottom" />
        

    </FrameLayout>

    <com.androprogrammer.test.materialapp1.ScrimInsetsFrameLayout
        android:id="@+id/container"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:fitsSystemWindows="true"
        android:elevation="8dp"
        android:layout_gravity="start"
        app:insetForeground="#4000">

        <ListView
            android:id="@+id/list_drawer"
            android:layout_width="@dimen/drawer_width"
            android:layout_height="match_parent"
            android:choiceMode="singleChoice"
            android:background="@android:color/white"
            android:fitsSystemWindows="true" />
       

    </com.androprogrammer.test.materialapp1.ScrimInsetsFrameLayout>
</android.support.v4.widget.DrawerLayout>
actionbarlayout.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.Toolbar
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:minHeight="?attr/actionBarSize"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:id="@+id/toolbar"
    android:elevation="4dp"
    app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
    android:background="@color/primary"/>

drawerlist_header.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ImageView
        android:layout_width="match_parent"
        android:layout_height="160dp"
        android:id="@+id/imageView"
        android:scaleType="centerCrop"
        android:clickable="false"
        android:src="@drawable/headerbg"
        android:layout_gravity="center_horizontal|top" />
</FrameLayout>

Step -2 : Now layouts are set for main view. now we will create style for app so it will look same threw out the app.
values/styles.xml
    

    

    
values/attrs.xml

    
        
    

values/color.xml

    #fe2e2e
    #EFcb2424

values/dimens.xml

    2dp
    4dp
    320dp
    56dp


values/strings.xml


    My Application1
    Settings
    open
    close


values-v21/styles.xml


Step - 3 : Now we will move forward to code section of the app which was also know as dirty or magical section of the app.  put below code in ScrimInsetsFrameLayout classor what ever you named it in xml layout. 
ScrimInsetsFrameLayout.java
package com.androprogrammer.test.materialapp1;

/**
* Copyright 2014 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.support.v4.view.ViewCompat;
import android.util.AttributeSet;
import android.widget.FrameLayout;

/**
 * A layout that draws something in the insets passed to {@link #fitSystemWindows(Rect)}, i.e. the area above UI chrome
 * (status and navigation bars, overlay action bars).
 */
public class ScrimInsetsFrameLayout extends FrameLayout {
    private Drawable mInsetForeground;

    private Rect mInsets;
    private Rect mTempRect = new Rect();
    private OnInsetsCallback mOnInsetsCallback;

    public ScrimInsetsFrameLayout(Context context) {
        super(context);
        init(context, null, 0);
    }

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

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

    private void init(Context context, AttributeSet attrs, int defStyle) {
        final TypedArray a = context.obtainStyledAttributes(attrs,
                R.styleable.ScrimInsetsView, defStyle, 0);
        if (a == null) {
            return;
        }
        mInsetForeground = a.getDrawable(R.styleable.ScrimInsetsView_insetForeground);
        a.recycle();

        setWillNotDraw(true);
    }

    @Override
    protected boolean fitSystemWindows(Rect insets) {
        mInsets = new Rect(insets);
        setWillNotDraw(mInsetForeground == null);
        ViewCompat.postInvalidateOnAnimation(this);
        if (mOnInsetsCallback != null) {
            mOnInsetsCallback.onInsetsChanged(insets);
        }
        return true; // consume insets
    }

    @Override
    public void draw(Canvas canvas) {
        super.draw(canvas);

        int width = getWidth();
        int height = getHeight();
        if (mInsets != null && mInsetForeground != null) {
            int sc = canvas.save();
            canvas.translate(getScrollX(), getScrollY());

            // Top
            mTempRect.set(0, 0, width, mInsets.top);
            mInsetForeground.setBounds(mTempRect);
            mInsetForeground.draw(canvas);

            // Bottom
            mTempRect.set(0, height - mInsets.bottom, width, height);
            mInsetForeground.setBounds(mTempRect);
            mInsetForeground.draw(canvas);

            // Left
            mTempRect.set(0, mInsets.top, mInsets.left, height - mInsets.bottom);
            mInsetForeground.setBounds(mTempRect);
            mInsetForeground.draw(canvas);

            // Right
            mTempRect.set(width - mInsets.right, mInsets.top, width, height - mInsets.bottom);
            mInsetForeground.setBounds(mTempRect);
            mInsetForeground.draw(canvas);

            canvas.restoreToCount(sc);
        }
    }

    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();
        if (mInsetForeground != null) {
            mInsetForeground.setCallback(this);
        }
    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        if (mInsetForeground != null) {
            mInsetForeground.setCallback(null);
        }
    }

    /**
     * Allows the calling container to specify a callback for custom processing when insets change (i.e. when
     * {@link #fitSystemWindows(Rect)} is called. This is useful for setting padding on UI elements based on
     * UI chrome insets (e.g. a Google Map or a ListView). When using with ListView or GridView, remember to set
     * clipToPadding to false.
     */
    public void setOnInsetsCallback(OnInsetsCallback onInsetsCallback) {
        mOnInsetsCallback = onInsetsCallback;
    }

    public static interface OnInsetsCallback {
        public void onInsetsChanged(Rect insets);
    }
}


It is copyrighted by Google developer so if you copy it please give proper reference to them.
MainActivity.java
package com.androprogrammer.test.materialapp1;

import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.support.v7.app.ActionBarDrawerToggle;
import android.support.v7.widget.DefaultItemAnimator;
import android.support.v7.widget.LinearLayoutManager;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ListView;
import android.widget.Toast;

public class MainActivity extends ActionBarActivity implements ListView.OnItemClickListener
{
    private android.support.v7.widget.RecyclerView mRecyclerView;
    private android.support.v7.widget.RecyclerView.Adapter mAdapter;
    private android.support.v7.widget.RecyclerView.LayoutManager mLayoutManager;
    private android.support.v7.widget.Toolbar toolbar;
    private android.support.v4.widget.DrawerLayout drawerLayout;
    private ActionBarDrawerToggle drawerToggle;
    private ListView leftDrawerList;
    private ArrayAdapter<string> navigationDrawerAdapter;
    private Button fab;
    private String[] leftSliderData = {"Home", "Android", "Sitemap", "About", "Contact Me"};
    protected String[] myDataset = {"facebook", "Google+", "twitter", "Linkdin", "pintrest"};


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

        setRef();

        //Set the custom toolbar
        if (toolbar != null)
        {
            toolbar.setTitle(R.string.app_name);
            setSupportActionBar(toolbar);
        }

        drawerToggle = new ActionBarDrawerToggle(
                this,
                drawerLayout,
                toolbar,
                R.string.open,
                R.string.close
        )

        {
            public void onDrawerClosed(View view)
            {
                Log.d("MainActivity", "OnDrawerClosed");
                super.onDrawerClosed(view);
                invalidateOptionsMenu();
            }

            public void onDrawerOpened(View drawerView)
            {
                Log.d("MainActivity", "OnDrawerOpened");
                super.onDrawerOpened(drawerView);
                invalidateOptionsMenu();
            }
        };

        drawerLayout.setDrawerListener(drawerToggle);

        getSupportActionBar().setDisplayHomeAsUpEnabled(true);

        // use this setting to improve performance if you know that changes
        // in content do not change the layout size of the RecyclerView
        mRecyclerView.setHasFixedSize(true);

        // use a linear layout manager
        mLayoutManager = new LinearLayoutManager(this);
        mRecyclerView.setLayoutManager(mLayoutManager);
        mRecyclerView.setItemAnimator(new DefaultItemAnimator());

        // specify an adapter (see also next example)
        mAdapter = new MyAdapter(myDataset);
        mRecyclerView.setAdapter(mAdapter);

    }

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

    private void setRef()
    {
        mRecyclerView = (android.support.v7.widget.RecyclerView) findViewById(R.id.my_recycler_view);
        fab = (Button) findViewById(R.id.addButton);
        drawerLayout = (android.support.v4.widget.DrawerLayout) findViewById(R.id.drawer_layout);
        drawerLayout.setStatusBarBackground(R.color.primarydark);

        toolbar = (android.support.v7.widget.Toolbar) findViewById(R.id.toolbar);
        leftDrawerList = (ListView) findViewById(R.id.list_drawer);

        View list_header = getLayoutInflater().inflate(R.layout.drawerlist_header, null);
        leftDrawerList.addHeaderView(list_header);
        navigationDrawerAdapter = new ArrayAdapter<string>(MainActivity.this, android.R.layout.simple_expandable_list_item_1, leftSliderData);
        leftDrawerList.setAdapter(navigationDrawerAdapter);
        leftDrawerList.setOnItemClickListener(this);
    }

    @Override
    public void onItemClick(AdapterView parent, View view, int position, long id)
    {
        Toast.makeText(getApplicationContext(), "Clicked on " + leftSliderData[position - 1], Toast.LENGTH_LONG).show();
    }

}


Screen Shots

android navigation drawer android navigation drawer

Step - 4 : Now every thing done but please apply theme in manifest.xml in application tag.
android:theme="@style/AppTheme"
And one more thing is that to run app you need to add libraries in app.gradle file it is in app folder named as build.gradle
dependencies {
    compile fileTree(include: ['*.jar'], dir: 'libs')
    compile 'com.android.support:support-v4:21.0.0'
    compile 'com.android.support:recyclerview-v7:21.0.+'
    compile 'com.android.support:appcompat-v7:21.0.2'


Well that's it from my side now run the app and check how it works. hope you learned from this tutorial if find it useful please hare it with your friends or in circle. if i miss any thing or you have any query please let me know in below comment box.

Keep coding...

Let's Start with Material Design

Material design sampleHave Heard lots of about Android Material design but don't know from where to start, well in this tutorial i will tell you how to create app from scratch with material design. This tutorial is just a beginning of Material design tutorial. so i just keep it simple.


From Kitkat and all older version of android Google has given new overhaul with the Material design in Android 5.0 (Well known as Android Lollipop). with new Card view , Recycler view and some of the new animation it is really user friendly. User will love the animation in this version. So lets start new project with android api -21.

Step - 1 : Create New Android application and give name MainActivity to main activity and same name for layout file.
  • minSdkVersion="21”
  • targetSdkVersion="21"
now put below code in your project main.xml file. you can set layout as you want but for practice purpose you can go with same.
Main.xml


    <android.support.v7.widget.recyclerview android:elevation="5dp" android:id="@+id/my_recycler_view" android:layout_height="match_parent" android:layout_marginleft="4dp" android:layout_marginright="4dp" android:layout_margintop="10dp" android:layout_width="match_parent" android:scrollbars="vertical">
    </android..support.v7.widget.recyclerview>

    




I have used frame layout. it allows to put component(widget/view) at any specific position. To create round button there are number of ways but i have used simple way of setOval method of outline class. this button is for sample in this tutorial. i will make use of it next tutorial so keep subscribed with us.now put below code for oval shape of it.
drawable/ripple.xml

    
        
            
        
    


Below one is layout for recycler view item. RecyclerView is also list view but with some extended features like list item remove or add animation and much more. so put below file in row_layout.xml file.
layout/row_layout.xml


    
    


Step - 2 : Now your layout is set but to apply your custom theme now add style.xml file in value-v21 folder and put below code in it. you can change color codes as per your app theme.
value-v21/styles.xml

    


Step - 3 : Now All layout set so we move to code section. this is start up with material design so i have used simple array to display values in recycler view. put below code in main.java file.
Main.java
package com.androprogrammer.test.myapplication1;

import android.app.Activity;
import android.graphics.Outline;
import android.os.Bundle;
import android.support.v7.widget.DefaultItemAnimator;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewOutlineProvider;
import android.widget.Button;


public class MainActivity extends Activity
{
    private RecyclerView mRecyclerView;
    private RecyclerView.Adapter mAdapter;
    private RecyclerView.LayoutManager mLayoutManager;
    protected String [] myDataset = {"facebook", "Google+", "twitter", "Linkdin", "pintrest"};


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

        mRecyclerView = (RecyclerView) findViewById(R.id.my_recycler_view);

        Button fab = (Button) findViewById(R.id.addButton);

        ViewOutlineProvider viewOutlineProvider = new ViewOutlineProvider() {
            @Override
            public void getOutline(View view, Outline outline) {
                // Or read size directly from the view's width/height
                outline.setOval(0, 0, 56, 56);
            }
        };

        fab.setOutlineProvider(viewOutlineProvider);

        // use this setting to improve performance if you know that changes
        // in content do not change the layout size of the RecyclerView
        mRecyclerView.setHasFixedSize(true);

        // use a linear layout manager
        mLayoutManager = new LinearLayoutManager(this);
        mRecyclerView.setLayoutManager(mLayoutManager);
        mRecyclerView.setItemAnimator(new DefaultItemAnimator());

        // specify an adapter
        mAdapter = new MyAdapter(myDataset);
        mRecyclerView.setAdapter(mAdapter);

    }


    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }
}


MyAdapter.java
package com.androprogrammer.test.myapplication1;

import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import android.widget.Toast;

/**
 * Created by wasim on 07/12/14.
 */
public class MyAdapter extends RecyclerView.Adapter<myadapter .viewholder=""> {
    protected String[] mDataset;

    // Provide a reference to the views for each data item
    // Complex data items may need more than one view per item, and
    // you provide access to all the views for a data item in a view holder
    public static class ViewHolder extends RecyclerView.ViewHolder {
        // each data item is just a string in this case
        public TextView mTextView;
        public ViewHolder(View v) {
            super(v);
            mTextView = (TextView) v.findViewById(R.id.row_tv);
            v.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v)
                {
                    Toast.makeText(v.getContext(), "Clicked on " + getPosition(), Toast.LENGTH_LONG).show();
                }
            });
        }
    }

    // Provide a suitable constructor (depends on the kind of dataset)
    public MyAdapter(String[] myDataset) {
        this.mDataset = myDataset;
    }

    // Create new views (invoked by the layout manager)
    @Override
    public MyAdapter.ViewHolder onCreateViewHolder(ViewGroup parent,
                                                   int viewType) {
        // create a new view
        View v = LayoutInflater.from(parent.getContext())
                .inflate(R.layout.row_layout, parent, false);
        // set the view's size, margins, paddings and layout parameters

        ViewHolder vh = new ViewHolder(v);
        return vh;
    }

    // Replace the contents of a view (invoked by the layout manager)
    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {
        // - get element from your dataset at this position
        // - replace the contents of the view with that element
        holder.mTextView.setText(mDataset[position]);

    }

    // Return the size of your dataset (invoked by the layout manager)
    @Override
    public int getItemCount() {
        return mDataset.length;
    }
}


RecyclerView.Adapter is extened like other adapters in android. view more about adapters in my previous post. in this class you have to implement view holder pattern that will assign view to each row. well there is no more changes in recycler view then list view. in list view adapter you assign view in getView method while in this case you can assign data in onBindViewholder method.


Screen Shots

Let's Start with Material Design Let's Start with Material Design

So that's it from coding side now you can run app and test it. i hope you it will run smoothly.
if you have any query comment it in below comment box.
Keep coding...