Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to implement 2 different types of separators (i.e headers) in a ListView Adapter class

I am calling the adapter by this set of codes:

mAdapter = new MyCustomAdapter(getActivity());

mAdapter.addSeparatorItem(new ContentWrapper(q.get(0).getA_name(),null));
mAdapter.addItem(new ContentWrapper(q.get(0).getAS_name(), q.get(0).getDesc_art()));

Consider this code:

private class MyCustomAdapter extends BaseAdapter {

private static final int TYPE_ITEM = 0;
private static final int TYPE_SEPARATOR = 1;
private static final int TYPE_MAX_COUNT = TYPE_SEPARATOR + 1;

private ArrayList<ContentWrapper> mData = new ArrayList<ContentWrapper>();
private LayoutInflater mInflater;

private TreeSet<Integer> mSeparatorsSet = new TreeSet<Integer>();

public MyCustomAdapter(Context context)
{
    mInflater = LayoutInflater.from(context); 
}

public void addItem(ContentWrapper value) {
    mData.add(value);
    notifyDataSetChanged();
}

public void addSeparatorItem(ContentWrapper value) {
    mData.add(value);
    // save separator position
    mSeparatorsSet.add(mData.size() - 1);
    notifyDataSetChanged();
}

public ContentWrapper getItem(int position) {
    return mData.get(position);
}
@Override
public int getItemViewType(int position) {
    return mSeparatorsSet.contains(position) ? TYPE_SEPARATOR : TYPE_ITEM;
}

@Override
public int getViewTypeCount() {
    return TYPE_MAX_COUNT;
}

public int getCount() {
    return mData.size();
}

public long getItemId(int position) {
    Log.v("getItemId Position", ""+position);
    return position;

}

public View getView(final int position, View convertView, ViewGroup parent) {
    ViewHolder holder = null;
    int type = getItemViewType(position);
    if (convertView == null) {
        holder = new ViewHolder();
        switch (type) {
        case TYPE_ITEM:
            convertView = mInflater.inflate(R.layout.white, null);
            holder.textView = (TextView)convertView.findViewById(R.id.text);
            break;
        case TYPE_SEPARATOR:
            convertView = mInflater.inflate(R.layout.black, null);
            holder.textView = (TextView)convertView.findViewById(R.id.textSeparator);
            count++;
            break;
        }
        convertView.setTag(holder);
    } else {
        holder = (ViewHolder)convertView.getTag();
    } holder.textView.setText(mData.get(position).getItem());

    if (type == TYPE_ITEM) {
        holder.textView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                    AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
                    builder.setIcon(R.drawable.ic_launcher);
                    final String title = mData.get(position).getItem();
                    builder.setTitle(title);
                    builder.setMessage(mData.get(position).getItemDescription());
                    builder.setCancelable(false);
                    builder.setPositiveButton("Ok", new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            dialog.dismiss();
                        }
                    });
                    AlertDialog alertDialog = builder.create();
                    alertDialog.setOnShowListener(new DialogInterface.OnShowListener() {
                        @Override
                        public void onShow(DialogInterface dialog) {
                            AlertDialog alertDialog = (AlertDialog) dialog;
                            ViewGroup viewGroup = (ViewGroup) alertDialog.getWindow()
                                    .getDecorView();
                            TextView textView = findTextViewWithTitle(viewGroup, title);
                            if (textView != null) {
                                textView.setEllipsize(null);
                                textView.setMaxHeight((int) (100 * alertDialog.getContext().getResources().getDisplayMetrics().density)); 
                                textView.setMovementMethod(new ScrollingMovementMethod());
                            }
                        }
                    });
                    alertDialog.show();
                }

                private TextView findTextViewWithTitle(ViewGroup viewGroup, String title) {
                    for (int i = 0, N = viewGroup.getChildCount(); i < N; i++) {
                        View child = viewGroup.getChildAt(i);
                        if (child instanceof TextView) {
                            TextView textView = (TextView) child;
                            if (textView.getText().equals(title)) {
                                return textView;
                            }
                        } else if (child instanceof ViewGroup) {
                            ViewGroup vGroup = (ViewGroup) child;
                            return findTextViewWithTitle(vGroup, title);
                        }
                    }
                    return null;
                }
            });
    } else {
        holder.textView.setOnClickListener(null);
    }

