简体   繁体   English

Android Checkable ListView项保持选中状态

[英]Android Checkable ListView item remains checked

The problem: When I clear and repopulate my ListView, if an item was previously checked, the item at the same index is checked as well. 问题:当我清除并重新填充ListView时,如果以前检查过某个项目,那么也会检查同一索引处的项目。 If the number of elements added to the adapter < the index of the previously checked item, nothing is checked. 如果添加到适配器的元素数<先前检查项目的索引,则不检查任何内容。

I've tried blocking the use of convertView even if it exists but that has no effect. 我试过阻止convertView的使用,即使它存在但也没有效果。 Strangely, when I stepped through the debugger to see what is going on and I see that repopulating the adapter results in one or more calls to setChecked(true) in CheckableLinearLayout. 奇怪的是,当我逐步调试程序以查看发生了什么情况时,我发现重新填充适配器会导致对CheckableLinearLayout中的setChecked(true)进行一个或多个调用。 (I've included that stack trace below) Looking at the call stack for these invocations shows that its not any code that I wrote originating these calls. (我在下面包括了该堆栈跟踪信息)查看这些调用的调用堆栈表明,它不是我编写的源自这些调用的任何代码。 It's as though the ListView is remembering the index of the previous selection and restoring it on it's own. 就像ListView记住前一个选择的索引并自行恢复一样。

My implementation: I've got a ListView set to singleSelection mode. 我的实现:我将ListView设置为singleSelection模式。 I use a custom ArrayAdapter that has a getView(...) which returns instances of my CheckableListView implementation: 我使用一个具有getView(...)的自定义ArrayAdapter,它返回我的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);
   }
}

My ArrayAdapter's getView implementation: 我的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;
      }
   }

Any ideas? 有任何想法吗?

PS. PS。 Here's the stack trace of one of the mystery calls to setChecked(true): 这是对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)

You can use this CheckableLinearLayout 您可以使用此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 is overriden - so it changes it's status on click; performClick被覆盖-因此它会在单击时更改其状态; remove it if you don't need it 如果不需要,请将其删除

for background color you can use something like this: 对于背景色,您可以使用如下所示的内容:

<?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>

This is because every time when you move your items in listview then it recreates its each row but as you have not saved the state so on recreation it removes its state and set it to its default state. 这是因为每次在列表视图中移动项目时,它都会重新创建其每一行,但是由于您尚未保存状态,因此在重新创建时会删除其状态并将其设置为默认状态。 I have already posted an answer related to that you can check hope it will be helpful. 我已经发布了一个与之相关的答案,您可以检查希望对您有帮助。

How to implement a button that gets all checkbox's state and adds the value of checked item into arraylist? 如何实现一个获取所有复选框状态并将选中项的值添加到arraylist的按钮?

setChecked would be called (if there is Checkable interface) by ListView with value from getCheckedItemPosition(s) (set with ListView setItemChecked) after getView. ListView将使用getViewed之后的getCheckedItemPosition(用ListView setItemChecked设置)中的值来调用setChecked(如果有Checkable接口)。 Maybe internal container is not cleared when the adapter is cleared or changed. 清除或更改适配器时,可能未清除内部容器。 Uncheck the item when required. 需要时取消选中该项目。

I suggest to use state list drawable as well. 我建议也使用可绘制状态列表。 onCreateDrawableState, mergeDrawableState, refreshDrawableState, state_checked. onCreateDrawableState,mergeDrawableState,refreshDrawableState,state_checked。 Just google. 只是谷歌。

Here is a good implementation of CheckableRelativeLayout : link 这是CheckableRelativeLayout的良好实现: link

Also, setting listView SINGLE_CHOICE (or multiple) in layout/xml didn't work for me, used this instead after setting adapter 另外,在layout / xml中设置listView SINGLE_CHOICE(或多个)对我不起作用,而是在设置适配器后使用它

    myListView.setChoiceMode(AbsListView.CHOICE_MODE_SINGLE);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM