当项目被滚动出去时,Android listSelector仍然部分可见

当项目被滚动出去时,Android listSelector仍然部分可见,android,listview,Android,Listview,该图显示了问题:Venus项目被滚动出来,但其选择是可见的 我已经看过了,我正在使用建议的形状/梯度,但仍然没有运气 设备:安卓MS VS Emulator,安卓4.2 XHDPI API-17 IDE:androidstudio2。在Windows7上 布局、主要活动 <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/r

该图显示了问题:
Venus
项目被滚动出来,但其选择是可见的

我已经看过了,我正在使用建议的形状/梯度,但仍然没有运气

设备:安卓MS VS Emulator,安卓4.2 XHDPI API-17
IDE:androidstudio2。在Windows7上

布局、主要活动

<?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>
其中,
行星
只是一组字符串(太阳、水星、金星、地球、火星、木星……),而
我的项目
只是一个具有自定义高度的
文本视图

请问我哪里错了

编辑
为了澄清这个问题,它是关于默认列表选择行为的。这是关于没有任何属性的列表选择器项。不要太注意对应的可绘制名称。我准备将所选的
@drawable/item_重命名为
@drawable/item_默认值
。让我知道这是否有助于澄清问题,我会重新命名它

tl;dr不要在列表选择器上设置默认可绘制

当您为列表选择器提供默认的可绘制选项时,会出现此问题。我的意思是,在列表选择器定义中,您有一个没有状态要求的
item
标记,使得该
item
无意中成为默认值。您可以阅读有关选择器的更多信息

您的列表选择器代码:

<?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>
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"/>
进一步阅读

从Android的文档来看,我所说的并不完全是真的,所以你可能会问我的答案是否可信。本节专门针对那些寻求可信答案的人和非常好奇的人

为了理解列表选择器是如何工作的以及它是如何编程的,我们需要深入了解Android源代码。首先,
ListView
的逻辑实质实际上保存在一个名为
AbsListView
的类中(如果您没有下载源代码,可以参考)。深入研究此类的源代码,我们将发现一些与选择器相关的有用字段/函数:

  • mSelector
    :这是选择器的可绘制部分(您使用
    android:listSelector
    指定的选择器)
  • mSelectorRect
    :此字段确定选择器的绘制位置以及选择器的大小
  • mSelectedPosition
    :存储所选项目的索引(该字段实际上在类
    AdapterView
    的更深处声明)
  • 位置选择器(…)
    :更新应绘制选择器的位置
  • drawSelector(…)
    :绘制选择器
  • trackMotionScroll(…)
    :包含
    列表视图的滚动行为的逻辑
现在我们已经了解了环境,我们终于可以理解列表选择器行为的核心逻辑了。这一切都归结为
trackMotionScroll(…)
中的这几行代码:

布尔轨迹运动滚动(int deltaY,int incrementalDeltaY){
...
如果(!inTouchMode&&mSelectedPosition!=无效位置){
//如果我们没有处于触摸模式,并且有一个选定的项目,那么
//我们快速检查所选项目是否在屏幕上
最终int childIndex=mSelectedPosition-mFirstPosition;
如果(childIndex>=0&&childIndex=0&&childIndex
上面的源代码片段已从原始代码中编辑,以包含注释

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

因此,当您滚动
ListView
并且所选项目离开屏幕时,列表选择器将保持在所选项目解释所观察到的重影列表选择器的最后位置

最后的想法


从ListView的介绍开始,我不得不说整个东西设计得不是很好,可能会有很多问题。我强烈建议您尽可能使用它的后续版本,即回收视图。

也许您可以尝试在ListView中添加android:layerType=“software”

<ListView
    android:layerType="software"
    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>


我的选择器的条目措辞似乎有点误导。我真的很想弄明白默认选择器有什么问题,不管我给它取什么名字。如果我将
@drawable/item_selected
重命名为
@drawable/item_default
,这是否有助于澄清问题?如果我的答案不清楚,很抱歉。尝试将您选择的
@drawable/item\的代码更改为我发布的代码。当我写下“当您为列表选择器提供默认背景时,会出现此问题。”我指的是列表选择器的这一行
。因为您没有指定任何常量
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);
    }
}
<?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>
ArrayAdapter adapter = ArrayAdapter.createFromResource(this, 
    R.array.Planets, R.layout.myitem);
<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"/>
<?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);
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();
    }
    ...
}
<ListView
    android:layerType="software"
    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>