The first basic knowledge
However, we can have a list that is created by rows of icons, symbols and texts, check boxes and text, or anything that we want. It should be noted here is to provide enough data for the adapter and adapter help create a set of View objects fuller for each line.
For example, suppose we wanted to have a ListView with items created by a symbol, followed by a text line. We can create a layout for a line that looks like this:
This layout uses a LinearLayout to set the contents of a row, with a logo on the left and the content (with a custom font) on the right.
However, by default, Android does not know we want to use this layout to your ListView. To create a link between them, we need to give your Adapter ID of layout custom resources mentioned above, so put this code in your Activity:
package devexchanges.info.basiclistview; import android.annotation.SuppressLint; import android.app.ListActivity; import android.os.Bundle; import android.view.View; import android.widget.ArrayAdapter; import android.widget.ListView; import android.widget.TextView; public class ListViewDemo extends ListActivity { private TextView selection; private static final String[] items = { "lorem", "ipsum", "dolor", "sit", "amet", "consectetuer", "adipiscing", "elit", "morbi", "vel", "ligula", "vitae", "arcu", "aliquet", "mollis", "etiam", "vel", "erat", "placerat", "ante", "porttitor", "sodales", "pellentesque", "augue", "purus" }; @Override public void onCreate(Bundle icicle) { super.onCreate(icicle); setContentView(R.layout.activity_list_demo); setListAdapter(new ArrayAdapter<>(this, R.layout.row, R.id.label, items)); selection = (TextView) findViewById(R.id.selection); } @SuppressLint("SetTextI18n") public void onListItemClick(ListView parent, View v, int position, long id) { selection.setText(items[position] + " Clicked!"); } }Note: Reference to a layout (row.xml), we use R.layout as a prefix before the name of the XML file layout (R.layout.row). Code for this file in this sample project:
Output when running this project:
Dynamic ListView
package devexchanges.info.basiclistview; import android.app.ListActivity; import android.os.Bundle; import android.view.View; import android.view.ViewGroup; import android.widget.ArrayAdapter; import android.widget.ImageView; import android.widget.ListView; import android.widget.TextView; public class DynamicDemo extends ListActivity { TextView selection; private static final String[] items = {"lorem", "ipsum", "dolor", "sit", "amet", "consectetuer", "adipiscing", "elit", "morbi", "vel", "ligula", "vitae", "arcu", "aliquet", "mollis", "etiam", "vel", "erat", "placerat", "ante", "porttitor", "sodales", "pellentesque", "augue", "purus"}; @Override public void onCreate(Bundle icicle) { super.onCreate(icicle); setContentView(R.layout.activity_list_demo); setListAdapter(new IconicAdapter()); selection = (TextView) findViewById(R.id.selection); } public void onListItemClick(ListView parent, View v, int position, long id) { selection.setText(items[position]); } private class IconicAdapter extends ArrayAdapter<String> { public IconicAdapter() { super(DynamicDemo.this, R.layout.row, R.id.label, items); } public View getView(int position, View convertView, ViewGroup parent) { View row = super.getView(position, convertView, parent); ImageView icon = (ImageView) row.findViewById(R.id.icon); if (items[position].length() > 4) { icon.setImageResource(R.mipmap.ic_delete); } else { icon.setImageResource(R.mipmap.ic_check); } return (row); } } }Our IconicAdapter - a nested class in the Activity within 2 methods. Firstly, it has a constructor with a parameter passed to ArrayAdapter similar to what we did in the previous example. Secondly, it has methods getView() is executed - do 2 works:
- It uses the method overrided from getView() super class, returns to us an object View of row - fitted by ArrayAdapter. Specifically, our words are put into the TextView by ArrayAdapter that do it in a normal way.
- It finds the ImageView object and apply to it the logic made to know which symbols should be used, reference to one of the two resources are R.mipmap.ic_check and R.mipmap.ic_delete) .
With above row.xml file, this is output:
Recycling Views
As you see in the code above, getView() has an important View parameter called convertView. Sometimes, convertView may be null. In such cases, we need to create an object for each of the first View (through convert XML structure file to View) - like we do in the previous example. However, if convertView not null, then obviously it is one of the objects View was we created earlier! It would appear primarily when users scroll ListView. When the new line appears, Android will recycle lines have been rolled over to prevent from the create this line. See this example:
public class ListViewDemo extends ListActivity { private TextView selection; private static final String[] items = {"lorem", "ipsum", "dolor", "sit", "amet", "consectetuer", "adipiscing", "elit", "morbi", "vel", "ligula", "vitae", "arcu", "aliquet", "mollis", "etiam", "vel", "erat", "placerat", "ante", "porttitor", "sodales", "pellentesque", "augue", "purus"}; @Override public void onCreate(Bundle icicle) { super.onCreate(icicle); setContentView(R.layout.main); setListAdapter(new IconicAdapter()); selection = (TextView) findViewById(R.id.selection); } public void onListItemClick(ListView parent, View v, int position, long id) { selection.setText(items[position]); } class IconicAdapter extends ArrayAdapter<String> { IconicAdapter() { super(ListViewDemo.this, R.layout.row, items); } public View getView(int position, View convertView, ViewGroup parent) { View row = convertView; if (row == null) { LayoutInflater inflater = getLayoutInflater(); row = inflater.inflate(R.layout.row, parent, false); } TextView label = (TextView) row.findViewById(R.id.label); label.setText(items[position]); ImageView icon = (ImageView) row.findViewById(R.id.icon); if (items[position].length() > 4) { icon.setImageResource(R.drawable.delete); } else { icon.setImageResource(R.drawable.ok); } return row; } } }Here we check to see if the convertView is null and, if so we then inflate our row—but if it is not null, we just reuse it. The work to fill in the contents (icon image, text) is the same in either case. The advantage is that if the convertView is not null, we avoid the potentially expensive inflation step.
Use ViewHolder Pattern
And you can see most of my post related ListView use this feature, a ViewHolder class can be very simple with a constructor which declared items through call findViewbyId() in it:
class ViewHolder { private class ViewHolder { private ImageView icon; private TextView text; public ViewHolder(View v) { icon = (ImageView)v.findViewById(R.id.icon); text = (TextView)v.findViewById(R.id.label); } }By it, in getView(), when convertView is null, we inflate the XML file and convert to View, after that, linked it with the ViewHolder instance through call setTag(). Moreover, if convertView is not null, recycling it with the exist ViewHolder object with getTag():
ViewHolder holder; //initialize a null ViewHolder object LayoutInflater inflater = (LayoutInflater) getSystemService(Activity.LAYOUT_INFLATER_SERVICE); // If holder not exist then locate all view from UI file. if (convertView == null) { // inflate UI from XML file convertView = inflater.inflate(R.layout.row, parent, false); // get all UI view holder = new ViewHolder(convertView); // set tag for holder convertView.setTag(holder); } else { // if holder created, get tag from view holder = (ViewHolder) convertView.getTag(); }With this design pattern, our ListView will be scrolled very smoothly, it's performance is optimized. You can realize it effect when the ListView data is much complicated. And this is full code for this Activity (extends from AppCompatActivity, not ListActivity like the examples above):
package info.devexchanges.listviewsample; import android.app.Activity; import android.app.ListActivity; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.ImageView; import android.widget.ListView; import android.widget.TextView; public class MainActivity extends AppCompatActivity { private TextView selection; private ListView listView; private static final String[] items = {"lorem", "ipsum", "dolor", "sit", "amet", "consectetuer", "adipiscing", "elit", "morbi", "vel", "ligula", "vitae", "arcu", "aliquet", "mollis", "etiam", "vel", "erat", "placerat", "ante", "porttitor", "sodales", "pellentesque", "augue", "purus"}; @Override public void onCreate(Bundle icicle) { super.onCreate(icicle); setContentView(R.layout.activity_main); selection = (TextView) findViewById(R.id.selection); listView = (ListView) findViewById(R.id.list); //set on click listener for each List Item listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> adapterView, View view, int position, long l) { selection.setText(items[position]); } }); //set listview adapter ArrayAdapter<String> adapter = new IconicAdapter(); listView.setAdapter(adapter); } private class IconicAdapter extends ArrayAdapter<String> { public IconicAdapter() { super(MainActivity.this, R.layout.row, items); } public View getView(int position, View convertView, ViewGroup parent) { ViewHolder holder; //initialize a null ViewHolder object LayoutInflater inflater = (LayoutInflater) getSystemService(Activity.LAYOUT_INFLATER_SERVICE); // If holder not exist then locate all view from UI file. if (convertView == null) { // inflate UI from XML file convertView = inflater.inflate(R.layout.row, parent, false); // get all UI view holder = new ViewHolder(convertView); // set tag for holder convertView.setTag(holder); } else { // if holder created, get tag from view holder = (ViewHolder) convertView.getTag(); } if (items[position].length() > 4) { holder.icon.setImageResource(R.mipmap.ic_delete); } else { holder.icon.setImageResource(R.mipmap.ic_check); } holder.text.setText(items[position]); return convertView; } } private class ViewHolder { private ImageView icon; private TextView text; public ViewHolder(View v) { icon = (ImageView)v.findViewById(R.id.icon); text = (TextView)v.findViewById(R.id.label); } } }