4

对于我的Android项目,我有一个listview,每个项目都有一个复选框。数据通过使用CursorAdapter类从SQLite数据库加载。但是,每当我滚动时,复选框的位置就会移动并转到列表视图的下一部分。我该如何解决这个问题? GIF of my CheckBox Problem 这里是我的光标适配器类别:使用CursorAdapter在列表视图中维护复选框状态

public class VocabCursorAdapter extends CursorAdapter { 

private static final int DIFFICULT = 0; 
private static final int FAMILIAR = 1; 
private static final int EASY = 2; 
private static final int PERFECT = 3; 

public VocabCursorAdapter(Context context, Cursor c, int flags) { 
    super(context, c, 0); 
} 

@Override 
public View newView(Context context, Cursor cursor, ViewGroup parent) { 
    return LayoutInflater.from(context).inflate(R.layout.item_vocab, parent, false); 
} 

@Override 
public void bindView(View view, Context context, Cursor cursor) { 
    // Find fields to populate in inflated template 
    TextView tvVocabName = (TextView) view.findViewById(R.id.vocabName); 
    TextView tvVocabDefinition = (TextView) view.findViewById(R.id.vocabDefinition); 
    ImageView tvVocabLevel = (ImageView) view.findViewById(R.id.vocabLevel); 
    // Extract properties from cursor 
    String vocab = cursor.getString(cursor.getColumnIndexOrThrow(VocabDbContract.COLUMN_NAME_VOCAB)); 
    String definition = cursor.getString(cursor.getColumnIndexOrThrow(VocabDbContract.COLUMN_NAME_DEFINITION)); 
    int level = cursor.getInt(cursor.getColumnIndexOrThrow(VocabDbContract.COLUMN_NAME_LEVEL)); 
    // Populate fields with extracted properties 
    tvVocabName.setText(vocab); 
    tvVocabDefinition.setText(definition); 

    if (level == DIFFICULT) { 
     tvVocabLevel.setImageResource(R.drawable.level_bars_difficult); 
     tvVocabLevel.setTag(DIFFICULT); 
    } 
    else if (level == FAMILIAR) { 
     tvVocabLevel.setImageResource(R.drawable.level_bars_familiar); 
     tvVocabLevel.setTag(FAMILIAR); 
    } 
    else if (level == EASY) { 
     tvVocabLevel.setImageResource(R.drawable.level_bars_easy); 
     tvVocabLevel.setTag(EASY); 
    } 
    else if (level == PERFECT) { 
     tvVocabLevel.setImageResource(R.drawable.level_bars_perfect); 
     tvVocabLevel.setTag(PERFECT); 
    } 
} 

这是我的列表项XML,item_vocab.xml:

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

<ImageView 
    android:layout_width="36sp" 
    android:layout_height="36sp" 
    android:id="@+id/vocabLevel" 
    android:layout_gravity="right" 
    android:src="@drawable/level_bars" 
    android:scaleType="fitXY" 
    android:contentDescription="@string/vocab_level" 
    android:layout_centerVertical="true" 
    android:layout_toLeftOf="@+id/editCheckbox" 
    android:layout_toStartOf="@+id/editCheckbox"/> 

<TextView 
    android:layout_width="wrap_content" 
    android:layout_height="wrap_content" 
    android:textAppearance="?android:attr/textAppearanceLarge" 
    android:text="Large Text" 
    android:id="@+id/vocabName" 
    android:layout_alignParentTop="true" 
    android:layout_alignParentLeft="true" 
    android:layout_alignParentStart="true" 
    android:layout_toLeftOf="@+id/vocabLevel" 
    android:layout_toStartOf="@+id/vocabLevel"/> 

<TextView 
    android:layout_width="match_parent" 
    android:layout_height="wrap_content" 
    android:textAppearance="?android:attr/textAppearanceSmall" 
    android:text="Small Text" 
    android:id="@+id/vocabDefinition" 
    android:layout_alignParentLeft="true" 
    android:layout_alignParentStart="true" 
    android:layout_toLeftOf="@+id/vocabLevel" 
    android:layout_toStartOf="@+id/vocabLevel" 
    android:layout_below="@id/vocabName"/> 

<CheckBox 
    android:layout_width="wrap_content" 
    android:layout_height="wrap_content" 
    android:id="@+id/editCheckbox" 
    android:layout_centerVertical="true" 
    android:layout_alignParentRight="true" 
    android:layout_alignParentEnd="true"/> 

</RelativeLayout> 

这是我的XML其中包含一个ListView

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 
      xmlns:tools="http://schemas.android.com/tools" 
      android:layout_width="match_parent" 
      android:layout_height="match_parent" 
      tools:context=".controller.MyVocab" 
      android:paddingLeft="5dp"> 

<ListView 
    android:layout_width="fill_parent" 
    android:layout_height="wrap_content" 
    android:id="@+id/mVocabList" 
    android:layout_alignParentTop="true" 
    android:layout_alignParentLeft="true" 
    android:layout_alignParentStart="true"/> 

<TextView 
    android:layout_width="match_parent" 
    android:layout_height="wrap_content" 
    android:text="@string/empty_text_view" 
    android:id="@android:id/empty" 
    android:layout_alignParentTop="true" 
    android:layout_alignParentLeft="true" 
    android:layout_alignParentStart="true"/> 

</RelativeLayout> 

我在StackOverflow上看过很多不同的解决方案,但我无法在我自己的应用程序中成功完成。举个例子,这个post有一个类似的问题,但是它的解决方案使用了getView,而且我很难理解如何用newView和bindView来实现它。

还有一些其他解决方案可能是不涉及cursoradapter的示例。任何帮助非常感谢,非常感谢! 编辑#1:在合并Phan的变化后,当我滚动列表视图(见GIF)时,复选框状态将重置为false,而不是保持其状态。

回答

3

原因: ListView中重新使用的意见。

解决方案:

class VocabCursorAdapter extends CursorAdapter { 
    List<Integer> selectedItemsPositions;//to store all selected items position 

