Thursday, April 28, 2016

How to Filter objects in custom Adapter using SearchView and ListView

Hi guys,

Recently I was asked to make a ListView with custom adapter. To show the list view item I have used CardView. Of course this was the easy part. Then again I was asked to include the search functionality for this ListView. In this blog post I'm going to show you the code which I used to achieve this target. I have faced several issues while I'm doing this and hope you can avoid them by using the code I used. Please make a comment if you know a better practice. I'm always looking for knowledge.

I'm going to add comment lines so you can understand the code well. Some explanations have done before the code and here included are only the necessary java and xml files. Ok, Here we go.

Main components used in this projects are,
1. android.support.v7.widget.SearchView;
2. android.support.v4.app.ListFragment;
3. android.support.v7.widget.CardView
4. Custom ArrayAdapter.

I have used a generated master/detail project from android studio. As i said I'm NOT going to include all java files. Here is the fragment that generate list.

1. ProductListFragment.java

1:  package com.???.????.View;  
2:  import android.app.Activity;  
3:  import android.app.SearchManager;  
4:  import android.content.Context;  
5:  import android.os.Bundle;  
6:  import android.support.v4.app.ListFragment;  
7:  import android.support.v7.widget.SearchView;  
8:  import android.util.Log;  
9:  import android.view.LayoutInflater;  
10:  import android.view.Menu;  
11:  import android.view.MenuInflater;  
12:  import android.view.MenuItem;  
13:  import android.view.View;  
14:  import android.view.ViewGroup;  
15:  import android.widget.ArrayAdapter;  
16:  import android.widget.ListView;  
17:  import com.???.????.Adapter.ProductItemAdapter; // can't expose my company :-)  
18:  import com..???.????.Model.ProductItem;  
19:  import com..???.????.R;  
20:  import java.util.ArrayList;  
21:  import java.util.HashMap;  
22:  import java.util.List;  
23:  import java.util.Map;  
24:  public class ProductListFragment extends ListFragment {  
25:    private static final String STATE_ACTIVATED_POSITION = "activated_position";  
26:    private Callbacks mCallbacks = sDummyCallbacks;  
27:    private int mActivatedPosition = ListView.INVALID_POSITION;  
28:    public static Map<String, ProductItem> ITEM_MAP = new HashMap<String, ProductItem>();  
29:    public static List<ProductItem> list = new ArrayList<ProductItem>();  
30:    private SearchView searchView;  
31:    private SearchView.OnQueryTextListener queryTextListener;  
32:    private ProductItemAdapter productItemAdapter;  
33:    public interface Callbacks {  
34:      public void onItemSelected(String id);  
35:    }  
36:    private static Callbacks sDummyCallbacks = new Callbacks() {  
37:      @Override  
38:      public void onItemSelected(String id) {  
39:      }  
40:    };  
41:    public ProductListFragment() {  
42:    }  
43:    @Override  
44:    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {  
45:      inflater.inflate(R.menu.menu_searchable, menu);  
46:      MenuItem searchItem = menu.findItem(R.id.searchButtonInMenu);  
47:      SearchManager searchManager = (SearchManager) getActivity().getSystemService(Context.SEARCH_SERVICE);  
48:      if (searchItem != null) {  
49:        searchView = (SearchView) searchItem.getActionView();  
50:      }  
51:      if(searchItem != null) {  
52:        searchView.setSearchableInfo(searchManager.getSearchableInfo(getActivity().getComponentName()));  
53:        queryTextListener = new SearchView.OnQueryTextListener() {  
54:          @Override  
55:          public boolean onQueryTextChange(String newText) {  
56:            productItemAdapter.getFilter().filter(newText); // I have overide getFilter() in my custom adapter  
57:            return true;  
58:          }  
59:          @Override  
60:          public boolean onQueryTextSubmit(String query) {  
61:            return true;  
62:          }  
63:        };  
64:        searchView.setOnQueryTextListener(queryTextListener);  
65:      }  
66:      super.onCreateOptionsMenu(menu, inflater);  
67:    }  
68:    @Override  
69:    public void onCreate(Bundle savedInstanceState) {  
70:      super.onCreate(savedInstanceState);  
71:      productItemAdapter = new ProductItemAdapter(getActivity(), R.layout.product_item, getModel());  
72:      setListAdapter(productItemAdapter);  
73:    }  
74:    private List<ProductItem> getModel() {  
75:      list.clear();  
76:      ProductItem newItem = new ProductItem();  
77:      newItem.setId("1");  
78:      newItem.setProductImage(R.drawable.drone);  
79:      newItem.setProductName("TestTittle");  
80:      newItem.setProductCode("12434");  
81:      newItem.setProductPrice("$ 450.00");  
82:      newItem.setProductDescription("This is a line that describe the above product. Add all the details you want here. From now ther will be dummy" +  
83:          "content. as toyu sdf asdf asd fdsdfk sdfsd asfsdf dfsdfdf sdp;sdmsm");  
84:      ProductItem newItem1 = new ProductItem();  
85:      newItem1.setId("2");  
86:      newItem1.setProductImage(R.drawable.shoe);  
87:      newItem1.setProductName("TestTittle1");  
88:      newItem1.setProductCode("12434111");  
89:      newItem1.setProductDescription("THIS IS FOR SECOND ITEM AND THESE ARE STATIC CONTENTS. :-) This is a line that describe the above product. Add all the details you want here. From now ther will be dummy" +  
90:          "content. as toyu sdf asdf asd fdsdfk sdfsd asfsdf dfsdfdf sdp;sdmsm");  
91:      newItem1.setProductPrice("$ 250.00");  
92:      ProductItem newItem3 = new ProductItem();  
93:      newItem3.setId("3");  
94:      newItem3.setProductImage(R.drawable.shoe);  
95:      newItem3.setProductName("Shoes of me");  
96:      newItem3.setProductCode("12434111");  
97:      newItem3.setProductDescription("THIS IS FOR SECOND ITEM AND THESE ARE STATIC CONTENTS. :-) This is a line that describe the above product. Add all the details you want here. From now ther will be dummy" +  
98:          "content. as toyu sdf asdf asd fdsdfk sdfsd asfsdf dfsdfdf sdp;sdmsm");  
99:      newItem3.setProductPrice("$ 250.00");  
100:      ProductItem newItem4 = new ProductItem();  
101:      newItem4.setId("4");  
102:      newItem4.setProductImage(R.drawable.shoe);  
103:      newItem4.setProductName("drone is here");  
104:      newItem4.setProductCode("12434111");  
105:      newItem4.setProductDescription("THIS IS FOR SECOND ITEM AND THESE ARE STATIC CONTENTS. :-) This is a line that describe the above product. Add all the details you want here. From now ther will be dummy" +  
106:          "content. as toyu sdf asdf asd fdsdfk sdfsd asfsdf dfsdfdf sdp;sdmsm");  
107:      newItem4.setProductPrice("$ 250.00");  
108:      ProductItem newItem5 = new ProductItem();  
109:      newItem5.setId("5");  
110:      newItem5.setProductImage(R.drawable.shoe);  
111:      newItem5.setProductName("drone is here");  
112:      newItem5.setProductCode("12434111");  
113:      newItem5.setProductDescription("THIS IS FOR SECOND ITEM AND THESE ARE STATIC CONTENTS. :-) This is a line that describe the above product. Add all the details you want here. From now ther will be dummy" +  
114:          "content. as toyu sdf asdf asd fdsdfk sdfsd asfsdf dfsdfdf sdp;sdmsm");  
115:      newItem5.setProductPrice("$ 250.00");  
116:      list.add(newItem);  
117:      ITEM_MAP.put(String.valueOf(list.size()), newItem);  
118:      list.add(newItem1);  
119:      ITEM_MAP.put(String.valueOf(list.size()), newItem1);  
120:      list.add(newItem3);  
121:      ITEM_MAP.put(String.valueOf(list.size()), newItem3);  
122:      list.add(newItem4);  
123:      ITEM_MAP.put(String.valueOf(list.size()), newItem4);  
124:      list.add(newItem5);  
125:      ITEM_MAP.put(String.valueOf(list.size()), newItem5);  
126:      return list;  
127:    }  
128:    @Override  
129:    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {  
130:      View v = inflater.inflate(R.layout.product_list_container,null);  
131:      setHasOptionsMenu(true);  
132:      return v;  
133:    }  
134:    @Override  
135:    public void onViewCreated(View view, Bundle savedInstanceState) {  
136:      super.onViewCreated(view, savedInstanceState);  
137:      // Restore the previously serialized activated item position.  
138:      if (savedInstanceState != null && savedInstanceState.containsKey(STATE_ACTIVATED_POSITION)) {  
139:        setActivatedPosition(savedInstanceState.getInt(STATE_ACTIVATED_POSITION));  
140:      }  
141:    }  
142:    @Override  
143:    public void onAttach(Activity activity) {  
144:      super.onAttach(activity);  
145:      // Activities containing this fragment must implement its callbacks.  
146:      if (!(activity instanceof Callbacks)) {  
147:        throw new IllegalStateException("Activity must implement fragment's callbacks.");  
148:      }  
149:      mCallbacks = (Callbacks) activity;  
150:    }  
151:    @Override  
152:    public void onDetach() {  
153:      super.onDetach();  
154:      // Reset the active callbacks interface to the dummy implementation.  
155:      mCallbacks = sDummyCallbacks;  
156:    }  
157:    @Override  
158:    public void onListItemClick(ListView listView, View view, int position, long id) {  
159:      super.onListItemClick(listView, view, position, id);  
160:      // Notify the active callbacks interface (the activity, if the  
161:      // fragment is attached to one) that an item has been selected.  
162:      mCallbacks.onItemSelected(list.get(position).getId());  
163:    }  
164:    @Override  
165:    public void onSaveInstanceState(Bundle outState) {  
166:      super.onSaveInstanceState(outState);  
167:      if (mActivatedPosition != ListView.INVALID_POSITION) {  
168:        // Serialize and persist the activated item position.  
169:        outState.putInt(STATE_ACTIVATED_POSITION, mActivatedPosition);  
170:      }  
171:    }  
172:    /**  
173:     * Turns on activate-on-click mode. When this mode is on, list items will be  
174:     * given the 'activated' state when touched.  
175:     */  
176:    public void setActivateOnItemClick(boolean activateOnItemClick) {  
177:      // When setting CHOICE_MODE_SINGLE, ListView will automatically  
178:      // give items the 'activated' state when touched.  
179:      getListView().setChoiceMode(activateOnItemClick ? ListView.CHOICE_MODE_SINGLE : ListView.CHOICE_MODE_NONE);  
180:    }  
181:    private void setActivatedPosition(int position) {  
182:      if (position == ListView.INVALID_POSITION) {  
183:        getListView().setItemChecked(mActivatedPosition, false);  
184:      } else {  
185:        getListView().setItemChecked(position, true);  
186:      }  
187:      mActivatedPosition = position;  
188:    }  
189:  }  

