Android - 点击 ListView 后保持选中状态

37

我有一个活动,其中有两个 ListView 组件。当你在第一个组件中选择一个值时,第二个组件将填充与第一个 ListView 中选择相关的值。这个机制可以正常工作,但现在我想让用户的选择保持高亮显示。

我已经在第二个 ListView 上使用了 android:listSelector="#CCCCCC" XML 属性使其工作良好,但一旦引入 OnItemClickListener(像我在第一个 ListView 上使用的那个)后,这种效果好像被清除了。

到目前为止,这就是我所拥有的:

我在浏览有关此主题的各种答案时发现了自定义的 OnItemClickListener(稍微修改了一下,以便它能够加载我信息到第二个 ListView):

private class ItemHighlighterListener implements OnItemClickListener {

    private View oldSelection = null;

    public void clearSelection() {
        if(oldSelection != null) {
            oldSelection.setBackgroundColor(android.R.color.transparent);
        }
    }

    public void onItemClick(AdapterView<?> parent, View view, int pos, long id) {
        clearSelection();
        oldSelection = view;
        view.setBackgroundDrawable(view.getContext().getResources().getDrawable(R.drawable.list_selector));
        loadClubs(mXMLPortalOptions.getRegion(pos).getId());
        mClubList.setAdapter(new ArrayAdapter<String>(getApplicationContext(), R.layout.list_item_white, mClubs));
    }
}

以下是我的list_selector.xml文件:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">

    <item android:state_selected="true"><shape>
            <solid android:color="#CCCCCC" />
        </shape></item>

    <item android:state_selected="false"><shape>
            <solid android:color="#FFFFFF" />
        </shape></item>

</selector>

这个方法(OnItemClick)被调用并执行了,但是我的ListItem的背景颜色仍然保持不变 :/

我无法相信这个简单的任务已经变得如此复杂。

如果我省略了有用的代码,或者我的问题缺少细节,请随意指出,我会尽力解释清楚。


我认为这与您使用的SDK版本有关。 - jsmith
我正在使用API级别7。 - Jean-Philippe Roy
i.masm的帖子中有一个链接,指向我所想的内容。http://android-developers.blogspot.com/2008/12/touch-mode.html - jsmith
也许这可以帮助:https://dev59.com/ymUp5IYBdhLWcg3wVWev - Alberto Estrella
11个回答

63

给选定的条目设置一个位置变量。在 onItemClicked() 方法中更改该位置。在 List Adapter 中的 getView() 内检查所选位置,并为所选项目设置背景。

public class TestAdapter extends BaseAdapter
{
    private Context context;
    private ArrayList<TestList> testList;
    private int selectedIndex;
    private int selectedColor = Color.parseColor("#1b1b1b");

    public TestAdapter(Context ctx, ArrayList<TestList> testList)
    {
        this.context = ctx;
        this.testList = testList;
        selectedIndex = -1;
    }

    public void setSelectedIndex(int ind)
    {
        selectedIndex = ind;
        notifyDataSetChanged();
    }

    @Override
    public int getCount()
    {
        return testList.size();
    }

    @Override
    public Object getItem(int position)
    {
        return testList.get(position);
    }

    @Override
    public long getItemId(int position)
    {
        return position;
    }

    private class ViewHolder
    {
        TextView tv;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent)
    {
        View vi = convertView;
        ViewHolder holder;
        if(convertView == null)
        {
            vi = LayoutInflater.from(context).inflate(R.layout.test_list_item, null);
            holder = new ViewHolder();

            holder.tv = (TextView) vi;

            vi.setTag(holder);
        }
        else
        {
            holder = (ViewHolder) vi.getTag();
        }

        if(selectedIndex!= -1 && position == selectedIndex)
        {
            holder.tv.setBackgroundColor(Color.BLACK);
        }
        else
        {
            holder.tv.setBackgroundColor(selectedColor);
        }
        holder.tv.setText("" + (position + 1) + " " + testList.get(position).getTestText());

        return vi;
    }

}

当列表项被点击时,现在设置selectedIndex变量。

public class TestActivity extends Activity implements OnItemClickListener
{
    // Implemented onItemClickListener

    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position, long id)
    {
        adapter.setSelectedIndex(position);
    }
}