    public VocabCursorAdapter(Context context, Cursor c,int flags) { 
     super(context, c,0); 
     selectedItemsPositions = new ArrayList<>(); 
    } 

    @Override 
    public View newView(Context context, Cursor cursor, ViewGroup viewGroup) { 
     View view = LayoutInflater.from(context).inflate(R.layout.item_vocab, viewGroup, false); 
     CheckBox box = (CheckBox) view.findViewById(R.id.editCheckbox); 
     box.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { 
      @Override 
      public void onCheckedChanged(CompoundButton compoundButton, boolean b) { 
       int position = (int) compoundButton.getTag(); 
       if (b) { 
        //check whether its already selected or not 
        if (!selectedItemsPositions.contains(position)) 
         selectedItemsPositions.add(position); 
       } else { 
        //remove position if unchecked checked item 
        selectedItemsPositions.remove((Object) position); 
       } 
      } 
     }); 
     return view; 
    } 

    @Override 
    public void bindView(View view, Context context, Cursor cursor) { 

     //your other stuff 

     CheckBox box = (CheckBox) view.findViewById(R.id.editCheckbox); 
     box.setTag(cursor.getPosition()); 

     if (selectedItemsPositions.contains(cursor.getPosition())) 
      box.setChecked(true); 
     else 
      box.setChecked(false); 
    } 
} 

happyCoding;

+0

谢谢skadoosh!它现在有效! –

1

试试这个

public class VocabCursorAdapter extends CursorAdapter { 

    private ArrayList<Boolean> itemChecked = new ArrayList<Boolean>(); // array list for store state of each checkbox 

    public VocabCursorAdapter(Context context, Cursor c, int flags) { 

     for (int i = 0; i < c.getCount(); i++) { // c.getCount() return total number of your Cursor 
      itemChecked.add(i, false); // initializes all items value with false 
     } 
    } 

    @Override 
    public void bindView(View view, Context context, Cursor cursor) { 

     ... 
     final int position = cursor.getPosition(); // get position by cursor 

     CheckBox checkBox = (CheckBox) view.findViewById(R.id.editCheckbox); 
     checkBox.setOnClickListener(new OnClickListener() { 
       public void onClick(View v) { 

        if (itemChecked.get(position) == true) { // if current checkbox is checked, when you click -> change it to false 
         itemChecked.set(position, false); 
        } else { 
         itemChecked.set(position, true); 
        } 
       } 
     }); 

     checkBox.setChecked(itemChecked.get(position)); // set the checkbox state base on arraylist object state 
     Log.i("In VocabCursorAdapter","position: "+position+" - checkbox state: "+itemChecked.get(position)); 
    } 
} 
+0

你好潘先生,非常感谢你的帮助!我已经整合了您的更改,但是,当我将其滚动到新视图而不是保持其状态时,复选框状态现在会重置为false/empty。 –

+0

我在将问题中的更改合并后,附加了GIF。 –

+0

@CharlesLi我添加了1行代码在我的文章中打印'logcat'。将其添加到您的代码。 之后,你能告诉我哪个位置checkbox state = false logcat –