2. menu_searchable.xml (where the search button creates)

1:  <?xml version="1.0" encoding="utf-8"?>  
2:  <menu xmlns:android="http://schemas.android.com/apk/res/android"  
3:    xmlns:app="http://schemas.android.com/apk/res-auto">  
4:    <item  
5:      android:id="@+id/searchButtonInMenu"  
6:      android:title="Search Products"  
7:      android:icon="@android:drawable/ic_menu_search"  
8:      app:showAsAction ="collapseActionView|ifRoom"  
9:      app:actionViewClass="android.support.v7.widget.SearchView" />  
10:  </menu>  

3. product_list_container.xml  (the fragments layout and where the ListView is. NOTE you have to use android:id="@android:id/list" because ProductListFragment.java is extending from ListFragment).
1:  <?xml version="1.0" encoding="utf-8"?>  
2:  <LinearLayout  
3:    xmlns:android="http://schemas.android.com/apk/res/android"  
4:    android:layout_width="match_parent"  
5:    android:orientation="vertical"  
6:    android:layout_height="match_parent"  
7:    android:background="#ddd"  
8:    >  
9:    <RelativeLayout  
10:      android:layout_width="match_parent"  
11:      android:layout_height="wrap_content"  
12:      android:background="#fff"  
13:      android:paddingTop="2dp"  
14:      android:paddingBottom="2dp">  
15:      <CheckBox  
16:        android:id="@+id/checkboxActive"  
17:        android:layout_width="wrap_content"  
18:        android:layout_height="wrap_content"  
19:        android:text="Active"/>  
20:      <CheckBox  
21:        android:id="@+id/checkboxInactive"  
22:        android:layout_width="wrap_content"  
23:        android:layout_height="wrap_content"  
24:        android:text="Inactive"  
25:        android:layout_below="@id/checkboxActive"/>  
26:      <Spinner  
27:        android:layout_width="wrap_content"  
28:        android:layout_height="wrap_content"  
29:        android:layout_alignParentRight="true"  
30:        android:drawSelectorOnTop="true"  
31:        android:entries="@array/array_name">  
32:      </Spinner>  
33:    </RelativeLayout>  
34:    <ListView  
35:      android:id="@android:id/list"  
36:      android:layout_width="match_parent"  
37:      android:layout_height="match_parent"  
38:      android:divider="@android:color/transparent"  
39:      android:dividerHeight="4dp"  
40:      android:background="#ddd"  
41:      android:layout_marginLeft="6dp"  
42:      android:layout_marginRight="6dp"  
43:      />  
44:  </LinearLayout>  

