Statement of the problem
At the end
of our series of articles the application must perform the following functions:
- Add / Delete / Select / Move scheduled case.
- Delete all scheduled cases.
- Select / deselect all scheduled cases.
- Save / Load cases scheduled on the card.
- Animation delete scheduled case.
- Animation flying cells.
Some theory
ListView is
a view group that displays a list of scrollable items. The list items are automatically
inserted to the list using an Adapter that pulls content from a source such as
an array or database query and converts each item result into a view that's
placed into the list.
The basic
algorithm development and customization ListView as follows:
1. First,
you must develop a data structure which will represent one item. It can be a
simple type or object (such as String or Integer), or developed class, as in
our case (ToDoItem).
2. In the
next step, it is necessary to develop all xml layout:
- layout for a list item (listitem.xml);
- layout for the Activity, which will be our ListView (in this case, main.xml);
- layout for the Header and Footer, if they (list_header.xml, list_footer.xml).
3. Next,
you need to develop adapter (CustomListAdapter.java) for the formation of our
ListView to the specified data source. In this case, the adapter will be extended
from the BaseAdapter and the data source will be used ArrayList.
4. The last
step - the union of all developed in our Activity. Algorithm for object
initialization next:
- load or initialize list (ArrayList) of the data;
- initialize ListView (using the findViewById());
- create adapter with the downloaded data;
- if there is a Header or Footer, that add them to the ListView (using methods addHeaderView() or addFooterView(), respectively);
- set an adapter for ListView (using the setAdapter()).
For correct
work of ListView, you should follow these rules:
- Header and Footer to be added before calling setAdapter ();
- if you added Header or Footer, that note that the parameter position in the method onItemClick () for ListView will:
- For one more, if you have added Header;
- One or two larger than the data source (method
size() for the ArrayList, if added 1 (or 2) View, respectively).
Practice
The images
used in the project can be downloaded here.
Before the development layouts and classes, we must add the string resources.
Before the development layouts and classes, we must add the string resources.
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">CustomListView</string>
<string name="txt_header_title">ToDo List</string>
<string name="txt_footer_title">Done: %s</string>
</resources>
1. Development
of the project will begin with the development of the class ToDoItem. Objects
of this class will be the data source for the items in the list.
Set the
following attributes of the class:
name - the name of the case;
check - done / not done by the current case;
index - the position in the list when adding
(to be used to sort the list).
The class
will be serialized in order to be able to save and load.
Source
code:
import java.io.Serializable; /** * An item to the list */ public class ToDoItem implements Serializable{ private static final long serialVersionUID = 2008719019880549886L; private String name; // Name of the case private boolean check; // Done / Not done private int index; // Position in the list when adding public String getName() { return name; } public void setName(String name) { this.name = name; } public boolean isCheck() { return check; } public void setCheck(boolean check) { this.check = check; } public int getIndex() { return index; } public void setIndex(int index) { this.index = index; } public ToDoItem(String name, int index) { setName(name); setIndex(index); setCheck(false); } public ToDoItem() { setName(""); setCheck(false); } }
2. Development
of layouts
To begin
with develop xml-layout to represent a single element in the list.On the layout
of a few comments:
- android:focusable="false" used for the CheckBox, so that you can handle the press a list item (method onItemClick () for ListView).
Source
code (listitem.xml):
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content" >
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="3dp"
android:layout_marginRight="3dp"
android:background="@drawable/item"
android:orientation="horizontal" >
<TextView
android:id="@+id/listitem_name"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="left|center_vertical"
android:layout_weight="1"
android:singleLine="true"
android:ellipsize="end"
android:layout_marginLeft="10dp"/>
<CheckBox
android:id="@+id/listitem_check"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="5dp"
android:button="@drawable/custom_checkbox"
android:focusable="false"/>
</LinearLayout>
</LinearLayout>
Now,
designed layouts for the Header and Footer. Header will be used for the header
and Footer will display the number completed cases.
Source code
(list_header.xml):
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content" >
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="3dp"
android:layout_marginRight="3dp"
android:background="@drawable/header" >
<TextView
android:id="@+id/header_title"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:shadowColor="@android:color/black"
android:shadowDy="1"
android:shadowRadius="2"
android:textColor="@android:color/white"
android:textSize="20sp"
android:textStyle="bold"
/>
</LinearLayout>
</LinearLayout>
Source code
(list_footer.xml):
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
>
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="3dp"
android:layout_marginRight="3dp"
android:background="@drawable/footer" >
<TextView
android:id="@+id/footer_title"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginRight="2dp"
android:gravity="right|center_vertical"
android:textStyle="bold" />
</LinearLayout>
</LinearLayout>
Now develop main layout that will ListView. Standard ListView by clicking on the item highlighted in black. To avoid this, it is necessary to add a property: android:cacheColorHint="@android:color/transparent".
Source code (main.xml):
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/white" >
<ListView
android:id="@+id/listview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:cacheColorHint="@android:color/transparent" />
</LinearLayout>
3. Development
of adapter
In this
example, we will use the simplest adapter extended from BaseAdapter. The source
of data will be ArraList<ToDoItem>.
Source code
(CustomListAdapter):
import java.util.ArrayList; import java.util.List; import android.content.Context; import android.graphics.Paint; import android.os.Message; import android.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.CheckBox; import android.widget.TextView; /** * Adapter for ListView */ public class CustomListAdapter extends BaseAdapter { private LayoutInflater inflater_; private List<ToDoItem> list = new ArrayList<ToDoItem>(); private Context context; public CustomListAdapter(List<ToDoItem> list, Context context) { this.list = list; inflater_ = LayoutInflater.from(context); this.context = context; } @Override public int getCount() { return this.list.size(); } @Override public Object getItem(int position) { return this.list.get(position); } @Override public long getItemId(int position) { return position; } public void setList(List<ToDoItem> list) { this.list = list; } @Override public View getView(final int position, View view, ViewGroup parent) { View v = view; if (view == null) v = inflater_.inflate(R.layout.listitem, parent, false); v.setVisibility(View.VISIBLE); // Getting an item from list final ToDoItem item = list.get(position); // Setting the name of the case TextView listitem_name = (TextView)v.findViewById(R.id.listitem_name); listitem_name.setText(item.getIndex() + ". " + item.getName()); // Strikethrough completed cases if (item.isCheck()) listitem_name.setPaintFlags(Paint.ANTI_ALIAS_FLAG | Paint.STRIKE_THRU_TEXT_FLAG); else listitem_name.setPaintFlags(Paint.ANTI_ALIAS_FLAG); // Setting checkbox and events on click CheckBox listitem_check = (CheckBox)v.findViewById(R.id.listitem_check); listitem_check.setChecked(item.isCheck()); listitem_check.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { Message msg = new Message(); msg.arg1 = position; msg.what = MainActivity.MSG_CHANGE_ITEM; ((MainActivity)context).getHandler().sendMessage(msg); } }); return v; } }
4. Development of a specialized class Utils.
The class I
am developing an alone, because it uses methods (static), which we can use in
the future in other classes.
Method ()
is used to remove the default selection for the item in the list and divider between
the elements.
Method
sorting() is used to sort the list. Ability to sort by done / not done cases
(type = 0) and the index (type = 1).
Source code
(Utils):
import java.util.Collections; import java.util.Comparator; import java.util.List; import android.content.Context; import android.graphics.drawable.ColorDrawable; import android.widget.ListView; /** * Specialized class for various static methods */ public class Utils { /** * Set various parameters for the list. In particular, it removes the divider in a list. */ public static void setList(ListView list, Context context) { list.setSelector(android.R.color.transparent); ColorDrawable sage = new ColorDrawable(context.getResources().getColor( android.R.color.transparent)); list.setDivider(sage); list.setDividerHeight(0); } /** * Sort the list * @param type 0 - sort by checkmark * @param type 1 - sort by index */ public static void sorting(List<ToDoItem> list, final int type) { Collections.sort(list, new Comparator<ToDoItem>() { @Override public int compare(ToDoItem item1, ToDoItem item2) { int compare = 0; switch (type) { case 0: Boolean bool_value1 = Boolean.valueOf(item1.isCheck()); Boolean bool_value2 = Boolean.valueOf(item2.isCheck()); compare = bool_value1.compareTo(bool_value2); if (compare == 0) compare = (item1.getIndex() > item2.getIndex()) ? 1 : -1; break; case 1: Integer int_value1 = Integer.valueOf(item1.getIndex()); Integer int_value2 = Integer.valueOf(item2.getIndex()); compare = int_value1.compareTo(int_value2); break; } return compare; } }); } }
5. Main
class MainActivity.
The
attributes of our class will be the following:
// Messages for Handler public static final int MSG_UPDATE_ADAPTER = 0; public static final int MSG_CHANGE_ITEM = 1; // Objects for working with to-do list private List<ToDoItem> list = new ArrayList<ToDoItem>(); private CustomListAdapter adapter; private ListView listview; // Footer for ListView private View footer;
Messages
are used to Handler. They make it possible to update the list is not from the
main thread.
Footer
shall be made to the attributes that you can update it at runtime.
Now do all
the necessary initialization in the method onCreate().
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); initList(); // Initialization list listview = (ListView)findViewById(R.id.listview); /* Adding header and footer to the ListViewNote: 1. These Views must be added before calling setAdapter 2. These Views are taken into account in obtaining the position of an item in the list (for example in the way OnItemClickListener (for ListView) and getView (for Adapter)) */ View header = LayoutInflater.from(this).inflate(R.layout.list_header, null, false); ((TextView)header.findViewById(R.id.header_title)).setText(R.string.txt_header_title); listview.addHeaderView(header); footer = LayoutInflater.from(this).inflate(R.layout.list_footer, null, false); listview.addFooterView(footer); adapter = new CustomListAdapter(list, this); setCountPurchaseProduct(); // Calculation done cases listview.setAdapter(adapter); listview.setOnItemClickListener(new OnItemClickListener() { @Override public void onItemClick(AdapterView parent, View view, int position, long id) { // If there is a tap on the Header or Footer, do not do anything if (position == 0 || position == list.size() + 1) return; Message msg = new Message(); msg.arg1 = position - 1; msg.what = MSG_CHANGE_ITEM; handler.sendMessage(msg); } }); // ListView set up and sort our list of the check box Utils.setList(listview, this); Utils.sorting(list, 0); }
Add methods
to initialize the list and calculation done cases:
/** * Initialization list */ private void initList() { for (int i = 1; i <= 10; i++) list.add(new ToDoItem("ToDo Item " + i, i)); } /** * Calculate amount cases done */ private void setCountPurchaseProduct() { int count = 0; Iterator<ToDoItem> it = list.iterator(); while (it.hasNext()) { ToDoItem item = it.next(); if (item.isCheck()) count++; } ((TextView)footer.findViewById(R.id.footer_title)).setText(String.format(getString(R.string.txt_footer_title), count)); }
The final
step will be the development Handler:
private Handler handler = new Handler() { public void handleMessage(Message msg) { switch (msg.what) { case MSG_UPDATE_ADAPTER: // ListView updating adapter.notifyDataSetChanged(); setCountPurchaseProduct(); break; case MSG_CHANGE_ITEM: // Do / not do case ToDoItem item = list.get(msg.arg1); item.setCheck(!item.isCheck()); Utils.sorting(list, 0); adapter.notifyDataSetChanged(); setCountPurchaseProduct(); break; } } }; public Handler getHandler() { return handler; }
Excellent post.
ReplyDeleteandroid application development