There are times when you want to show a collection of related data (e.g., books, movies) in an easy-to-read format. A ListView is a great candidate for this this of task. A ListView will create a scrollable list of items that can be displayed.
In this tutorial, we will create an app that will a ListView of Adobe Apps using a string array:
As in many objects that are created, we will:
In this second version of the Adobe Apps, we will drag-and-drop a ListView component onto the screen and the use an Adapter and an array to populate the ListView.
android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin"
android:background="#5151fe">
<ListView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/ListView"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true" android:divider="#cccccc"
android:dividerHeight="1dp"/>
While you could have done this at the SAME time you created the ListView component above, we wanted to do it in a systematic approach. However, in real production, it would be best to create the components and give them names at the same time they are created.
IMPORTANT CONCEPT TO REMEMBER: It is important to name every component that will be used by Java code with an id. The id is used to store a reference of the element in a Java object as you will see later.
<ListView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/appsListView"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true" android:divider="#cccccc"
android:dividerHeight="1dp"
/>
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string-array name="adobe_apps">
<item>Dreamweaver</item>
<item>Photoshop</item>
<item>Illustrator</item>
<item>InDesign</item>
<item>Animate</item>
<item>After Effects</item>
<item>Premiere Pro</item>
<item>LightRoom</item>
<item>Muse</item>
<item>Fuse</item>
<item>Acrobat Pro</item>
<item>Audition</item>
<item>Audition</item>
</string-array>
</resources>
<ListView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/appsListView"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:divider="#cccccc"
android:dividerHeight="1dp"
android:entries="@array/adobe_apps"/>
public class MainActivity extends AppCompatActivity {
// Create a reference variable
private ListView myListView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Get reference of ListView from XML file
myListView = (ListView) findViewById(R.id.appsListView);
// Get array of apps from string array string resources
String[] apps = getResources().getStringArray(R.array.adobe_apps);
// BETWEEN the data and ListView is the adapter which "adapt" array elements to list items
ArrayAdapter<String> appsAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, apps);
myListView.setAdapter(appsAdapter);
/* // Alternative
myListView.setAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1,
android.R.id.text1, getResources().getStringArray(R.array.adobe_apps)));*/
}
}
Because the ListView is used in most apps, there is a dedicated ListView activity that can be used instead of the default view. It has several advantages:
We will modify the previous example to use the ListView Activity.
android:id="@android:id/list" />
public class MainActivity extends ListActivity {
// Create a reference variable AppCompatActivity
// private ListView myListView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Get reference of ListView from XML file
// myListView = (ListView) findViewById(R.id.appsListView);
// Get array of apps from string array string resources
String[] apps = getResources().getStringArray(R.array.adobe_apps);
// BETWEEN the data and ListView is the adapter which "adapt" array elements to list items
ArrayAdapter<String> appsAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, apps);
// myListView.setAdapter(appsAdapter);
setListAdapter(appsAdapter);
/* // Alternative
myListView.setAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1,
android.R.id.text1, getResources().getStringArray(R.array.adobe_apps)));*/
}
}
<resources>
<string name="app_name">Adobe ListView App</string>
<string name="empty_list">No data is available.</string>
</resources>
android:id="@android:id/list" />
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@android:id/empty" android:gravity="center" android:text="@string/empty_list"/>
</RelativeLayout>
ArrayAdapter<String> appsAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, new String[]{});
ArrayAdapter<String> appsAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, apps);
Similar to an onClickListener that are typically used for buttons and images, a ListView has the setOnItemClickListener.
// Define Adapter to "adapt" Array elements to List itemsNOTE: DO NOT COMPLETE THE CODE
myListView.setAdapter(...);
// Set setOnItemClickListener
myListView.setOnItemClickListener(new OnItemClick
// Set setOnItemClickListener
myListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
}
});
// Set setOnItemClickListener
myListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
String clickedValue = myListView.getItemAtPosition(position).toString();
Toast.makeText(getApplicationContext(), clickedValue, Toast.LENGTH_SHORT).show();
}
});
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
// String clickedValue = myListView.getItemAtPosition(position).toString();
String clickedValue2 = (String) parent.getAdapter().getItem(position);
Toast.makeText(getApplicationContext(), clickedValue2, Toast.LENGTH_SHORT).show();
}
<GridView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/appsListView"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:numColumns="2"/>
public class MainActivity extends AppCompatActivity {3. CHECK POINT: Run the app in an emulator. You should see that it behaves the same as the ListView when items are clicked. However, the items are now displayed in two column per the numColomn attribute.
private GridView myGridView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Get reference of ListView from XML file
myGridView = (GridView) findViewById(R.id.appsListView);
// Define Adapter to "adapt" Array elements to List items
myGridView.setAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1,
android.R.id.text1, getResources().getStringArray(R.array.adobe_apps)));
// Set setOnItemClickListener
myGridView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
// String clickedValue = myListView.getItemAtPosition(position).toString();
String clickedValue2 = (String) parent.getAdapter().getItem(position);
Toast.makeText(getApplicationContext(), clickedValue2, Toast.LENGTH_SHORT).show();
}
});
}
}
As with most View components, you can customize them by creating a separate XML file as a template and then inflate it.
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/custom_listview_selector"
android:orientation="horizontal"
android:padding="5dp">
<LinearLayout
android:id="@+id/thumbnail"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_marginRight="5dp"
android:orientation="vertical">
<ImageView android:id="@+id/list_icon"
android:layout_width="50dp"
android:layout_height="50dp"
android:src="@mipmap/ic_launcher" />
</LinearLayout>
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignTop="@id/thumbnail"
android:layout_toRightOf="@id/thumbnail"
android:text="Adobe Dreamweaver"
android:textColor="#000000"
android:textSize="18sp"
android:textStyle="bold" />
<TextView
android:id="@+id/sub_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/title"
android:layout_toRightOf="@id/thumbnail"
android:text="Web and Mobile"
android:textColor="#ffffff"
android:textSize="14sp"
android:textStyle="bold" />
<ImageView
android:id="@+id/arrow"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@android:drawable/ic_media_play"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"/>
</RelativeLayout>
// ArrayAdapter<String> appsAdapter = new ArrayAdapter<String>(getApplicationContext(), // android.R.layout.simple_list_item_1,android.R.id.text1, appsStringArray);
public class CustomListViewAdapter extends BaseAdapter {
}
public class CustomListViewAdapter extends BaseAdapter {
@Override
public int getCount() {
return 0;
}
@Override
public Object getItem(int position) {
return null;
}
@Override
public long getItemId(int position) {
return 0;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
return null;
}
}
public class CustomListViewAdapter extends BaseAdapter {
private Context mContext;
private ArrayList<HashMap<String,String>> topics;
private static LayoutInflater inflater = null;
public CustomListViewAdapter(Context context, ArrayList<HashMap<String, String>> data){
mContext = context;
topics = data;
inflater = (LayoutInflater)context.getSystemService(context.LAYOUT_INFLATER_SERVICE);
}
@Override
public int getCount() {
return topics.size();
}
@Override
public Object getItem(int position) {
return position;
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View view = convertView;
if (convertView == null){
view = inflater.inflate(R.layout.custom_listview_row, null);
}
TextView title = (TextView) view.findViewById(R.id.title);
TextView sub_title = (TextView) view.findViewById(R.id.sub_title);
ImageView image = (ImageView) view.findViewById(R.id.list_icon);
HashMap<String, String> mTopic = new HashMap<>();
mTopic = topics.get(position);
title.setText(mTopic.get("title"));
sub_title.setText(mTopic.get("sub_title"));
return view;
}
}
public class MainActivity extends AppCompatActivity {
private ListView myListView;
private CustomListViewAdapter customListViewAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
myListView = (ListView) findViewById(R.id.appsListView);
final ArrayList<HashMap<String,String>> titleList = new ArrayList<>();
final String[] tittleArray = getResources().getStringArray(R.array.adobe_apps);
final String[] subtitleArray = getResources().getStringArray(R.array.adobe_apps_subtitle);
for (int i=0; i<subtitleArray.length; i++){
HashMap<String,String> data = new HashMap<>();
data.put("title", tittleArray[i].toString());
data.put("sub_title",subtitleArray[i].toString());
titleList.add(data);
}
// Create new adapter and assign it to the ListView
customListViewAdapter = new CustomListViewAdapter(getApplicationContext(),titleList);
myListView.setAdapter(customListViewAdapter);
// Add Event Listener
myListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
String myItem = tittleArray[position];
Toast.makeText(getApplicationContext(), myItem, Toast.LENGTH_SHORT).show();
}
});
}
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View view = convertView;
if (convertView == null){
view = inflater.inflate(R.layout.custom_listview_row, null);
}
TextView title = (TextView) view.findViewById(R.id.title);
TextView sub_title = (TextView) view.findViewById(R.id.sub_title);
ImageView image = (ImageView) view.findViewById(R.id.list_icon);
HashMap<String, String> mTopic = new HashMap<>();
mTopic = topics.get(position);
title.setText(mTopic.get("title"));
sub_title.setText(mTopic.get("sub_title"));
if(position==0) {
image.setImageResource(R.drawable.dreamweaver);
}
if(position==1) {
image.setImageResource(R.drawable.photoshop);
}
if(position==2) {
image.setImageResource(R.drawable.illustrator);
}
if(position==3) {
image.setImageResource(R.drawable.indesign);
}
if(position==4) {
image.setImageResource(R.drawable.animate);
}
if(position==5) {
image.setImageResource(R.drawable.after_effects);
}
if(position==6) {
image.setImageResource(R.drawable.premiere_pro);
}
if(position==7) {
image.setImageResource(R.drawable.lightroom);
}
if(position==8) {
image.setImageResource(R.drawable.muse);
}
if(position==9) {
image.setImageResource(R.drawable.fuse);
}
if(position==10) {
image.setImageResource(R.drawable.acrobat_pro_dc);
}
if(position==11) {
image.setImageResource(R.drawable.audition);
}
if(position==12) {
image.setImageResource(R.drawable.phonegap);
}
return view;
}
}
It is important to note how these components will be knitted together. The listview state xml files are nested inside of the selector xml file which is inside of the row xml which is pulled inside (inflated) in the java file (e.g., CustomListViewAdapter.java):
Not only do you have to create a custom layout, you have to create a custom adapter as well. In order to show the custom ListView, we will have to inflate it with Java.
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
<gradient
android:angle="270" android:startColor="#f1f1f2"
android:centerColor="#7878fa"
android:endColor="#cfcfcf"> </gradient>
</shape>
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
<gradient
android:angle="270"
android:centerColor="#5454f5"
android:endColor="#cfcfcf"
android:startColor="#f1f1f2"> </gradient>
</shape>
<?xml version="1.0" encoding="utf-8"?>NOTE: The first <item> element is used if the list item has not been selected and have not been pressed--show the default background (e.g., custom_listview. The second <item> element is used if the list item is presses but not selected. The third <item> element is used if the list item is pressed and selected (user has press and then removed finger from selection).
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/custom_listview_normal"
android:state_pressed="false"
android:state_selected="false">
</item>
<item android:drawable="@drawable/custom_listview_hover"
android:state_pressed="true">
</item>
<item android:drawable="@drawable/custom_listview_hover"
android:state_pressed="true"
android:state_selected="true">
</item>
</selector>
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="5dp"
android:background="@drawable/custom_listview_selector">