4. product_item.xml (layout for each row in ListView. I have used CardView here).

1:  <?xml version="1.0" encoding="utf-8"?>  
2:  <android.support.v7.widget.CardView  
3:    xmlns:android="http://schemas.android.com/apk/res/android"  
4:    xmlns:app="http://schemas.android.com/apk/res-auto"  
5:    android:layout_width="match_parent"  
6:    android:layout_height="wrap_content"  
7:    app:cardBackgroundColor = "#fff"  
8:    app:cardCornerRadius="2dp"  
9:    >  
10:    <LinearLayout  
11:      android:padding="2dp"  
12:      android:layout_margin="8dp"  
13:      android:layout_width="match_parent"  
14:      android:layout_height="match_parent"  
15:      android:orientation="horizontal"  
16:      android:background="@android:color/white">  
17:      <ImageView  
18:        android:id="@+id/productImage"  
19:        android:layout_width="0dp"  
20:        android:layout_height="match_parent"  
21:        android:layout_weight="1"  
22:        android:gravity="center"  
23:        android:src="@drawable/drone" />  
24:      <LinearLayout  
25:        android:layout_width="0dp"  
26:        android:layout_height="wrap_content"  
27:        android:layout_gravity="center"  
28:        android:layout_weight="2"  
29:        android:orientation="vertical">  
30:        <TextView  
31:          android:id="@+id/productName"  
32:          android:layout_width="match_parent"  
33:          android:layout_height="match_parent"  
34:          android:gravity="center"  
35:          android:text="Pro Title"  
36:          android:textStyle="bold" />  
37:        <TextView  
38:          android:id="@+id/productCode"  
39:          android:layout_width="match_parent"  
40:          android:layout_height="match_parent"  
41:          android:gravity="center"  
42:          android:text="Sub Title" />  
43:      </LinearLayout>  
44:      <TextView  
45:        android:id="@+id/productPrice"  
46:        android:layout_width="0dp"  
47:        android:layout_height="match_parent"  
48:        android:layout_weight="1"  
49:        android:gravity="center"  
50:        android:text="Sub Title" />  
51:    </LinearLayout>  
52:  </android.support.v7.widget.CardView>  

5. ProductItem.java (POJO class)

1:  package com.duo.cloudchargev1.Model;  
2:  /**  
3:   * Created by Sijith on 4/7/2016.  
4:   */  
5:  public class ProductItem {  
6:    private String id;  
7:    private int productImage;  
8:    private String productName;  
9:    private String productCode;  
10:    private String productPrice;  
11:    private String productDescription;  
12:    public String getId() {  
13:      return id;  
14:    }  
15:    public void setId(String id) {  
16:      this.id = id;  
17:    }  
18:    public int getProductImage() {  
19:      return productImage;  
20:    }  
21:    public void setProductImage(int productImage) {  
22:      this.productImage = productImage;  
23:    }  
24:    public String getProductName() {  
25:      return productName;  
26:    }  
27:    public void setProductName(String productName) {  
28:      this.productName = productName;  
29:    }  
30:    public String getProductCode() {  
31:      return productCode;  
32:    }  
33:    public void setProductCode(String productCode) {  
34:      this.productCode = productCode;  
35:    }  
36:    public String getProductPrice() {  
37:      return productPrice;  
38:    }  
39:    public void setProductPrice(String productPrice) {  
40:      this.productPrice = productPrice;  
41:    }  
42:    public String getProductDescription() {  
43:      return productDescription;  
44:    }  
45:    public void setProductDescription(String productDescription) {  
46:      this.productDescription = productDescription;  
47:    }  
48:    @Override  
49:    public String toString() {  
50:      return productName;  
51:    }  
52:  }  


