设置从适配器检查的 ListView 项目

发布于 2024-12-01 13:09:19 字数 5905 浏览 5 评论 0原文

我有一个 ListView 显示 2 种项目。其中一种包含 CheckedTextView。作为适配器,我使用自定义适配器扩展 ArrayAdapter,其数据结构包含有关内部已检查/未检查状态的信息。

有些项目被标记为选中(在我的数据结构中),所以我当然希望在创建 ListView 时标记复选框。我尝试使用 CheckedTextView 的 setChecked() 方法在适配器中的 getView() 方法上执行此操作,但它不起作用。我发现信息表明它应该使用 setItemChecked() 在 ListView 级别上完成,它确实有效,但对我来说没有意义,因为要使其工作,我必须循环 Activity 的 onCreate() 中的所有项目调用 setItemChecked()对于那些被选中的人。项目列表可能很长,但只选择了其中的几个,所以这是一种浪费。

为什么在 getView() 中调用 setChecked() 不起作用?有没有更好的方法来做到这一点(我是 Android 新手)。

下面是我检查的项目布局和适配器。

布局:

<?xml version="1.0" encoding="utf-8"?>
<CheckedTextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@android:id/text1"
    android:layout_width="fill_parent"
    android:layout_height="?android:attr/listPreferredItemHeight"
    android:gravity="center_vertical"
    android:textAppearance="@style/listViewItemText"
    android:checkMark="?android:attr/listChoiceIndicatorMultiple"
    android:paddingLeft="6dp"
    android:paddingRight="6dp"
/>

适配器:

package com.melog.re.droid.search;

import java.util.ArrayList;
import java.util.List;

import android.content.Context;
import android.os.Parcel;
import android.os.Parcelable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.CheckedTextView;
import android.widget.TextView;

import com.melog.re.droid.core.R;
import com.melog.re.droid.search.MultiCriterionValue.MultiCriterionSingleValue;
import com.melog.re.droid.search.MultiListAdapter.MultiChoiceItem;

public class MultiListAdapter extends ArrayAdapter<MultiChoiceItem> {
    private static final int VIEW_TYPES_COUNT = 2;

    public static final int VIEW_TYPE_GROUP = 0;
    public static final int VIEW_TYPE_ITEM = 1;

    private LayoutInflater mInflater;

    public MultiListAdapter(Context context, int textViewResourceId, List<MultiChoiceItem> objects) {
        super(context, textViewResourceId, objects);
        mInflater = LayoutInflater.from(context);
    }

    @Override
    public int getItemViewType(int position) {
        MultiChoiceItem item = getItem(position);
        return (item.children.size() == 0) ? VIEW_TYPE_ITEM : VIEW_TYPE_GROUP;
    }

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

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        // TODO: optymalizacja ViewHolder
        MultiChoiceItem item = getItem(position);
        int viewType = getItemViewType(position);
        switch (viewType) {
        case VIEW_TYPE_GROUP:
            if (convertView == null) {
                convertView = mInflater.inflate(R.layout.multi_choice_group_item, parent, false);
            }
            TextView groupLabel = (TextView) convertView.findViewById(android.R.id.text1);
            groupLabel.setText(item.toString());
            break;
        case VIEW_TYPE_ITEM:
            if (convertView == null) {
                convertView = mInflater.inflate(R.layout.multi_choice_child_item, parent, false);
            }
            CheckedTextView itemLabel = (CheckedTextView) convertView.findViewById(android.R.id.text1);
            itemLabel.setText(item.toString());
            itemLabel.setChecked(item.selected);
            break;
        }

        return convertView;
    }

    public static class MultiChoiceItem implements Parcelable {
        public static final int CONTENTS_DESCR = 1;

        public String label;
        public String value;
        public boolean selected = false;
        public ArrayList<MultiChoiceItem> children = new ArrayList<MultiChoiceItem>();

        public MultiChoiceItem(String l, String v, boolean sel) {
            label = l;
            value = v;
            selected = sel;
        }
        public MultiChoiceItem(MultiCriterionSingleValue v) {
            label = v.toString();
            value = v.value;
        }
        public MultiChoiceItem(Parcel in) {
            label = in.readString();
            value = in.readString();
            selected = (in.readInt() == 1);
        }
        @Override
        public String toString() {
            return label;
        }
        @Override
        public int describeContents() {
            return CONTENTS_DESCR;
        }
        @Override
        public void writeToParcel(Parcel dest, int flags) {
            dest.writeString(label);
            dest.writeString(value);
            dest.writeInt(selected ? 1 : 0);
        }

        public static final Parcelable.Creator<MultiChoiceItem> CREATOR = new Parcelable.Creator<MultiChoiceItem>() {
            public MultiChoiceItem createFromParcel(Parcel in) {
                return new MultiChoiceItem(in);
            }

            public MultiChoiceItem[] newArray(int size) {
                return new MultiChoiceItem[size];
            }
        };
}
}

