【问题标题】:Android Checkable ListView item remains checkedAndroid Checkable ListView 项目保持选中状态
【发布时间】:2014-12-10 23:24:47
【问题描述】:

问题: 当我清除并重新填充我的 ListView 时,如果之前检查过某个项目,那么也会检查同一索引处的项目。如果添加到适配器的元素数量

我尝试阻止使用 convertView,即使它存在但没有效果。奇怪的是,当我逐步通过调试器查看发生了什么时,我看到重新填充适配器会导致在 CheckableLinearLayout 中调用一次或多次 setChecked(true)。 (我在下面包含了该堆栈跟踪)查看这些调用的调用堆栈表明它不是我编写的任何发起这些调用的代码。就好像 ListView 正在记住先前选择的索引并自行恢复它。

我的实现: 我将 ListView 设置为 singleSelection 模式。我使用了一个自定义的 ArrayAdapter,它有一个 getView(...),它返回我的 CheckableListView 实现的实例:

public class CheckableLinearLayout extends LinearLayout implements Checkable {

   private boolean isChecked;

   public CheckableLinearLayout(Context context) {
      super(context);
   }

   public CheckableLinearLayout(Context context, AttributeSet attrs) {
      super(context, attrs);
   }

   @Override
   public void setChecked(boolean isChecked) {
      this.isChecked = isChecked;
      if(isChecked) {
         setBackgroundColor(Color.DKGRAY);
      } else {
         setBackgroundColor(Color.BLACK);
      }
   }

   @Override
   public boolean isChecked() {
      return isChecked;
   }

   @Override
   public void toggle() {
      setChecked(!isChecked);
   }
}

我的 ArrayAdapter 的 getView 实现:

@Override
public View getView(int pos, View convertView, ViewGroup parent) {

         LayoutInflater inf = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);

         CheckableLinearLayout row = (CheckableLinearLayout) convertView;
         if (row == null) {
            row = (CheckableLinearLayout) inf.inflate(R.layout.file_explorer_item, parent, false);
         }

         // I explicitly set checked to false, in case this was a convertView:
         row.setChecked(false);

         // population various elements of the view is done here

         return row;
      }
   }

有什么想法吗?

PS。这是对 setChecked(true) 的一个神秘调用的堆栈跟踪:

<1> main@830013232224, prio=5, in group 'main', status: 'RUNNING'
      at my.package.CheckableLinearLayout.setChecked(CheckableLinearLayout.java:25)
      at android.widget.ListView.setupChild(ListView.java:1834)
      at android.widget.ListView.makeAndAddView(ListView.java:1765)
      at android.widget.ListView.fillSpecific(ListView.java:1318)
      at android.widget.ListView.layoutChildren(ListView.java:1636)
      at android.widget.AbsListView.onLayout(AbsListView.java:1863)
      at android.view.View.layout(View.java:11278)
      at android.view.ViewGroup.layout(ViewGroup.java:4224)
      at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1628)
      at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1486)
      at android.widget.LinearLayout.onLayout(LinearLayout.java:1399)
      at android.view.View.layout(View.java:11278)
      at android.view.ViewGroup.layout(ViewGroup.java:4224)
      at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1628)
      at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1486)
      at android.widget.LinearLayout.onLayout(LinearLayout.java:1399)
      at android.view.View.layout(View.java:11278)
      at android.view.ViewGroup.layout(ViewGroup.java:4224)
      at android.widget.FrameLayout.onLayout(FrameLayout.java:431)
      at android.view.View.layout(View.java:11278)
      at android.view.ViewGroup.layout(ViewGroup.java:4224)
      at android.widget.FrameLayout.onLayout(FrameLayout.java:431)
      at android.view.View.layout(View.java:11278)
      at android.view.ViewGroup.layout(ViewGroup.java:4224)
      at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1628)
      at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1486)
      at android.widget.LinearLayout.onLayout(LinearLayout.java:1399)
      at android.view.View.layout(View.java:11278)
      at android.view.ViewGroup.layout(ViewGroup.java:4224)
      at android.widget.FrameLayout.onLayout(FrameLayout.java:431)
      at android.view.View.layout(View.java:11278)
      at android.view.ViewGroup.layout(ViewGroup.java:4224)
      at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1628)
      at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1486)
      at android.widget.LinearLayout.onLayout(LinearLayout.java:1399)
      at android.view.View.layout(View.java:11278)
      at android.view.ViewGroup.layout(ViewGroup.java:4224)
      at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1628)
      at android.widget.LinearLayout.layoutHorizontal(LinearLayout.java:1617)
      at android.widget.LinearLayout.onLayout(LinearLayout.java:1401)
      at android.view.View.layout(View.java:11278)
      at android.view.ViewGroup.layout(ViewGroup.java:4224)
      at android.widget.FrameLayout.onLayout(FrameLayout.java:431)
      at android.view.View.layout(View.java:11278)
      at android.view.ViewGroup.layout(ViewGroup.java:4224)
      at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1628)
      at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1486)
      at android.widget.LinearLayout.onLayout(LinearLayout.java:1399)
      at android.view.View.layout(View.java:11278)
      at android.view.ViewGroup.layout(ViewGroup.java:4224)
      at android.widget.FrameLayout.onLayout(FrameLayout.java:431)
      at android.view.View.layout(View.java:11278)
      at android.view.ViewGroup.layout(ViewGroup.java:4224)
      at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1489)
      at android.view.ViewRootImpl.handleMessage(ViewRootImpl.java:2442)
      at android.os.Handler.dispatchMessage(Handler.java:99)
      at android.os.Looper.loop(Looper.java:137)
      at android.app.ActivityThread.main(ActivityThread.java:4424)
      at java.lang.reflect.Method.invokeNative(Method.java:-1)
      at java.lang.reflect.Method.invoke(Method.java:511)
      at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:784)
      at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:551)
      at dalvik.system.NativeStart.main(NativeStart.java:-1)