6. ProductItemAdapter.java (A custom adapter and most important java class. I am going to include important things I have learnt after the code.)

1:  package com.????.??????.Adapter;  
2:  import android.content.Context;  
3:  import android.view.LayoutInflater;  
4:  import android.view.View;  
5:  import android.view.ViewGroup;  
6:  import android.widget.ArrayAdapter;  
7:  import android.widget.Filter;  
8:  import android.widget.ImageView;  
9:  import android.widget.TextView;  
10:  import com.????.??????.Model.ProductItem;  
11:  import com.????.??????.R;  
12:  import java.util.ArrayList;  
13:  import java.util.List;  
14:  public class ProductItemAdapter extends ArrayAdapter<ProductItem> {  
15:    private List<ProductItem> productItemListOriginal;  
16:    private List<ProductItem> productItemListFiltered; // if you are not keeping a duplicate no product item views when deleting values in search view  
17:    private final Context context;  
18:    public ProductItemAdapter(Context context, int resource, List<ProductItem> productItems) {  
19:      super(context, resource, productItems);  
20:      this.context = context;  
21:      this.productItemListOriginal = productItems;  
22:      this.productItemListFiltered = productItems;  
23:    }  
24:    static class ViewHolder {  
25:      protected ImageView productImage;  
26:      protected TextView productName;  
27:      protected TextView productCode;  
28:      protected TextView productPrice;  
29:    }  
30:    @Override  
31:    public View getView(int position, View convertView, ViewGroup parent) {  
32:      View view = null;  
33:      if(convertView == null){  
34:        view = LayoutInflater.from(parent.getContext()).inflate(R.layout.product_item,parent,false);  
35:        ViewHolder viewHolder = new ViewHolder();  
36:        viewHolder.productImage = (ImageView) view.findViewById(R.id.productImage);  
37:        viewHolder.productName = (TextView)view.findViewById(R.id.productName);  
38:        viewHolder.productCode = (TextView)view.findViewById(R.id.productCode);  
39:        viewHolder.productPrice = (TextView)view.findViewById(R.id.productPrice);  
40:        view.setTag(viewHolder);  
41:      }  
42:      else{  
43:        view = convertView;  
44:      }  
45:      ViewHolder holder = (ViewHolder) view.getTag();  
46:      holder.productImage.setImageResource(productItemListFiltered.get(position).getProductImage());  
47:      holder.productName.setText(productItemListFiltered.get(position).getProductName());  
48:      holder.productCode.setText(productItemListFiltered.get(position).getProductCode());  
49:      holder.productPrice.setText(productItemListFiltered.get(position).getProductPrice());  
50:      return view;  
51:    }  
52:    @Override  
53:    public Filter getFilter() {  
54:      return new Filter() {  
55:        @Override  
56:        protected FilterResults performFiltering(CharSequence constraint) {  
57:          constraint = constraint.toString().toLowerCase();  
58:          FilterResults result = new FilterResults();  
59:          if (constraint != null && constraint.toString().length() > 0) {  
60:            List<ProductItem> founded = new ArrayList<ProductItem>();  
61:            for(ProductItem item: productItemListOriginal){  
62:              if(item.toString().toLowerCase().contains(constraint)){  
63:                founded.add(item);  
64:              }  
65:            }  
66:            result.values = founded;  
67:            result.count = founded.size();  
68:          }else {  
69:            result.values = productItemListOriginal;  
70:            result.count = productItemListOriginal.size();  
71:          }  
72:          return result;  
73:        }  
74:        @Override  
75:        protected void publishResults(CharSequence constraint, FilterResults results) {  
76:          if (results != null) {  
77:            productItemListFiltered = (List<ProductItem>) results.values; // set the adapter list to data  
78:            notifyDataSetChanged(); // notify data set change  
79:          } else {  
80:            notifyDataSetChanged(); // notify data set change  
81:          }  
82:        }  
83:      };  
84:    }  
85:    @Override  
86:    public int getCount() { // indexoutofboundsexception if not implemented  
87:      return productItemListFiltered.size();   
88:    }  
89:  }  


Important things to Note...
  1. Override the getCount(). If you are not override this there will be indexOutOfBoundsException
  2. Create a original and duplicate Lists. If you make the changes to the original list, deleting        characters in SearchView will show you empty list. Always make changes to the duplicate list.

Here how it looks like.




Hope you can get something from this post. Happy Coding :-).

Monday, April 25, 2016

Common template for any android project (Multi screens support)

Hi All,

As I learn more about android, I have a need to create a common android project so  that I can use it to start the future projects which is surly easy my life. Any android project needs to support both phone and tablets for a flexible UI. So we need fragments (as google recommends) and code logic to handle multiple device support.

I'm going to show you my common project to begin for any android project which supports phones and tablets. I created this project referring developer site for android. But here I think my example will easier to read and understand. And yes, I'm still learning android so if you see any issue or things to improve, please comment. I'll really appreciate it.

Here's how my common template look like in phone and tablet.


Let's see the codes. Here's the project structure.


I put comments in the code so it should be self-explanatory.

1. AndroidManifest.xml
 <?xml version="1.0" encoding="utf-8"?>  
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"  
   package="com.sajithkumara.flexibletemplate" >  
   <application  
     android:allowBackup="true"  
     android:icon="@mipmap/ic_launcher"  
     android:label="@string/app_name"  
     android:supportsRtl="true"  
     android:theme="@style/AppTheme" >  
     <activity android:name=".activity.MainActivity" >  
       <intent-filter>  
         <action android:name="android.intent.action.MAIN" />  
         <category android:name="android.intent.category.LAUNCHER" />  
       </intent-filter>  
     </activity>  
     <activity android:name=".activity.BMIActivity"></activity>  
     <activity android:name=".activity.BMRActivity"></activity>  
   </application>  
 </manifest>  