以及一个活动的 onCreate()

...     
mAdapter = new MultiListAdapter(this, R.id.head, mItems);
setListAdapter(mAdapter);

final ListView listView = getListView();
listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
listView.setItemsCanFocus(false);
...

编辑:

在找到更好的解决方案之前,我在活动中实现了检查:

...
int len = mListView.getCount();
for (int i = 0; i < len; i++) {
    if (((MultiChoiceItem) mListView.getItemAtPosition(i)).selected) {
        mListView.setItemChecked(i, true);
    }
}
...

我认为性能将是唯一的潜在问题,但我发现了另一个问题 - 更严重。该列表启用了文本过滤器(mListView.setTextFilterEnabled(true))。这是失败的场景:

  1. 我正在打开新列表,
  2. 我正在检查第二项,
  3. 当过滤完成后,我开始输入以过滤某些项目
  4. ,第二项仍然被检查,即使它不是我检查的那个

我假设的setItemChecked() 标记列表上已检查的固定位置,而我需要一些东西来标记该位置上的项目 - 即使列表上的位置发生变化也保持状态。

虽然性能问题可能(在某种程度上)被忽略 - 这个新问题确实阻碍了我,所以我非常感谢任何帮助。

I have a ListView which displays 2 kinds of items. One of these kinds contains CheckedTextView. As an adapter I'm using custom adapter extending ArrayAdapter with data structure containing information about checked/unchecked states inside.

Some of the items are marked as selected (in my data structure) so of course I'd like the checkboxes to be marked when the ListView is created. I tried to do it on getView() method in adapter with CheckedTextView's setChecked() method but it doesn't work. I found information that it should be done on a ListView level with setItemChecked() and it does work but it doesn't make sense to me because to make it work I would have to loop all items in activity's onCreate() calling setItemChecked() for those selected. The list of items may be very long with only a few of them selected so it's a waste.

Why calling setChecked() in getView() isn't working? Is there a better way of doing this (I'm an Android newbie).

Below you have my checked item layout and the adapter.

Layout:

<?xml version="1.0" encoding="utf-8"?>
<CheckedTextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@android:id/text1"
    android:layout_width="fill_parent"
    android:layout_height="?android:attr/listPreferredItemHeight"
    android:gravity="center_vertical"
    android:textAppearance="@style/listViewItemText"
    android:checkMark="?android:attr/listChoiceIndicatorMultiple"
    android:paddingLeft="6dp"
    android:paddingRight="6dp"
/>

Adapter:

package com.melog.re.droid.search;

import java.util.ArrayList;
import java.util.List;

import android.content.Context;
import android.os.Parcel;
import android.os.Parcelable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.CheckedTextView;
import android.widget.TextView;

import com.melog.re.droid.core.R;
import com.melog.re.droid.search.MultiCriterionValue.MultiCriterionSingleValue;
import com.melog.re.droid.search.MultiListAdapter.MultiChoiceItem;

public class MultiListAdapter extends ArrayAdapter<MultiChoiceItem> {
    private static final int VIEW_TYPES_COUNT = 2;

    public static final int VIEW_TYPE_GROUP = 0;
    public static final int VIEW_TYPE_ITEM = 1;

    private LayoutInflater mInflater;

    public MultiListAdapter(Context context, int textViewResourceId, List<MultiChoiceItem> objects) {
        super(context, textViewResourceId, objects);
        mInflater = LayoutInflater.from(context);
    }

    @Override
    public int getItemViewType(int position) {
        MultiChoiceItem item = getItem(position);
        return (item.children.size() == 0) ? VIEW_TYPE_ITEM : VIEW_TYPE_GROUP;
    }

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

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        // TODO: optymalizacja ViewHolder
        MultiChoiceItem item = getItem(position);
        int viewType = getItemViewType(position);
        switch (viewType) {
        case VIEW_TYPE_GROUP:
            if (convertView == null) {
                convertView = mInflater.inflate(R.layout.multi_choice_group_item, parent, false);
            }
            TextView groupLabel = (TextView) convertView.findViewById(android.R.id.text1);
            groupLabel.setText(item.toString());
            break;
        case VIEW_TYPE_ITEM:
            if (convertView == null) {
                convertView = mInflater.inflate(R.layout.multi_choice_child_item, parent, false);
            }
            CheckedTextView itemLabel = (CheckedTextView) convertView.findViewById(android.R.id.text1);
            itemLabel.setText(item.toString());
            itemLabel.setChecked(item.selected);
            break;
        }

