滚动项目时,Android listSelector仍部分可见 [英] Android listSelector still partly visible when the item is scrolled out

查看:67
本文介绍了滚动项目时,Android listSelector仍部分可见的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

该图像显示了问题:Venus项目已滚动出,但它的选择是可见的.

The image shows the problem: Venus item is scrolled out but it's selection is visible.

我查看了 ListView中的悬挂列表选择器,我正在使用形状/渐变按照建议,但仍然没有运气.

I've looked at Hanged listSelector in ListView and I'm using shapes/gradient as it was advised, but still no luck.

设备:适用于Android,Android 4.2 XHDPI API-17的MS VS Emulator
IDE:Windows 7 WM上的Android Studio 2.

Device: MS VS Emulator for Android, Android 4.2 XHDPI API-17
IDE: Android Studio 2. on Windows 7 WM.

布局,主要活动

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <ListView
        android:id="@android:id/list"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:choiceMode="singleChoice"
        android:drawSelectorOnTop="false"
        android:scrollingCache="false"
        android:animationCache="false"
        android:listSelector="@drawable/list_selector">
    </ListView>

</LinearLayout>

列表选择器

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:state_pressed="true"
        android:drawable="@drawable/item_pressed" />
    <item
        android:drawable="@drawable/item_selected" />
</selector>

可绘制的物品,已按下

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle" >
    <gradient
        android:angle="90"
        android:endColor="#0000ff"
        android:startColor="#0000ff" />

</shape>

item_selected

item_selected

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle" >
    <gradient
        android:angle="90"
        android:endColor="#00ff00"
        android:startColor="#00ff00" />

</shape>

活动代码

package com.example.listdemo;

import android.app.ListActivity;
import android.os.Bundle;
import android.widget.ArrayAdapter;

public class MainActivity extends ListActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        ArrayAdapter adapter = ArrayAdapter.createFromResource(this, R.array.Planets, R.layout.myitem);
        setListAdapter(adapter);
    }
}

其中Planets只是一个字符串数组(太阳,水星,金星,地球,火星,木星...),而myitem只是一个具有自定义高度的TextView.

where Planets is just an array of strings (Sun, Mercury, Venus, Earth, Mars, Jupiter...) and myitem is just a TextView with custom height.

请问我在哪里错了?

编辑 为了澄清这个问题,它与默认列表选择行为有关.那是关于没有android:state_xxx属性的列表选择器项.不要过多注意相应的可绘制名称.我准备将@drawable/item_selected重命名为@drawable/item_default.让我知道这是否有助于澄清问题,我将其重命名.

EDIT To clarify the question, it's about default list selection behavior. That is about list selector item with no android:state_xxx attributes. Don't pay much attention to corresponding drawable name. I'm ready to rename @drawable/item_selected to @drawable/item_default. Let me know if it will help to clarify the problem and I'll rename it.

推荐答案

tl; dr 请勿在列表选择器上设置默认的Drawable.

tl;dr Don't set a default Drawable on your list selector.

当您为列表选择器提供默认的Drawable时,就会出现此问题.我的意思是,在列表选择器定义中,您有一个item标记,没有状态要求,这使得item会无意中成为默认 Drawable.您可以在此处了解更多信息.

This problem arises when you give the list selector a default Drawable. By this I mean within your list selector definition, you have an item tag with no state requirement which makes that item inadvertently the default Drawable. You can read more about selectors here.

您的列表选择器代码:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:state_pressed="true"
        android:drawable="@drawable/item_pressed" />
    <item
        android:drawable="@drawable/item_selected" /> <-- this is what I'm referring to
</selector>

修复

您遇到的问题是由始终绘制列表选择器引起的(即使选择器不在屏幕上).通常,这不是问题,因为列表选择器是透明的(因此是不可见的).但是,由于您为列表选择器提供了默认背景,这意味着只要列表选择器出现在屏幕上,它就会可见,从而导致您观察到怪异的行为.相反,您真正想要的是仅在实际选择一个项目时显示此背景.