2.build.gradle (Module: app)
 apply plugin: 'com.android.application'  
 android {  
   compileSdkVersion 22  
   buildToolsVersion "22.0.1"  
   defaultConfig {  
     applicationId "com.sajithkumara.flexibletemplate"  
     minSdkVersion 16  
     targetSdkVersion 22  
     versionCode 1  
     versionName "1.0"  
   }  
   buildTypes {  
     release {  
       minifyEnabled false  
       proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'  
     }  
   }  
 }  
 dependencies {  
   compile fileTree(dir: 'libs', include: ['*.jar'])  
   testCompile 'junit:junit:4.12'  
   compile 'com.android.support:appcompat-v7:22.2.1'  
   compile 'com.android.support:cardview-v7:22.2.1'  
 }  

3. MainActivity
 package com.sajithkumara.flexibletemplate.activity;  
 import android.content.Intent;  
 import android.support.v4.app.Fragment;  
 import android.support.v4.app.FragmentTransaction;  
 import android.os.Bundle;  
 import android.support.v7.app.AppCompatActivity;  
 import android.widget.FrameLayout;  
 import com.sajithkumara.flexibletemplate.R;  
 import com.sajithkumara.flexibletemplate.fragment.BMIFragment;  
 import com.sajithkumara.flexibletemplate.fragment.BMRFragment;  
 import com.sajithkumara.flexibletemplate.fragment.MenuFragment;  
 // this is the first view which includes MenuFragment  
 public class MainActivity extends AppCompatActivity implements MenuFragment.OnMenuItemSelectedListener {  
   FrameLayout frameContainer;  
   @Override  
   protected void onCreate(Bundle savedInstanceState) {  
     super.onCreate(savedInstanceState);  
     setContentView(R.layout.activity_main); // there are 2 activity_main.xml files.  
                         // 1 for phones(in layout), a for tablets (in layout-sw600dp)  
     frameContainer = (FrameLayout) findViewById(R.id.frame_container);  
     if (frameContainer == null) {  
       // If we have a phone activity_main.xml in layout folder loads.Do nothing here.  
     } else {  
       // If we have a tablet activity_main.xml in layout-sw600dp folder loads.  
       // load the BMIFragment in the frame_container  
       BMIFragment sef = new BMIFragment();  
       changeFragment(sef); // replace the fragments  
     }  
   }  
   @Override  
   public void onMenuItemSelect(int layoutId) { // Listener interface in MenuFragment  
     Intent intent = null;  
     if (frameContainer == null) { // If we have a phone.......  
       switch (layoutId) { // layoutId pass from MenuFragment  
         case 1: // if layoutId = 1, load BMIActivity(not BMIFragment)  
           intent = new Intent(this, BMIActivity.class);  
           intent.putExtra("layoutId", layoutId);  
           startActivity(intent);  
           return;  
         case 2:// if layoutId = 2, load BMRActivity(not BMRFragment)  
           intent = new Intent(this, BMRActivity.class);  
           startActivity(intent);  
           return;  
       }  
     } else {  // If we have a tablet.......  
       switch (layoutId) {  
         case 1: //load BMIFragment  
           BMIFragment sef = new BMIFragment();  
           changeFragment(sef);  
           return;  
         case 2: //load BMRFragment  
           BMRFragment bmiFragment = new BMRFragment();  
           changeFragment(bmiFragment);  
           return;  
       }  
     }  
   }  
   private void changeFragment(Fragment fragment) {  
     FragmentTransaction fragmentTransaction = null;  
     fragmentTransaction = getSupportFragmentManager().beginTransaction();  
     fragmentTransaction.replace(R.id.frame_container, fragment);  
     fragmentTransaction.commit();  
   }  
 }  

4. BMIActivity
 package com.sajithkumara.flexibletemplate.activity;  
 import android.os.Bundle;  
 import android.support.v7.app.AppCompatActivity;  
 import android.view.MenuItem;  
 import com.sajithkumara.flexibletemplate.R;  
 public class BMIActivity extends AppCompatActivity {  
   // Put all the java controlling code in BMIFragment. No need to code here.  
   @Override  
   protected void onCreate(Bundle savedInstanceState) {  
     super.onCreate(savedInstanceState);  
     setContentView(R.layout.activity_bmi);  
   }  
   @Override  
   public boolean onOptionsItemSelected(MenuItem item) {  
     this.finish(); // end this activity and go back  
     return super.onOptionsItemSelected(item);  
   }  
 }  

5. BMRActivity
 package com.sajithkumara.flexibletemplate.activity;  
 import android.os.Bundle;  
 import android.support.v7.app.AppCompatActivity;  
 import android.view.MenuItem;  
 import com.sajithkumara.flexibletemplate.R;  
 public class BMRActivity extends AppCompatActivity {  
   // Put all the java controlling code in BMRFragment. No need to code here.  
   @Override  
   protected void onCreate(Bundle savedInstanceState) {  
     super.onCreate(savedInstanceState);  
     setContentView(R.layout.activity_bmr);  
   }  
   @Override  
   public boolean onOptionsItemSelected(MenuItem item) {  
     this.finish(); // end this activity and go back  
     return super.onOptionsItemSelected(item);  
   }  
 }  