<10> Binder Thread #2@830019920808, prio=5, in group 'main', status: 'RUNNING'
      at dalvik.system.NativeStart.run(NativeStart.java:-1)

<9> Binder Thread #1@830019917952, prio=5, in group 'main', status: 'RUNNING'
      at dalvik.system.NativeStart.run(NativeStart.java:-1)

<8> FinalizerWatchdogDaemon@830019904352 daemon, prio=5, in group 'main', status: 'SLEEPING'
      at java.lang.VMThread.sleep(VMThread.java:-1)
      at java.lang.Thread.sleep(Thread.java:1031)
      at java.lang.Thread.sleep(Thread.java:1013)
      at java.lang.Daemons$FinalizerWatchdogDaemon.run(Daemons.java:213)
      at java.lang.Thread.run(Thread.java:856)

<7> FinalizerDaemon@830019904008 daemon, prio=5, in group 'main', status: 'WAIT'
      at java.lang.Object.wait(Object.java:-1)
      at java.lang.Object.wait(Object.java:401)
      at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:102)
      at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:73)
      at java.lang.Daemons$FinalizerDaemon.run(Daemons.java:168)
      at java.lang.Thread.run(Thread.java:856)

<6> ReferenceQueueDaemon@830019903648 daemon, prio=5, in group 'main', status: 'WAIT'
      at java.lang.Object.wait(Object.java:-1)
      at java.lang.Object.wait(Object.java:364)
      at java.lang.Daemons$ReferenceQueueDaemon.run(Daemons.java:128)
      at java.lang.Thread.run(Thread.java:856)

<5> Compiler@830019903408 daemon, prio=5, in group 'system', status: 'WAIT'
      at dalvik.system.NativeStart.run(NativeStart.java:-1)

<3> Signal Catcher@830019902928 daemon, prio=5, in group 'system', status: 'WAIT'
      at dalvik.system.NativeStart.run(NativeStart.java:-1)

<2> GC@830019902704 daemon, prio=5, in group 'system', status: 'WAIT'
      at dalvik.system.NativeStart.run(NativeStart.java:-1)