非常好的解释 - 多个新概念一次性讲解,谢谢! - Plasty Grove
伟大且简单,这就是我喜欢的代码!在我将一些东西设为静态并移动一些内容之前,我没有让它正常工作。(请看下面我的回答)。+1 - whyoz
你好,我正在尝试使用可扩展列表视图的这种方法,但无法使其正常工作。每当我打开一个组时,都会发现其中有一个项目被选中。 - Prateek
@Shaiful,实际上这个方法可以工作,但是由于每次点击项目时都会调用notifyDataSetChanged(),导致UI响应变慢。您能告诉我是否有任何解决方法吗? - ductran
1
@R4j 这不应该使您的用户界面变慢。可能有其他原因。每次调用notifyDataSetChanged()时,只有可见行数会得到更新。 - Shaiful
显示剩余3条评论

5

对于Shaiful的好方法,我想进一步解释一下,它可能无法在您的情况下起作用。

如果您的代码全部都写在public void onListItemClick(ListView l, View v, int index, long id)中,如果您正在使用Fragment并且必须声明一个接口而不是实现OnListItemClickListener,或者其他导致IDE生成错误的原因,您可能需要静态访问变量和方法。

public static int selectedPosition = 0;
ArrayAdapter<Your_obj> adapter = null;

@Override
public void onListItemClick(ListView l, View v, int index, long id) {
    super.onListItemClick(l, v, index, id);

        selectedPosition = index;
        Your_adapter.setSelectedIndex(selectedPosition);
        adapter.notifyDataSetChanged();
}

在您的适配器中:

private static int selectedIndex;

//public Your_adapter...

public static void setSelectedIndex(int ind) {
    selectedIndex = ind;
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    WellHolder holder = null;

    if (null == convertView) {

                //set up your "holder"
    }

    if (position == selectedIndex) {
        convertView.setBackgroundColor(convertView.getResources().getColor(R.color.cyan));
    }
    else {
        convertView.setBackgroundColor(convertView.getResources().getColor(R.color.silver));
    }

    return convertView;
}

另外一些不同之处在于您不需要将任何变量初始化为“0”或“-1”,并且在您的活动中调用了notifyDataSetChanged()。

再次感谢@Shaiful提供的解决方案。这确实帮助我节省了时间,尝试使默认在iOS上工作的内容适用于Android,同时避免了选择器/项目/焦点/按下等问题。


这是因为您没有使用状态列表,而是采用了一种低效且不太适合的解决方案。在这种情况下,最好依靠平台来处理此类问题。 - Necronet

4

我曾遇到类似问题,以下是我的解决方案:

首先,在你的列表视图中添加自定义列表选择器:

<ListView
    android:id="@+id/list"
    android:layout_width="match_parent"
    android:layout_height="fill_parent"
    android:listSelector="@drawable/listselector" />

在listselector.xml文件中:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_accelerated="false"
        android:drawable="@drawable/bg" />
</selector>

最后,一个具有突出颜色的 drawable bg.xml:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <solid android:color="#33b5e6"/>
</shape>

3
我认为最好且最简单的解决方法是这样的。您不需要在ListView本身上设置任何android:listSelector,也不需要对适配器进行任何更改。您甚至不需要在OnItemClickListener中调用任何setSelection(position),因为它会自动处理。
  1. Set to your ListView:

    android:choiceMode="singleChoice"
    
  2. Set background of the list item itself:

    android:background="?android:attr/activatedBackgroundIndicator"
    
  3. That's it.

这样,您将获得默认的系统行为。这就是在默认的 android.R.layout.simple_list_item_activated_1 布局中完成的。


2
//create a list_itemselectorin drawable folder
//you will get the list item selected background color change once you select //the item

    <selector xmlns:android="http://schemas.android.com/apk/res/android">

        <!-- Focused State -->
        <item android:state_focused="true"><shape>
                <solid android:color="#66FFFFFF" />
            </shape></item>
        <!-- Pressed State -->

        <item android:state_pressed="true"><shape>
                <solid android:color="@color/Black" />
            </shape></item>

        <!-- Default State -->
        <item><shape>
                <solid android:color="@color/Black" />
            </shape></item>

    </selector>


    //create a list in layout folder
      <ListView
            android:id="@+id/mySlidingList"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:choiceMode="singleChoice"
            android:divider="@color/GrayHot"
            android:dividerHeight="1dip"
            android:listSelector="@drawable/list_itemselector"
            android:scrollbars="none" />