6. MenuFragment
 package com.sajithkumara.flexibletemplate.fragment;  
 import android.app.Activity;  
 import android.os.Bundle;  
 import android.support.v4.app.Fragment;  
 import android.support.v7.widget.CardView;  
 import android.view.LayoutInflater;  
 import android.view.View;  
 import android.view.ViewGroup;  
 import com.sajithkumara.flexibletemplate.R;  
 public class MenuFragment extends Fragment {  
   private OnMenuItemSelectedListener mListener;  
   CardView cardViewBMI, cardViewBMR;  
   public static MenuFragment newInstance(String param1, String param2) {  
     MenuFragment fragment = new MenuFragment();  
     return fragment;  
   }  
   public MenuFragment() {  
     // Required empty public constructor  
   }  
   @Override  
   public void onCreate(Bundle savedInstanceState) {  
     super.onCreate(savedInstanceState);  
   }  
   @Override  
   public View onCreateView(LayoutInflater inflater, ViewGroup container,  
                Bundle savedInstanceState) {  
     View v = inflater.inflate(R.layout.fragment_menu, container, false);  
     cardViewBMI = (CardView) v.findViewById(R.id.cardBMI);  
     cardViewBMR = (CardView) v.findViewById(R.id.cardBMR);  
     cardViewBMI.setOnClickListener(new View.OnClickListener() {  
       @Override  
       public void onClick(View v) {  
         onMenuItemSelected(1); // layoutId = 1 to identify BMI card view click  
       }  
     });  
     cardViewBMR.setOnClickListener(new View.OnClickListener() {  
       @Override  
       public void onClick(View v) {  
         onMenuItemSelected(2); // layoutId = 2 to identify BMR card view click  
       }  
     });  
     return v;  
   }  
   public void onMenuItemSelected(int layoutId) {  
     if (mListener != null) {  
       mListener.onMenuItemSelect(layoutId);  
     }  
   }  
   @Override  
   public void onAttach(Activity activity) {  
     super.onAttach(activity);  
     try {  
       mListener = (OnMenuItemSelectedListener) activity;  
     } catch (ClassCastException e) {  
       throw new ClassCastException(activity.toString()  
           + " must implement OnMenuItemSelectedListener");  
     }  
   }  
   @Override  
   public void onDetach() {  
     super.onDetach();  
     mListener = null;  
   }  
   public interface OnMenuItemSelectedListener {  
     public void onMenuItemSelect(int layoutId);  
   }  
 }  

7. BMIFragment
 package com.sajithkumara.flexibletemplate.fragment;  
 import android.os.Bundle;  
 import android.support.v4.app.Fragment;  
 import android.support.v7.app.ActionBar;  
 import android.support.v7.app.AppCompatActivity;  
 import android.view.LayoutInflater;  
 import android.view.View;  
 import android.view.ViewGroup;  
 import android.widget.Button;  
 import android.widget.Toast;  
 import com.sajithkumara.flexibletemplate.R;  
 public class BMIFragment extends Fragment {  
   Button btnToast;  
   public BMIFragment() {  
     // Required empty public constructor  
   }  
   @Override  
   public View onCreateView(LayoutInflater inflater, ViewGroup container,  
                Bundle savedInstanceState) {  
     // Inflate the layout for this fragment  
     View v = inflater.inflate(R.layout.fragment_bmi, container, false);  
     //adding action bar and setting backward navigation  
     ActionBar actionBar = ((AppCompatActivity) getActivity()).getSupportActionBar();  
     if (getActivity().getClass().getSimpleName().equals("BMIActivity")) {  
       actionBar.setDisplayHomeAsUpEnabled(true);  
     }  
     actionBar.setTitle("BMI Calculator");  
     btnToast = (Button)v.findViewById(R.id.btnToast);  
     btnToast.setOnClickListener(new View.OnClickListener() {  
       @Override  
       public void onClick(View v) {  
         Toast.makeText(getActivity(),"You clicked toast message button!!!",Toast.LENGTH_SHORT).show();  
       }  
     });  
     return v;  
   }  
 }  

8. BMRFragment
 package com.sajithkumara.flexibletemplate.fragment;  
 import android.os.Bundle;  
 import android.support.v4.app.Fragment;  
 import android.support.v7.app.ActionBar;  
 import android.support.v7.app.AppCompatActivity;  
 import android.view.LayoutInflater;  
 import android.view.View;  
 import android.view.ViewGroup;  
 import com.sajithkumara.flexibletemplate.R;  
 public class BMRFragment extends Fragment {  
   public BMRFragment() {  
     // Required empty public constructor  
   }  
   @Override  
   public View onCreateView(LayoutInflater inflater, ViewGroup container,  
                Bundle savedInstanceState) {  
     // Inflate the layout for this fragment  
     View v = inflater.inflate(R.layout.fragment_bmr, container, false);  
     //adding action bar and setting backward navigation  
     ActionBar actionBar = ((AppCompatActivity) getActivity()).getSupportActionBar();  
     if (getActivity().getClass().getSimpleName().equals("BMRActivity")) {  
       actionBar.setDisplayHomeAsUpEnabled(true);  
     }  
     actionBar.setTitle("BMR Calculator");  
     return v;  
   }  
 }  

9. activity_main.xml (in layout folder)
 <?xml version="1.0" encoding="utf-8"?>  
 <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="@dimen/activity_vertical_margin"  
   android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity">  
   <fragment  
     android:layout_width="match_parent"  
     android:layout_height="match_parent"  
     android:name="com.sajithkumara.flexibletemplate.fragment.MenuFragment"  
     android:id="@+id/menuFragInActivity"  
     tools:layout="@layout/fragment_menu" />  
 </RelativeLayout>  

10. activity_main.xml (in layout-sw600dp folder)
 <?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:layout_width="match_parent"  
   android:layout_height="match_parent"  
   tools:context=".MainActivity">  
   <fragment  
     android:layout_width="0dp"  
     android:layout_weight="1"  
     android:layout_height="match_parent"  
     android:name="com.sajithkumara.flexibletemplate.fragment.MenuFragment"  
     android:id="@+id/menu_fragment"  
     tools:layout="@layout/fragment_menu" />  
   <FrameLayout  
     android:id="@+id/frame_container"  
     android:layout_width="0dp"  
     android:layout_weight="2"  
     android:layout_height="match_parent">  
   </FrameLayout>  
 </LinearLayout>  