【问题讨论】:

  • Afaik setChecked 将由 ListView 调用(如果有 Checkable 接口),其值来自 getCheckedItemPosition(s)(使用 ListView setItemChecked 设置)在 getView 之后。清除或更改适配器时,可能未清除内部容器。需要时取消选中该项目。
  • 您是否在您的ListView 中调用setChoiceMode(ListView.CHOICE_MODE_SINGLE);?根据我的经验,CHOICE_SINGLE_MODE 中的 ListView 会自行处理此类问题。
  • @Longerian:不——我在应用于我的 ListView 的 xml 样式中这样做。应用的样式如下所示: I知道有报告称通过 XML 设置时此设置被忽略,但快速测试表明,如果我将其注释掉,则不会启用单选模式,并且单击时不会检查项目。
  • @Deucalion:我认为你正在做某事!重新填充时,我添加了以下内容,并且事情似乎按预期工作: int checkedPos = getListView().getCheckedItemPosition(); getListView().setItemChecked(checkedPos, false);
  • 这是官方的 - 成功了!谢谢Deucalion。如果您想将您的评论变成答案,我会接受。

标签: java android listview


【解决方案1】:

你可以使用这个 CheckableLinearLayout

public class CheckableLinearLayout extends LinearLayout implements Checkable {
boolean mChecked = false;

private static final int[] CHECKED_STATE_SET = {
        R.attr.state_checked
};

public CheckableLinearLayout(Context context, AttributeSet attrs) {
    super(context, attrs);
}

@Override
public boolean isChecked() {
    return mChecked;
}

@Override
public void setChecked(boolean checked) {
    if (mChecked != checked) {
        mChecked = checked;
        refreshDrawableState();
    }
}

@Override
public void toggle() {
    mChecked = !mChecked;
    refreshDrawableState();
}

@Override
protected int[] onCreateDrawableState(int extraSpace) {
    final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
    if (isChecked()) {
        mergeDrawableStates(drawableState, CHECKED_STATE_SET);
    }
    return drawableState;
}

@Override
public boolean performClick() {
    toggle();
    return super.performClick();
}

}

performClick 被覆盖 - 因此它会在点击时更改其状态;如果你不需要它就删除它

对于背景颜色,您可以使用以下内容:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_checked="true">
        <shape android:shape="rectangle">
            <solid android:color="#e13f2e"/>
        </shape>
    </item>
    <item android:state_checked="false">
        <shape android:shape="rectangle">
            <solid android:color="#ffffff"/>
        </shape>
    </item>
</selector>

【讨论】:

  • 遇到错误:state_checked cannot be resolved or is not a field
  • @LuisA.Florit 试试 android.R.attr.state_checked
【解决方案2】:

setChecked 将由 ListView 调用(如果有 Checkable 接口),其值来自 getCheckedItemPosition(s)(使用 ListView setItemChecked 设置)在 getView 之后。当适配器被清除或更改时,可能没有清除内部容器。需要时取消选中该项目。

我建议也使用可绘制的状态列表。 onCreateDrawableState、mergeDrawableState、refreshDrawableState、state_checked。只是谷歌。

【讨论】:

    【解决方案3】:

    这是因为每次在列表视图中移动项目时,它都会重新创建其每一行,但由于您尚未保存状态,因此在重新创建时它会删除其状态并将其设置为默认状态。我已经发布了一个相关的答案,您可以查看希望它会有所帮助。

    How to implement a button that gets all checkbox's state and adds the value of checked item into arraylist?

    【讨论】:

    • 我的问题实际上是相反的:我不希望列表视图在重新创建时保留状态。此外,isChecked 的默认状态是 false(布尔值的默认值),最重要的是,我的 getView() 实现明确将其设置为 false,以防使用回收视图。
    • 如果您不想保留或保存您的状态,我无法理解您想要做什么,那么您无需保存,只需相应地设置即可。
    • 我想说我正在相应地设置它,但有些东西正在覆盖我正在设置的内容。调试器显示正在调用 setChecked(true),尽管我不知道。
    • 在所有情况下,您都需要保存您的状态,因为如果它在列表视图中重新创建行,那么它还会随机设置您的复选框状态。
    • 这就是为什么我在 getView() 中明确调用 setChecked(false)。
    【解决方案4】:

    这是一个很好的 CheckableRelativeLayout 实现:link

    另外,在 layout/xml 中设置 listView SINGLE_CHOICE(或多个)对我不起作用,在设置适配器后改用它

        myListView.setChoiceMode(AbsListView.CHOICE_MODE_SINGLE);
    

    【讨论】:

      猜你喜欢
      • 2011-08-06
      • 2011-03-07
      • 1970-01-01
      • 1970-01-01
      • 2018-10-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多