Android - Custom ListAdapter look more fancier

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.

Note : Please follow the Android Tutorial Post Here Before You Step On This Post.

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
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 =)

Popular posts from this blog

Example to disable save as certain file type in SSRS Report Viewer

Phone book application example - Java

Modal Popup Message Box ASP.NEt C# Example