// 然后查看输出结果。


2

lv.setSelector(R.drawable.highlighter);

在drawable文件夹中放置一个highlighter.png图片,这是突出显示列表视图中所选项目的最简单方法。


2

两周前我在寻找相关资料,结果发现使用drawable选择器是不可能实现的。

如果需要更多信息,请查看Android开发者博客中的这篇文章:触摸模式

简而言之:只有当手指触摸屏幕时,才会选中相应项目。

另一种可能性是将所选项目保存在变量中,并使用自定义适配器进行不同的绘制,就像Shaiful所说的那样。


0

有一个非常简单的完全XML解决方案,适用于我。 首先,定义带有选择器代码的XML可绘制对象,在其中“正常”状态将对应于列表项的“选中未按下”可视状态,state_pressed=true 将对应于可视化状态的“按下”。 类似Holo蓝色选择的文件“custom_item_selector.xml”的示例:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_pressed="true">
        <shape android:shape="rectangle">
            <solid
                android:color="#643292ff">
            </solid>
            <stroke
                android:width="1dp"
                android:color="#c83292ff">
            </stroke>
        </shape>
    </item>
    <item>
        <shape android:shape="rectangle">
            <solid
                android:color="#323292ff">
            </solid>
            <stroke
                android:width="1dp"
                android:color="#783292ff">
            </stroke>
        </shape>
    </item>
</selector>

(也可以在那里设置焦点状态)。 其次,将此xml可绘制对象应用为ListView的listSelector,并设置所需的choiceMode:

<ListView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:id="@+id/listView"
            android:choiceMode="singleChoice"
            android:listSelector="@drawable/custom_item_selector"/>

就这些。它允许为“简单选择”和“按下选择”项目定义不同的可视状态,例如在按下时使项目变亮。


0
总结一下这篇文章,或许能帮助未来的某个人,我的建议是:)

首先,我们需要创建一个名为res/drawable/list_item_background.xml文件,并加入以下内容:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:state_activated="true"
        android:drawable="@color/list_item_activated" />
    <item
        android:drawable="@color/list_item_default" />
</selector>

当然,要指定您的可绘制资源。您还可以添加其他具有不同状态的<item>元素,例如state_pressedstate_focused等。

然后,我们应该将background参数设置为我们自定义列表项ViewGroup元素(例如res/layout/list_item_layout.xml)如下所示:

android:background="@drawable/list_item_background"

下一步是修改我们的自定义Adapter类。以下是代码片段:
public class CustomAdapter extends BaseAdapter {
    private List<Item> items;
    private LayoutInflater itemInflater;        
    private int selectedIndex; // add this

    public CustomAdapter(Context c, List<Item> items) {
        this.items = items;
        this.itemInflater = LayoutInflater.from(c);
        selectedIndex = -1; // add this
    }

    /* add this */
    public void setSelectedIndex(int index) {
        selectedIndex = index;
        notifyDataSetChanged();
    }

    /* other adapter's stuff */

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        if(convertView == null) {
            convertView = itemInflater.inflate(R.layout.list_item_layout, parent, false);
        }

        // add this
        convertView.setActivated(selectedIndex != -1 && position == selectedIndex);

        /* do some stuff */

        return convertView;
    }
}

最后,在AdapterView.OnItemClickListeneronItemClick(...)方法中,我们应该调用适配器的setSelectedIndex(position)方法。
public class YourActivity extends Activity
        implements AdapterView.OnItemClickListener {

    private CustomAdapter mCustomAdapter;

    /* activity implementation */

    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
        mCustomAdapter.setSelectedIndex(position);
    }
}

现在,我们可以满意地突出显示适当的列表项 :)
附言:如果我们想在列表上启用多选模式,我们只需将以下字符串放置到我们的活动类中,其中保留了“listView”实例:
listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);

因此,我们将得到适当的多个项高亮显示。

-- 希望这能帮助任何人 :)


0
如果您可以使用可绘制对象来显示列表项的高亮,则应使用以下代码:
listView.setSelector(R.drawable.bg_image);

它有效。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接