ListView, is, without a doubt, one of the most important Views in Android SDK. A listView is usually populated using an array adapter, a class that takes an array of some kind and populates the ListView with it. This makes populating the ListView with, lets say, a Strings array very easy. But most of the time, you want to populate your listView with a custom layout, that usually contains multiples of strings, buttons and the likes. In order to do this, you'll need to create a class that extends an ArrayAdapter and handles populating a ListView with the provided layout. Alright, lets get to it!
First, fire up the Android Studio (or Eclipse with the android plugin installed, whichever your prefer) and create a new project. I named my project "ListViewExample". Minimum required SDK - API 16: Android 4.1 (but anything lower than that should work too). Leave everything else as is.
Now you should have an activity called "MainActivity" and two layouts - "activity_main.xml" and "fragment_main.xml" (the latter only if you're using the android studio). This step is optional and only applies if you're using the Android Studio - delete fragment_main.xml from your layout folder. Then go to your activity and delete the following chunks of code:
1
2
3
4
5
| if (savedInstanceState == null) {
getFragmentManager().beginTransaction()
.add(R.id.container, new PlaceholderFragment())
.commit();
}
|
and
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
| @Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
/**
* A placeholder fragment containing a simple view.
*/
public static class PlaceholderFragment extends Fragment {
public PlaceholderFragment() {
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_main, container, false);
: return rootView;
}
}
|
W're not going to use fragments or the menu button in this project.
Next go to activity_main.xml and replace everything in there with the following code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.tysovsky.listviewexample.MainActivity"
android:orientation="vertical">
<ListView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/listView"
android:layout_gravity="center_horizontal" />
</LinearLayout>
|
Now you should see a ListView that fills the entire screen of the app.
The next step is creating the layout of every individual entry in the list. In the layout folder create a new layout called content_layout.xml. Here's the simple xml I used:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
| <?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/content_image"
android:src="@drawable/ic_launcher"
android:layout_alignParentTop="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceSmall"
android:text="Title"
android:id="@+id/content_title"
android:layout_alignParentTop="true"
android:layout_toRightOf="@+id/content_image" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceSmall"
android:text="Subtitle"
android:id="@+id/content_subttitle"
android:layout_below="@+id/content_title"
android:layout_toRightOf="@+id/content_image" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceSmall"
android:text="Description"
android:id="@+id/content_description"
android:layout_below="@+id/content_subttitle"
android:layout_toRightOf="@+id/content_image" />
</RelativeLayout>
|
Of course you can modify it to fit any of your needs.
If you were to compile & run the app right now, you will only see a blank page. The listView we've created earlier hasn't been populated yet. To populate the list, we're gonna use an Object Oriented approach and create a class that houses all of our values. Create a new java class called content. It's gonna take 4 parameters as its constructor: Bitmap "image" and three strings - "title", "subtitle" and "description". Create all the setters and getters. Here's my final code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
| package com.tysovsky.listviewexample;
import android.graphics.Bitmap;
/**
* Created by tysovsky on 4/7/14.
*/
public class content {
Bitmap image;
String title;
String subtitle;
String description;
public content(Bitmap bmp, String ttl, String subttl, String desc){
this.image = bmp;
this.title = ttl;
this.subtitle = subttl;
this.description = desc;
}
public Bitmap getImage(){
return this.image;
}
public String getTitle(){
return this.title;
}
public String getSubtitle(){
return this.subtitle;
}
public String getDescription(){
return this.description;
}
}
|
Go back to our MainActivity. Let's create our array of content now. Above the onCreate() method add the following line:
1
| List ContentList = new ArrayList();pre>
|
Lets create some more variables. Below the previous line add these two lines:
1
2
| Bitmap image;
ListView list;
|
The first line is a bitmap that we'll pas as a constructor of our content class later. Of course you can pass any image, whether it's stored locally or was downloaded from the internet. The second line is a listView that we will later reference to the listView from the activity_main.xml. Inside the onCreate() function insert the following:
1
2
3
4
5
6
7
8
9
10
| list = (ListView) findViewById(R.id.listView);
//Create a bitmap from ic_laucher that is located at /drawable folders by default, this will be displayed as an image of every item on the list
image = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher);
ContentList.add(new content(image, "Title1", "Subtitle1", "Some description here1"));
ContentList.add(new content(image, "Title2", "Subtitle2", "Some description here2"));
ContentList.add(new content(image, "Title3", "Subtitle3", "Some description here3"));
ContentList.add(new content(image, "Title4", "Subtitle4", "Some description here4"));
ContentList.add(new content(image, "Title5", "Subtitle5", "Some description here5"));
|
Now let's create an ArrayAdapter and populate the list view with it.
1
2
| ArrayAdapter<content> adapter = new CustomArrayAdapter();
list.setAdapter(adapter);
|
And here comes the exiting part, we're finally gonna write our CustomArrayAdapter class! Create a new class inside your MainActivity:
1
2
3
4
5
6
7
| private class CustomArrayAdapter extends ArrayAdapter<content>{
//Class constructor
public CustomArrayAdapter(){
//Takes context(MainActivity.this), layout to populate with(R.layout.content_layout) and an array to populate with(ContentList)
super(MainActivity.this, R.layout.content_layout, ContentList);
}
}
|
As you can see, an ArrayAdapter requires 3 constructors. First one is a context (MainActivity.this), the second one is a layout of our content (R.layout.content_layout), and finally a list that we're gonna use to populate the list view(ContentList).
Now lets create a function inside the CustomArrayAdapter class that will return the view that we need:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
| @Override
public View getView(int position, View convertView, ViewGroup parent){
View contentView = convertView;
if(contentView == null){
//If view was not returned, create it
contentView = getLayoutInflater().inflate(R.layout.content_layout, parent, false);
}
//Get the current content we're working with
content currentContent = ContentList.get(position);
//Reference the ImageView from the xml
ImageView image = (ImageView) contentView.findViewById(R.id.content_image);
//Set it to our bmp
image.setImageBitmap(currentContent.getImage());
//Reference the rest of the variables and set properties of the current content as their value
TextView titleTextView = (TextView) contentView.findViewById(R.id.content_title);
titleTextView.setText(currentContent.getTitle());
TextView subTitleText = (TextView) contentView.findViewById(R.id.content_subttitle);
subTitleText.setText(currentContent.getSubtitle());
TextView descriptionTextView = (TextView) contentView.findViewById(R.id.content_description);
descriptionTextView.setText(currentContent.getDescription());
return contentView;
}
|
ListView has its own OnClickListener called OnItemClickListener. We will use it to listen for clicks on out list. Write the following code inside of your OnCreate() function:
1
2
3
4
5
6
7
| //Listen for clicks on the list items
list.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
Toast.makeText(MainActivity.this, "You've pressed item #" + i, Toast.LENGTH_SHORT).show();
}
});
|
And last but not least, we might want to listen for clicks of a listView child views. I am gonna listen for clicks on every imageView inside of every item inside the ListView. To do this put the following code inside your getView() function of the CustomArrayAdapter class:
1
2
3
4
5
6
7
8
9
| //Add a tag to the ImageView so we could differentiate between them later on
image.setTag(new Integer(position));
//Listen for clicks
image.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Toast.makeText(MainActivity.this, "You've clicked an image in item#" + view.getTag().toString(),Toast.LENGTH_LONG ).show();
}
});
|
And here's the entire code of my MainActivity for those whoo need it
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
| com.tysovsky.listviewexample;
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
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;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends Activity {
List<content> ContentList = new ArrayList<content>();
Bitmap image;
ListView list;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
list = (ListView) findViewById(R.id.listView);
ArrayAdapter<content> adapter = new CustomArrayAdapter();
list.setAdapter(adapter);
//Listen for clicks on the list items
list.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
Toast.makeText(MainActivity.this, "You've pressed item #" + i, Toast.LENGTH_SHORT).show();
}
});
//Create a bitmap from ic_laucher that is located at /drawable folders by default, this will be displayed as an image of every item on the list
image = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher);
ContentList.add(new content(image, "Title1", "Subtitle1", "Some description here1"));
ContentList.add(new content(image, "Title2", "Subtitle2", "Some description here2"));
ContentList.add(new content(image, "Title3", "Subtitle3", "Some description here3"));
ContentList.add(new content(image, "Title4", "Subtitle4", "Some description here4"));
ContentList.add(new content(image, "Title5", "Subtitle5", "Some description here5"));
}
private class CustomArrayAdapter extends ArrayAdapter<content>{
//Class constructor
public CustomArrayAdapter(){
//Takes context(MainActivity.this), layout to populate with(R.layout.content_layout) and an array to populate with(ContentList)
super(MainActivity.this, R.layout.content_layout, ContentList);
}
@Override
public View getView(int position, View convertView, ViewGroup parent){
View contentView = convertView;
if(contentView == null){
//If view was not returned, create it
contentView = getLayoutInflater().inflate(R.layout.content_layout, parent, false);
}
//Get the current content we're working with
content currentContent = ContentList.get(position);
//Reference the ImageView from the xml
ImageView image = (ImageView) contentView.findViewById(R.id.content_image);
//Set it to our bmp
image.setImageBitmap(currentContent.getImage());
//Add a tag to the ImageView so we could differentiate between them later on
image.setTag(new Integer(position));
//Listen for clicks
image.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Toast.makeText(MainActivity.this, "You've clicked an image in item#" + view.getTag().toString(),Toast.LENGTH_LONG ).show();
}
});
//Reference the rest of the variables and set properties of the current content as their value
TextView titleTextView = (TextView) contentView.findViewById(R.id.content_title);
titleTextView.setText(currentContent.getTitle());
TextView subTitleText = (TextView) contentView.findViewById(R.id.content_subttitle);
subTitleText.setText(currentContent.getSubtitle());
TextView descriptionTextView = (TextView) contentView.findViewById(R.id.content_description);
descriptionTextView.setText(currentContent.getDescription());
return contentView;
}
}
}
|