In this tutorial, we will update the layout of our
ListView rows, so they show both the name and address of the restaurant, plus an icon indicating the type. Along the way, we will need to create our own custom
ListAdapter to handle our row views and a RestaurantHolder to populate a row from a
restaurant.
Create a Stub Custom Adapter
Create a stub implementation of a
RestaurantAdapter that will be where we put our logic for creating our own custom rows. That can look like this, implemented as an inner class of
LunchList:
class RestaurantAdapter extends ArrayAdapter<Restaurant> {
RestaurantAdapter() {
super(LunchList.this,
android.R.layout.simple_list_item_1,model);
}
}
The above code use hard-wire in the
android.R.layout.simple_list_item_1 layout for now, and we get our Activity and model from
LunchList itself.
Next we need to change our adapter data member to be a
RestaurantAdapter, both where it is declared and where it is instantiated in
onCreate(). Make these changes, then rebuild and reinstall the application and confirm it works as it did at the end of the previous tutorial.
Design The List Row
Design a row that incorporates all three of our model elements:
name,
address, and
type.
For the type, we will use three icons, one for each specific type (
sit down,
take-out,
delivery).
Note : You can use whatever icons you wish
The Icon will be located on :
res/drawable/
NOTE: If your project has no res/drawable/ directory, but does have res/drawable-ldpi/ and others with similar suffixes, rename res/drawablemdpi/ to res/drawable/ directory for use in this project, and delete the other res/drawable-* directories.
Example Custom List
To achieve this look, we use a nested pair of
LinearLayout containers. Use
the following XML as the basis for
LunchList/res/layout/row.xml:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="4dip"
>
<ImageView android:id="@+id/icon"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:layout_alignParentTop="true"
android:layout_alignParentBottom="true"
android:layout_marginRight="4dip"
/>
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
>
<TextView android:id="@+id/title"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center_vertical"
android:textStyle="bold"
android:singleLine="true"
android:ellipsize="end"
/>
<TextView android:id="@+id/address"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center_vertical"
android:singleLine="true"
android:ellipsize="end"
/>
</LinearLayout>
</LinearLayout>
Some of the unusual attributes applied in this layout include:
- android:padding, which arranges for some whitespace to be put outside the actual widget contents but still be considered part of the widget (or container) itself when calculating its size
- android:textStyle, where we can indicate that some text is in bold or italics
- android:singleLine, which, if true, indicates that text should not word-wrap if it extends past one line
- android:ellipsize, which indicates where text should be truncated and ellipsized if it is too long for the available space
Override getView()
Next, we need to use this layout ourselves in our
RestaurantAdapter. To do this, we need to override
getView() and inflate the layout as needed for rows.
Modify
RestaurantAdapter to look like the following:
class RestaurantAdapter extends ArrayAdapter<Restaurant> {
RestaurantAdapter() {
super(LunchList.this,
android.R.layout.simple_list_item_1,model);
}
public View getView(int position, View convertView,
ViewGroup parent) {
View row=convertView;
if (row==null) {
LayoutInflater inflater=getLayoutInflater();
row=inflater.inflate(R.layout.row, null);
}
Restaurant r=model.get(position);
((TextView)row.findViewById(R.id.title)).setText(r.getName());
((TextView)row.findViewById(R.id.address)).setText(r.getAddress());
ImageView icon=(ImageView)row.findViewById(R.id.icon);
if (r.getType().equals("sit_down")) {
icon.setImageResource(R.drawable.ball_red);
}
else if (r.getType().equals("take_out")) {
icon.setImageResource(R.drawable.ball_yellow);
}
else {
icon.setImageResource(R.drawable.ball_green);
}
return(row);
}
}
Create a RestaurantHolder
To improve performance and encapsulation, we should move the logic that populates a row from a restaurant into a separate class, one that can cache the
TextView and
ImageView widgets.
To do this, add the following static inner class to
LunchList:
static class RestaurantHolder {
private TextView name=null;
private TextView address=null;
private ImageView icon=null;
RestaurantHolder(View row) {
name=(TextView)row.findViewById(R.id.title);
address=(TextView)row.findViewById(R.id.address);
icon=(ImageView)row.findViewById(R.id.icon);
}
void populateFrom(Restaurant r) {
name.setText(r.getName());
address.setText(r.getAddress());
if (r.getType().equals("sit_down")) {
icon.setImageResource(R.drawable.ball_red);
}
else if (r.getType().equals("take_out")) {
icon.setImageResource(R.drawable.ball_yellow);
}
else {
icon.setImageResource(R.drawable.ball_green);
}
}
}
Recycle Rows via RestaurantHolder
To take advantage of the new
RestaurantHolder, we need to modify
getView() in
RestaurantAdapter. Following the holder pattern, we need to create a
RestaurantHolder when we inflate a new row, cache that wrapper in the row via
setTag(), then get it back later via
getTag().
Change
getView() to look like the following:
public View getView(int position, View convertView,
ViewGroup parent) {
View row=convertView;
RestaurantHolder holder=null;
if (row==null) {
LayoutInflater inflater=getLayoutInflater();
row=inflater.inflate(R.layout.row, parent, false);
holder=new RestaurantHolder(row);
row.setTag(holder);
}
else {
holder=(RestaurantHolder)row.getTag();
}
holder.populateFrom(model.get(position));
return(row);
}
LunchList Class (Full Code)
package apt.tutorial;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.view.LayoutInflater;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.RadioGroup;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.List;
public class LunchList extends Activity {
List<Restaurant> model=new ArrayList<Restaurant>();
RestaurantAdapter adapter=null;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Button save=(Button)findViewById(R.id.save);
save.setOnClickListener(onSave);
ListView list=(ListView)findViewById(R.id.restaurants);
adapter=new RestaurantAdapter();
list.setAdapter(adapter);
}
private View.OnClickListener onSave=new View.OnClickListener() {
public void onClick(View v) {
Restaurant r=new Restaurant();
EditText name=(EditText)findViewById(R.id.name);
EditText address=(EditText)findViewById(R.id.addr);
r.setName(name.getText().toString());
r.setAddress(address.getText().toString());
RadioGroup types=(RadioGroup)findViewById(R.id.types);
switch (types.getCheckedRadioButtonId()) {
case R.id.sit_down:
r.setType("sit_down");break;
case R.id.take_out:
r.setType("take_out");break;
case R.id.delivery:
r.setType("delivery");break;
}
adapter.add(r);
}
};
class RestaurantAdapter extends ArrayAdapter<Restaurant> {
RestaurantAdapter() {
super(LunchList.this, R.layout.row, model);
}
public View getView(int position, View convertView,
ViewGroup parent) {
View row=convertView;
RestaurantHolder holder=null;
if (row==null) {
LayoutInflater inflater=getLayoutInflater();
row=inflater.inflate(R.layout.row, parent, false);
holder=new RestaurantHolder(row);
row.setTag(holder);
}
else {
holder=(RestaurantHolder)row.getTag();
}
holder.populateFrom(model.get(position));
return(row);
}
}
static class RestaurantHolder {
private TextView name=null;
private TextView address=null;
private ImageView icon=null;
RestaurantHolder(View row) {
name=(TextView)row.findViewById(R.id.title);
address=(TextView)row.findViewById(R.id.address);
icon=(ImageView)row.findViewById(R.id.icon);
}
void populateFrom(Restaurant r) {
name.setText(r.getName());
address.setText(r.getAddress());
if (r.getType().equals("sit_down")) {
icon.setImageResource(R.drawable.ball_red);
}
else if (r.getType().equals("take_out")) {
icon.setImageResource(R.drawable.ball_yellow);
}
else {
icon.setImageResource(R.drawable.ball_green);
}
}
}
}
Rebuild and reinstall the application, then try adding several restaurants and confirm that, when the list is scrolled, everything appears as it should,the name, address, and icon all change.
By
Mohd Zulkamal
NOTE : – If You have Found this post Helpful, I will appreciate if you can Share it on Facebook, Twitter and Other Social Media Sites. Thanks =)