11. activity_bmi.xml
 <?xml version="1.0" encoding="utf-8"?>  
 <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"  
   tools:context="com.sajithkumara.flexibletemplate.activity.BMIActivity">  
   <fragment  
     android:id="@+id/exersiceFrag"  
     android:name="com.sajithkumara.flexibletemplate.fragment.BMIFragment"  
     android:layout_width="match_parent"  
     android:layout_height="match_parent"  
     tools:layout="@layout/fragment_bmi" />  
   <!-- Note the layout is fragment_bmi-->  
 </RelativeLayout>  

12. activity_bmr.xml
 <?xml version="1.0" encoding="utf-8"?>  
 <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"  
   tools:context="com.sajithkumara.flexibletemplate.activity.BMRActivity">  
   <fragment  
     android:layout_width="match_parent"  
     android:layout_height="match_parent"  
     android:name="com.sajithkumara.flexibletemplate.fragment.BMRFragment"  
     android:id="@+id/exersiceFrag"  
     tools:layout="@layout/fragment_bmr"/>  
   <!-- Note the layout is fragment_bmr-->  
 </RelativeLayout>  

13. fragment_bmi.xml
 <FrameLayout 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:background="#95a5a6"  
   tools:context="com.sajithkumara.flexibletemplate.fragment.BMIFragment">  
   <TextView  
     android:layout_width="match_parent"  
     android:layout_height="match_parent"  
     android:gravity="center"  
     android:text="Here place the content for BMI calculator"  
     android:textColor="#fff"  
     android:textSize="24sp" />  
   <Button  
     android:id="@+id/btnToast"  
     android:layout_width="wrap_content"  
     android:layout_height="wrap_content"  
     android:text="Toast Message"  
     android:layout_gravity="center_horizontal"  
     android:layout_marginTop="60dp"/>  
 </FrameLayout>  

14. fragment_bmr.xml
 <FrameLayout 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"  
   tools:context="com.sajithkumara.flexibletemplate.fragment.BMRFragment"  
   android:background="#7f8c8d">  
   <TextView  
     android:layout_width="match_parent"  
     android:layout_height="match_parent"  
     android:gravity="center"  
     android:text="BMR Calculator goes here.Place the content for BMR calculator"  
     android:textColor="#fff"  
     android:textSize="24sp" />  
 </FrameLayout>  

15. fragment_menu.xml
 <RelativeLayout 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:layout_width="match_parent"  
   android:layout_height="match_parent"  
   tools:context="com.sajithkumara.flexibletemplate.fragment.MenuFragment">  
   <LinearLayout  
     android:layout_width="match_parent"  
     android:layout_height="match_parent"  
     android:orientation="vertical">  
     <android.support.v7.widget.CardView  
       android:id="@+id/cardBMI"  
       android:layout_width="match_parent"  
       android:layout_height="35dp"  
       android:layout_margin="8dp"  
       android:background="@android:color/white"  
       app:cardCornerRadius="4dp">  
       <TextView  
         android:layout_width="wrap_content"  
         android:layout_height="wrap_content"  
         android:layout_gravity="center"  
         android:text="Body Mass Index"  
         android:textSize="30sp" />  
     </android.support.v7.widget.CardView>  
     <android.support.v7.widget.CardView  
       android:id="@+id/cardBMR"  
       android:layout_width="match_parent"  
       android:layout_height="35dp"  
       android:layout_margin="8dp"  
       android:background="@android:color/white"  
       app:cardCornerRadius="4dp">  
       <TextView  
         android:layout_width="wrap_content"  
         android:layout_height="wrap_content"  
         android:layout_gravity="center"  
         android:text="Basal Metabolic Rate"  
         android:textSize="30sp" />  
     </android.support.v7.widget.CardView>  
   </LinearLayout>  
 </RelativeLayout>  

You can download from github from https://github.com/SajithKumara/AndroidFlexibleUI.git
Here is a project in playstore which used this template - BMI Calculator .


That's all. Hope it will help to someone. Happy Coding :-) ......

The Evaluation of Android API levels

In the search of knowledge in android, I had this worrying question on my mind for a while. That is what are the significant highlights in android API levels and how could they impact to my development. So I search through the internet and found valuable information on android site. But it takes some time to read all these changes so I thought I need to summarize those changes so one can get a bird view on this.(Actually I have listed what I think as important. So I didn’t listed all the changes. Go to this link and you will find all the changes.)


Platform version
API Level
New Additions
Android 1.0
1
Begin of a new era
2
      For T-Mobile G1
3
Cupcake
      SlidingDrawer / HorizontalScrollView
      OnScreen soft keyboard
      Speech recognition via RecognizerIntent
      LocationManager - Applications can get location change updates via Intent
      WebView - Touch start/end/move/cancel DOM event support
      Redesigned Sensor Manager APIs
      GLSurfaceView - convenience framework for creating OpenGL  applications
      Broadcast Intent for app update install succeeded - for smoother app  upgrade experience

4
Donut
      New android.view.animation
      Quick Search Box
      New gesture API
      New android.speech.tts
      New SmsManager
      New DisplayMetrics
      New <supports-screens>
      New <uses-feature> 
      Battery usage indicator
      updated OpenCore 2 media engine
5
Eclair
      ANT support
      New APIs for sync adapters
      Bluetooth Support
      Live Wallpapers
8
Froyo
      android:installLocation install app in external storage
      New android:backupAgent
      generalized backup service on Cloud
      New OpenGL ES 2.0
      Dalvik JIT compiler
      Portable hotspot
      V8 JavaScript engine
      New UI modes "car mode" and "night mode" 
      New ScaleGestureDetector
      The layout attribute fill_parent is renamed to match_parent
      android.permission.SET_TIME — allows an application to set the system time.

