Как реализовать два разных типа разделителей (т.е. заголовков) в классе адаптера ListView

Я вызываю адаптер по этому набору кодов:

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()));

Рассмотрим этот код:

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;
}

Этот код просто отображает описание выбранного элемента (здесь TYPE_ITEM) в AlertDialog.

Как вы можете видеть, TYPE_SEPERATOR отключили onClick(), и я хочу добавить еще один TYPE_SEPERATOR_GRAY (пусть из gray.xml), который будет разделителем другого типа с отключенным onClick().

Нужно ли добавить еще один метод, аналогичный addSeparatorItem(ContentWrapper value) например addSeparatorItemGray(ContentWrapper value). Я знаю, что я должен добавить еще один случай в переключателе getView() для надувания grey.xml

Или что еще я должен добавить/изменить?

РЕДАКТИРОВАТЬ: ContentWrapper содержит текст элементов с его описанием. Я реализовал ContentWrapper для назначения каждого TYPE_ITEM с описанием

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 имеет тип MyCustomAdapter.

Первые 3-4 строки моего вопроса говорят, что у addSeparatorItem нет описания, поэтому во втором аргументе передается значение null, а в addItem есть текст и описание.

Я хочу добавить еще один TYPE_GRAY_SEPARATOR, в некоторые указанные позиции в списке вручную, например:

 mAdapter.addSeparatorItemGray("HI after 1st view");
 mAdapter.addSeparatorItemGray("HI after 23rd view");
 mAdapter.addSeparatorItemGray("HI after 45 view");

Ответ 1

Метод getViewType должен вернуть 3 (List Item + Separator + Grey Separator). Следовательно, установите TYPE_MAX_COUNT в 3.

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

Структура данных для размещения серого разделителя:

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

Метод добавления серого разделителя.

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

Метод getItemViewType должен возвращать соответствующий вид, основанный на позиции.

@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;
}

Метод getView должен обрабатывать 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
}

Ответ 2

Подумайте о дополнительном разделителе как другом виде. Поэтому, чтобы соответствовать вашему стилю кода, вам нужно добавить еще один Collection для этих разделителей и добавить необходимые методы:

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

Также обновите свой метод getViewTypeCount(), так как теперь у вас есть еще один вид. Наконец, добавьте еще один тег if else в свой метод getView, проверяя этот новый вид.


В качестве альтернативы ознакомьтесь с библиотекой StickyListHeaders, которая обрабатывает для вас много этой логики.

Ответ 3

Сначала добавьте еще один тип в getViewTypeCount и getItemViewType, как и другие.

Однако вы ошибаетесь. Вам не нужно делать элементы списка доступными! Это задача ListView для обнаружения кликов и запуска OnItemClickListener при нажатии элемента. Таким образом, вы можете удалить все вызовы setOnClickListener.

Чтобы сделать разделители в ListView, вам нужно сделать несколько элементов отключено. Для этой цели существуют следующие функции:

  • BaseAdapter.areAllItemsEnabled() - вы возвращаете false
  • isEnabled (int position) - возвращает false для элементов, которые являются разделителями

Также нет необходимости использовать Set < > для обозначения разделителей. Просто найдите исходный список записей с позицией и верните, что разделители отключены, нормальные элементы включены.