Sunday 6 March 2016

How to Create Custom Adapter for ListView in Android

Objective:

First, what is adapter? It's like a bridge that take data from source and make view to user.


ArrayAdapter is normally used to process only one dynamic data. But in real world , most of the cases we have to process more than one dynamic data. That's why we need custom adapter.

I'm going to explain things like a story. So advance sorry to geeks. :)

Our Project:

We will make a contact app where 
  • user store contact Name, Number & a default Image in a List-View temporary.
  • when user click any item in the list view, another activity will show that contact details.
  • where we set a call & message button to call or message that number.
Overview of our project:

 

 

I  will give a link to source code of the project.

Let's Get Started:

What do we need?
I assume that you have done the XML part of the project (Contacts & Contact Details) as I'm going to focus on 
  • Contact Model Class
  • Contact Adapter
  • Contact Details
if you have problem about layout, download the source code and find xml layouts in the layout folder.

Contact Model Class:

We need three field variable 
  • name
  • phone No
  • image id
We create a constructor using these fields to create an object of Contact.

// ADLabs
public class ContactModel {

    private String name;
    private String phoneNo;
    private int imageID;

    public ContactModel(String name, String phoneNo, int imageID) {
        setName(name);
        setPhoneNo(phoneNo);
        setImageID(imageID);
    }
    . . . . . . . .

}

Contact Adapter Class:

Here we will design our adapter for this we will extend ArrayAdapter class to override methods that already written by sdk.

We need two fields
  • context
  • ArrayList(<ContactModel>) contactList
We create a constructor using these fields to pass them from Main Activity.

// ADLabs

public class ContactAdapter extends ArrayAdapter {

    Context context;
    ArrayList contactList;

    public ContactAdapter(Context context, ArrayList objects) {
        super(context, R.layout.custom_layout, objects);
        this.context=context;
        this.contactList=objects;

    }

Inside the super method we specify our custom_layout that we going to use in ListView.

Now make a static class 'class_name' that contains EditText &ImageView referances.


// ADLabs

static class ViewHolder{
        ImageView pic;
        TextView showNameTV;
        TextView showPhoneNoTV;

    }


Why do we need such a class?
Answer is we will use an object that contains these fields.

Now, override 'getView'
From the name of method we can guess that return something view. Yes, We will generate our custom view for ListView by overriding this method.


// ADLabs

// generate view by myself instead of OS
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {

     // Create reference of ViewHolder Class
        ViewHolder viewHolder ;

  // If view all ready created...
        if(convertView == null){

            // Call System Service: Layout_Inflater Service
            LayoutInflater layoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
   // to convert my xml custom layout into view! by Layout_Inflater Service
            convertView = layoutInflater.inflate(R.layout.custom_layout,null);
   
   // Create Object
            viewHolder = new ViewHolder();
   // Initialize  
            viewHolder.pic= (ImageView) convertView.findViewById(R.id.showImageID);
            viewHolder.showNameTV= (TextView) convertView.findViewById(R.id.showNameID);
            viewHolder.showPhoneNoTV= (TextView) convertView.findViewById(R.id.showPhoneNoID);
   // Tag that object
            convertView.setTag(viewHolder);

        }else {
   // use already tagged object
            viewHolder = (ViewHolder) convertView.getTag();

        }
        // Set Values to 
        viewHolder.pic.setImageResource(contactList.get(position).getImageID());
        viewHolder.showNameTV.setText(contactList.get(position).getName());
        viewHolder.showPhoneNoTV.setText(contactList.get(position).getPhoneNo());

        return convertView;
    }



Inside the getView method

  • Create a ViewHolder Class reference.
  • At starting point view is null. So if condition will execute.
  • Call a system Service: Layout_Inflater_Service. This service will convert our custom layout XML file to view.
  • keep the generated view in a reference.
  • Create object of ViewHolder class
  • Initialize through object.
  • Tag this view Object for further use.
  • Set values through object and return that view.
  • Next time else condition will execute as we already created our view.
  • Retrieve view Object.
  • Set values through object & return view.

Now inside Main Activity:

we will put user input in a ArrayList(<ContactModel>) and pass it through Contact Adapter.
Code:


// ADLabs

        contactsLV = (ListView) findViewById(R.id.contactsLVID);
        nameET = (EditText) findViewById(R.id.nameETID);
        phoneNoET = (EditText) findViewById(R.id.phoneETID); 

        ArrayList<contactmodel>contactList = new ArrayList<>();
         
        // Create Adapter        
        ContactAdapter adapter = new ContactAdapter(getApplicationContext(),contactList);
        contactsLV.setAdapter(adapter);

        contactsLV.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView parent, View view, int position, long id) {

                Intent intent = new Intent(MainActivity.this,Main2Activity.class);
                intent.putExtra("nameKey",contactList.get(position).getName());
                intent.putExtra("phoneNoKey",contactList.get(position).getPhoneNo());
                intent.putExtra("imageKey",contactList.get(position).getImageID());
                startActivity(intent);
            }
        });


We we press any item in the list, Intent will pass value of current position to Main2Activity.

Now, Main2Activity Code: (Contact Details)

Code:

// ADLabs

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main2);

        profileImage = (ImageView) findViewById(R.id.contactDetailImgID);
        profileName = (TextView) findViewById(R.id.contactDetailNameID);
        profilePhoneNo = (TextView) findViewById(R.id.contactDetailPhoneID);
        callBtn = (ImageButton) findViewById(R.id.callBtnID);
        msgBtn = (ImageButton) findViewById(R.id.msgBtnID);

        profileImage.setImageResource(getIntent().getIntExtra("imageKey", 0));
        profileName.setText(getIntent().getStringExtra("nameKey"));
        phoneNo = getIntent().getStringExtra("phoneNoKey");
        profilePhoneNo.setText(phoneNo);


    }

    public void callNumber(View view){
        Intent callIntent = new Intent(Intent.ACTION_CALL, Uri.parse("tel:" + phoneNo));
        startActivity(callIntent);

    }

    public void msgNumber(View view){
        Intent msgIntent = new Intent(Intent.ACTION_SENDTO,Uri.parse("smsto:" + phoneNo));
        startActivity(msgIntent);
    }


We will retrieve value from Intent by message key and set them.
When Call button clicked Action_Call service will run.
When Message button clicked Action_SendTo service will run.

Try It Now.



( ͡° ͜ʖ ͡°)
Happy Coding :)
-@D