The issue you were having is caused by the list selector always being drawn (even if the selector isn't on screen). Normally this isn't an issue since the list selector is transparent (and thus invisible). However since you gave the list selector a default background, this meant that whenever the list selector was on screen, it would be visible causing the weird behavior you observed. Instead what you really want is to only show this background when an item is actually selected.

为此,首先我们必须从列表选择器中删除默认背景.然后,我们需要一种新的方式来指示选定的项目.由于您在ListView中指定了android:choiceMode="singleChoice",因此ListView将您的列表项视为复选框列表.因此,当用户检查其中一项时,其激活状态将设置为true.但是,默认情况下激活时,TextViews将不显示任何视觉效果.为了显示选定的特定背景,我们需要使用可以显示激活状态的列表项布局.一种方法是将ListView项目视图的背景更改为选择器,然后定义要用于激活状态的Drawable.

To do this, first we must remove the default background from the list selector. Then we need a new way to indicate selected items. Since you specified android:choiceMode="singleChoice" in your ListView, the ListView will treat your list items like a list of checkboxes. Thus, when the user checks one of the items, it's activated state will be set to true. However, TextViews will not show any visual effects when activated by default. To show a specific background when selected we need to use a list item layout that can display the activated state. One way to do this is to change the background of the ListView item view to a selector and define a Drawable you want to use for the activated state.

例如:

适配器代码:

ArrayAdapter adapter = ArrayAdapter.createFromResource(this, 
    R.array.Planets, R.layout.myitem);

myitem.xml:

myitem.xml:

<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@android:id/text1"
    style="?android:attr/spinnerItemStyle"
    android:singleLine="true"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@drawable/item_background"
    android:paddingTop="16dp"
    android:paddingBottom="16dp"/>

item_background.xml:

item_background.xml:

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

或者,如果您很懒:

ArrayAdapter adapter = ArrayAdapter.createFromResource(this, 
    R.array.planets_array, android.R.layout.simple_list_item_activated_1);

进一步阅读

从Android文档中,我所说的确实并不完全清楚,因此您可能会问我的答案是否可信.本节专门针对那些寻求可靠答案和非常好奇的人.

要了解列表选择器的工作方式和编程方式,我们需要深入研究Android源代码.首先,ListView逻辑的实质实际上保存在称为AbsListView的类中(如果您没有下载源代码,则可以参考

To understand how the list selector works and how it is programmed, we will need to dive into the Android source code. To begin, the meat of the logic of the ListView is actually held in a class called AbsListView (in case you do not have the source downloaded you can refer to this). Digging into the source of this class we will find a few useful fields/functions pertaining to the selector:

  • mSelector:这是选择器(用android:listSelector指定的选择器)的Drawable
  • mSelectorRect:此字段确定选择器的绘制位置以及选择器的大小
  • mSelectedPosition:存储所选项目的索引(该字段实际上在类AdapterView中甚至更深处声明)
  • positionSelector(...):更新选择器的绘制位置
  • drawSelector(...):绘制选择器
  • trackMotionScroll(...):包含ListView滚动行为的逻辑
  • mSelector: This is the Drawable of the selector (the one you specify with android:listSelector)
  • mSelectorRect: This field determines where the selector is drawn and how big the selector is
  • mSelectedPosition: Stores the index of the selected item (this field is actually declared even deeper down in the class AdapterView)
  • positionSelector(...): Updates where the selector should be drawn
  • drawSelector(...): Draws the selector
  • trackMotionScroll(...): Contains the logic of the ListView's scrolling behavior

现在我们已经了解了环境,我们终于可以了解列表选择器行为的核心逻辑.全部归结为trackMotionScroll(...)中的以下几行代码:

Now that we have a understanding of the environment, we can finally understand the core logic to the list selector's behavior. It all comes down to these few lines of code in trackMotionScroll(...):

boolean trackMotionScroll(int deltaY, int incrementalDeltaY) {
    ...
    if (!inTouchMode && mSelectedPosition != INVALID_POSITION) {
        // if we are not in touch mode and there is a selected item then
        // we do a quick check if the selected item is on screen
        final int childIndex = mSelectedPosition - mFirstPosition;
        if (childIndex >= 0 && childIndex < getChildCount()) {
            // if the selected item is on screen, we move the selector to
            // where the selected item is
            positionSelector(mSelectedPosition, getChildAt(childIndex));
        }
    } else if (mSelectorPosition != INVALID_POSITION) {
        // if we are in touch mode and there is a selected item then
        // we do a quick check if the selected item is on screen
        final int childIndex = mSelectorPosition - mFirstPosition;
        if (childIndex >= 0 && childIndex < getChildCount()) {
            // if the selected item is on screen, we move the selector to
            // where the selected item is
            positionSelector(INVALID_POSITION, getChildAt(childIndex));
        }
    } else {
        // otherwise, if nothing is selected, hide the selector (don't draw it)
        mSelectorRect.setEmpty();
    }
    ...
}

上面的源代码片段已从原始文件中进行了编辑,以包含注释.

我们终于在这里找到了解释所观察到的行为的逻辑:当mSelectorPosition == INVALID_POSITION或英语中没有选定项目时,列表选择器仅隐藏.否则,如果该项目在屏幕上,则将其放置在选定的项目上,否则不会对其位置进行任何更改.

It is here where we finally find the logic that explains the behavior observed: The list selector is only hidden when mSelectorPosition == INVALID_POSITION or, in English, when there are no selected items. Otherwise it is positioned at the selected item if the item is on screen, otherwise no changes are made to it's position.

因此,当您滚动ListView且所选项目不显示在屏幕上时,列表选择器仅停留在所选项目解释所观察到的 ghost 列表选择器的最后一个位置.

So when you scroll the ListView and the selected item goes off screen, the list selector just stays put in the last location the selected item was explaining the ghost list selector observed.

最终想法

自从引入ListViews以来,我不得不说整个事情的设计不是很好,并且可能会引起很多错误.我强烈建议您尽可能使用它的后继产品RecyclerView.

From working with ListViews since it's introduction, I have to say that the entire thing is not very well designed and it can be extremely buggy. I highly recommend using it's successor, the RecyclerView, whenever you can.

这篇关于滚动项目时,Android listSelector仍部分可见的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