return convertView;
}
}
public static class ViewHolder {
public TextView textView;
}

public boolean onTouch(View v, MotionEvent event) {
// TODO Auto-generated method stub
return false;
}

This code just display the description of selected item (here TYPE_ITEM) in an AlertDialog.

As you can see TYPE_SEPERATOR have disabled onClick() & I want to add one more TYPE_SEPERATOR_GRAY(let from gray.xml) be the another type separator with disabled onClick().

Do I need to add one more method similar to addSeparatorItem(ContentWrapper value) like addSeparatorItemGray(ContentWrapper value). I know that I have to add one more case in the switch of getView() for inflating the gray.xml

Or what else should I add/modify?

EDIT: ContentWrapper holds items text with its description. I implemented ContentWrapper to assign each TYPE_ITEM with a description

public class ContentWrapper {

private String mItem, mItemDescription;

public ContentWrapper(String item, String itemDescription) {
    mItem = item;
    mItemDescription = itemDescription;
}

public String getItem() {
    return mItem;
}

public String getItemDescription() {
    return mItemDescription;
}
}

mAdapter is of type MyCustomAdapter.

The first 3-4 lines of my question says that addSeparatorItem do not have any description so passed null in the second argument & addItem have both text , description.

I want to add the another TYPE_GRAY_SEPARATOR , at some specified positions in the list manually like:

 mAdapter.addSeparatorItemGray("HI after 1st view");
 mAdapter.addSeparatorItemGray("HI after 23rd view");
 mAdapter.addSeparatorItemGray("HI after 45 view");
like image 407
Vivek Warde Avatar asked Apr 09 '14 06:04

Vivek Warde


3 Answers

The method getViewType should return 3 (List Item + Separator + Gray Separator). Hence set TYPE_MAX_COUNT to 3.

private static final int TYPE_GRAY_SEPARATOR = 2;
private static final int TYPE_MAX_COUNT = TYPE_GRAY_SEPARATOR + 1;

Data structure to hold gray separator positions:

private TreeSet<Integer> mGraySeparatorsSet = new TreeSet<Integer>();

Method to add the gray separator.

public void addGraySeparatorItem(ContentWrapper value) {
    mData.add(value);
    // save separator position
    mGraySeparatorsSet.add(mData.size() - 1);
    notifyDataSetChanged();
}           

The method getItemViewType should return appropriate view based on position.

@Override
public int getItemViewType(int position) {
    int viewType = TYPE_ITEM;
    if(mSeparatorSet.contains(position))
       viewType = TYPE_SEPARATOR;
    else if(mGraySeparatorSet.contains(position)) {
       viewType = TYPE_GRAY_SEPARATOR; 
    }
    return viewType;
}

The method getView should handle TYPE_GRAY_SEPARATOR:

public View getView(final int position, View convertView, ViewGroup parent) {
    // Existing code
    switch(type) {
        // Existing cases
        case TYPE_GRAY_SEPARATOR: 
           // Inflate appropriate view
           break;
    }
    // Existing code
}
like image 75
Manish Mulimani Avatar answered Nov 09 '22 20:11

Manish Mulimani


Think of the extra separator as another view type.
Therefore, to conform to your code style, you need to add another Collection for these separators, and add needed methods:

private static final int TYPE_GRAY_SEPARATOR = 2;
private TreeSet<Integer> mGraySeparatorsSet = new TreeSet<Integer>();

Also update your getViewTypeCount() method, as you have one more view type now.
Lastly, add another if else check in your getView method, checking for this new view type.


Alternatively, check out the StickyListHeaders library, which handles a lot of this logic for you.

like image 24
nhaarman Avatar answered Nov 09 '22 21:11

nhaarman


First, add yet another type to getViewTypeCount and getItemViewType, as other suggest.

However, you make it wrong. You don't need to make list items clickable! This is task of ListView to detect clicks and fire OnItemClickListener when item is clicked. So you may remove all setOnClickListener calls.

To make separators in ListView, you need to make some items disabled. For this purpose, there are these functions:

  • BaseAdapter.areAllItemsEnabled() - you return false
  • isEnabled(int position) - return false for items that are separators

Also there's no need to use Set<> to mark separators. Just lookup your source list of entries with position, and return that separators are disabled, normal items are enabled.

like image 38
Pointer Null Avatar answered Nov 09 '22 19:11

Pointer Null