Android Checkable ListView项目仍保持选中状态

时间:2012-04-25 05:11:34

标签: java android listview

问题: 清除并重新填充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)

4 个答案:

答案 0 :(得分:5)

您可以使用此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>

答案 1 :(得分:0)

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

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

答案 2 :(得分:0)

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

我建议使用状态列表drawable。 onCreateDrawableState,mergeDrawableState,refreshDrawableState,state_checked。只是谷歌。

答案 3 :(得分:0)

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

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

    myListView.setChoiceMode(AbsListView.CHOICE_MODE_SINGLE);