9
Gingerbread
      SIP based VOIP android.net.sip (Internet Calling)
      NFC (Near Field Communication)
      gyroscope, rotation vector, linear acceleration, gravity, and barometer
      Multiple cameras support by Camera API 
      New DownloadManager system service
      New  system facility StrictMode
      Support for overscroll
      Text selection controls
      Notification text and icon styles(New TextAppearance.StatusBar.EventContent, TextAppearance.StatusBar.EventContent.Title,TextAppearance.StatusBar.Icon, and TextAppearance.StatusBar.Title)
      supports extra-large screen sizes
      New AlarmClock
      New  Criteria class
      New  StorageManager
10
Gingerbread


      NFC upgrades
11
Honeycomb
      Introducing Fragments
      An application will crash if it does not call through to the super implementation of its Activity.onPause() method.
      Action bar instead of tittle bar
      Support for multi core architecture
      Loader: A framework component that facilitates asynchronous loading of data in combination with UI components to dynamically load data without blocking the main thread. See the Loaders developer guide
      Drag and drop functionality
      HTTP live streaming
      support for Media/Picture Transfer Protocol (MTP/PTP) over USB
      System clipboard
      JsonReader and JsonWriter
      Media sync from SD card
12
Honeycomb
      Exposes an API to its built-in RTP (Real-time Transport Protocol) stack
      homescreen widgets resizeable
      New  LruCache class
      USB APIs
      MTP/PTP APIs

13
Honeycomb
      New resource qualifiers(sw320dp, sw720dp, w720dp, h1024dp)
      tabs are typically presented in the UI using the new ActionBar.newTab()
14
Ice Cream Sandwitch
      Social APIs in contacts provider
      New user profile
      Contact usage feedback
      New calendar API to change data which are stored in Calendar Provider
      New Voicemail Provider
      Camera class now includes APIs for detecting faces and controlling focus and metering areas
      Android Beam
      Wi-Fi peer-to-peer (P2P) connections
      Explore-by-touch mode
      New  VpnService
      New  view group GridLayout
      New  view TextureView
      Navigation bar
      Updated V8 JavaScript compiler
15
Ice Cream Sandwitch
      social stream API in contacts provider
16
Jelly bean
      Isolated services android:isolatedProcess="true" 
      New getMyMemoryState(ActivityManager.RunningAppProcessInfo)
      New intent protocol to directly launch the live wallpaper
      App stack navigation using android:parentActivityName
      The AudioEffect class now supports additional audio pre-processing types when capturing audio
      Android Beam™ now supports large payload transfers over Bluetooth
      The new InputManager class allows you to query the set of input devices
      Extends vsync timing across all drawing and animation done by the Android framework
      Expandable Notification
      Lights out and full screen mode
17
Jelly bean
      Daydream 
      Allows users to add app widgets to the lock screen
      Multiple user spaces on shareable devices such as tablets
      Right-to-left (RTL) UIs and reading direction, such as Arabic and Hebrew
      Nested fragments. Nested fragments are only supported when added to a fragment dynamically
      Renderscript Compute which is the first computation platform ported to run directly on a mobile device GPU
      Wireless display solution that complies with the Miracast
      HDR camera scene mode

18
Jelly bean
      Behavioral changes( load an intent in a different user profile)
      Java interfaces and native support for OpenGL ES 3.0.
      Mipmapping for drawables.
      ViewOverlay class
      Optical background layouts for 9 patch images.
      New rotationAnimation field in WindowManager while screen rotation
      New service class, NotificationListenerService 
      The Trace class
      Android sandbox reinforced with SELinux
      Keystore provider and APIs that allow applications to create exclusive-use keys

19
KitKat
      WebView support for new HTML5 features, and support for remote debugging
      New android.print framework
      The Telephony content provider (the "SMS Provider") allows apps to read and write SMS and MMS messages on the device
      Host card emulation (emulates NFC cards for data exchange)
      New NFC reader mode
      Rate the current track from the remote controller
      New android.transition framework facilitate animations between different states of your user interface
      Built-in step sensors
      System bars partially translucent with new themes, Theme.Holo.NoActionBar.TranslucentDecorand Theme.Holo.Light.NoActionBar.TranslucentDecor.
      In WebView, apps targeting earlier versions will have JS URLs evaluated directly and any result of the evaluation will not replace the current page content. Apps targetting KITKAT or later that load a JS URL will have the result of that URL replace the content of the current page
      New API, ActivityManager.isLowRamDevice(), lets you tune your app's behavior to match the device's memory configuration.
      A new storage access framework
      Full screen immersive mode
      A new tool called procstats helps you analyze the memory resources your app uses
Android 4.4W

      For Android Wear(Android watch)
21
Lollipop
      Material Design
      Screen capturing and screen sharing capabilities with android.media.projection APIs
      Java interfaces and native support for OpenGL ES 3.1.
      New android.hardware.camera2 API to facilitate fine-grain photo capture and image processing
      Act as a Bluetooth LE peripheral device
      improvements in battery life.(project volta)
      new JobScheduler API that lets you optimize battery life by defining jobs for the system to run asynchronously at a later time or under specified conditions (such as when the device is charging)
      New screen pinning API
      Render PDF document pages into bitmap images for printing by using the new PdfRenderer class
      New ART runtime to support a mix of ahead-of-time (AOT), just-in-time (JIT), and interpreted code
      platform support for 64-bit architectures(Nexus 9's NVIDIA Tegra K1)
      New  tilt detector and a heart rate sensor
22
Lollipop
      Multiple SIM card support
Android 6.0
23
M
      App linking
      Automatic full data backup and restore for apps.
      Authenticate users by using fingerprint scans on supported devices
      Support for the Hotspot 2.0 Release 1 spec on Nexus 6 and Nexus 9 devices





So This only a small summary on Android API levels. Hope this may help to someone. :-)