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 :-).
No comments:
Post a Comment