        return convertView;
    }

    public static class MultiChoiceItem implements Parcelable {
        public static final int CONTENTS_DESCR = 1;

        public String label;
        public String value;
        public boolean selected = false;
        public ArrayList<MultiChoiceItem> children = new ArrayList<MultiChoiceItem>();

        public MultiChoiceItem(String l, String v, boolean sel) {
            label = l;
            value = v;
            selected = sel;
        }
        public MultiChoiceItem(MultiCriterionSingleValue v) {
            label = v.toString();
            value = v.value;
        }
        public MultiChoiceItem(Parcel in) {
            label = in.readString();
            value = in.readString();
            selected = (in.readInt() == 1);
        }
        @Override
        public String toString() {
            return label;
        }
        @Override
        public int describeContents() {
            return CONTENTS_DESCR;
        }
        @Override
        public void writeToParcel(Parcel dest, int flags) {
            dest.writeString(label);
            dest.writeString(value);
            dest.writeInt(selected ? 1 : 0);
        }

        public static final Parcelable.Creator<MultiChoiceItem> CREATOR = new Parcelable.Creator<MultiChoiceItem>() {
            public MultiChoiceItem createFromParcel(Parcel in) {
                return new MultiChoiceItem(in);
            }

            public MultiChoiceItem[] newArray(int size) {
                return new MultiChoiceItem[size];
            }
        };
}
}

And a piece of activity's onCreate()

...     
mAdapter = new MultiListAdapter(this, R.id.head, mItems);
setListAdapter(mAdapter);

final ListView listView = getListView();
listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
listView.setItemsCanFocus(false);
...

Edit:

Until finding a better solution I implemented the checking in the Activity:

...
int len = mListView.getCount();
for (int i = 0; i < len; i++) {
    if (((MultiChoiceItem) mListView.getItemAtPosition(i)).selected) {
        mListView.setItemChecked(i, true);
    }
}
...

I thought that performance will be the only potential problem but I found another one - much more serious. The list has text filter enabled (mListView.setTextFilterEnabled(true)). Here's the failure scenario:

  1. I'm opening new list
  2. Im checking let's say 2-nd item
  3. I start typing to filter some items
  4. when the filtering is done, the 2-nd item is still checked even though it's not the one I checked

I assume that setItemChecked() marks a fixed position on the list checked while I need something that will mark the Item held on ths position - keeping the state even when the position on the list changes.

While performance issue could be (to some point) ignored - this new problem is really blocking me, so I would really appreciate any help with it.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

山川志 2024-12-08 13:09:19

如果您使用 ListView.CHOICE_MODE_MULTIPLECHOICE_MODE_SINGLE,则在调用 getView 后,项目的检查状态将被 ListView 覆盖。您必须使用以下构造:

@Override
public View getView(int position, View convertView, ViewGroup parent) {
   ((ListView)parent).setItemChecked(position, true);
}

另一种可能性是使用 ListView.CHOICE_MODE_NONE 并自行管理检查状态。

If you use ListView.CHOICE_MODE_MULTIPLE or CHOICE_MODE_SINGLE the check states of the items will be overridden by ListView after getView is being called. You have to use the following construct:

@Override
public View getView(int position, View convertView, ViewGroup parent) {
   ((ListView)parent).setItemChecked(position, true);
}

Another possiblity is to use ListView.CHOICE_MODE_NONE and manage the check states by yourself.

影子的影子 2024-12-08 13:09:19

查看步骤
首先在 item xml 中进行更改
在复选框中添加单行,

`android:onClick="chatWithFriend"` 

以便当单击复选框时
然后在活动名称中创建一个方法

  public void chatWithFriend(View view){}
here you will get the view of check box,

之前在 getView 中添加这一行

现在在 return Convert viewholder.checkbox.setTag(position);

;现在您将获得复选框的位置,

希望这对您有所帮助。

Check out the steps
First in item xml make the changes
add single line in check box

`android:onClick="chatWithFriend"` 

so when the click on cheackbox
then create the one method in activity name

  public void chatWithFriend(View view){}
here you will get the view of check box,

Now add the line in getView before return convert view

holder.checkbox.setTag(position);

Now u will get the position of the check box

I hope this will help you.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文