为什么当带有 CheckBox 的 ListView 行离开屏幕时不更新数据模型?
Why data model is not updated when ListView row with a CheckBox gets off-screen?
虽然这是我想要的行为,但有人可以解释一下为什么会这样吗?
我有一个 ListView
,其中包含一个 CheckBox
和一个 TextView
的自定义行。这是行布局代码:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="match_parent"
android:layout_width="match_parent"
android:orientation="horizontal">
<CheckBox
android:id="@+id/shop_list_checkbox"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/shop_list_textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Sample text"
/>
</LinearLayout>
然后我有了这个简单的数据模型(省略了一些方法和字段),基本上是一个有名称和选择状态的商店:
public class ShopListItem {
//name of the shop
private String name;
//shop selection state
private boolean isSelected;
public ShopListItem(String shopKey, String name, boolean isSelected) {
this.shopKey = shopKey;
this.name = name;
this.isSelected = isSelected;
}
public boolean isSelected() {
return isSelected;
}
public void setSelected(boolean selectionState) {
this.isSelected = selectionState;
}
}
这是我自定义的相关代码 ArrayAdapter
:
public View getView(int position, View convertView, @NonNull ViewGroup parent) {
// Temporary view to be returned as converterView
View view;
// Checking if view is reused or not
if (convertView == null) { //new view is generated
// Inflate new view and associate its views with viewHodler
view = inflater.inflate(R.layout.row_shop_list, parent, false);
final ViewHolder viewHolder = new ViewHolder();
viewHolder.shopNameTextView = (TextView) view.findViewById(R.id.shop_list_textView);
viewHolder.checkBox = (CheckBox) view.findViewById(R.id.shop_list_checkbox);
// Asign check change listener
viewHolder.checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
Log.i("i", "isChecked value is: "+isChecked+", buttonView.isChecked() value is: "+buttonView.isChecked());
// Get reference to the shop (data model) of checked row
ShopListItem shop = (ShopListItem) viewHolder.checkBox.getTag();
// Update selection state in data model
shop.setSelected(buttonView.isChecked());
listSelections(shopsArrayList);
}
});
// Store viewHolder inside temporary view
view.setTag(viewHolder);
// Store reference to a shop (data model) of this row in the checkbox reference
viewHolder.checkBox.setTag(shopsArrayList.get(position));
} else { //existing view reused
// Assign view from adapter to temporary view
view = convertView;
// Get viewholder from view which was stored while generating new view
ViewHolder storedHolder = (ViewHolder) view.getTag();
//Store reference to a shop (data model) of this row in the checkbox reference
storedHolder.checkBox.setTag(shopsArrayList.get(position));
}
// Get viewholder from view (from new view or converview)
ViewHolder holder = (ViewHolder) view.getTag();
// Populate holder references with data from shops array list
holder.shopNameTextView.setText(shopsArrayList.get(position).getName());
holder.checkBox.setChecked(shopsArrayList.get(position).isSelected());
//return view
return view;
}
谜底在于OnCheckedChangeListener
。每当我单击第一行的复选框时,侦听器都会收到正确的状态并更新商店数组列表的第一项(为真或假)。但是,如果我选中第一个复选框并向下滚动列表,一旦第一行离开屏幕,就会以状态 false
调用 OnCheckedChangeListener
,但商店数组列表的第一项不会更新至 false
。为什么会这样?为什么它只在单击框时更新,而不是在隐藏调用时不更新?
在您的数据列表中添加一个标志以维护选中的数据项>
public boolean isSelected() {
return selected;
}
public void setSelected(boolean selected) {
this.selected = selected;
}
并在 Adapter 的 getView() 中为复选框添加代码
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
Log.v("ConvertView", String.valueOf(position));
if (convertView == null) {
LayoutInflater vi = (LayoutInflater)getSystemService(
Context.LAYOUT_INFLATER_SERVICE);
convertView = vi.inflate(R.layout.country_info, null);
holder = new ViewHolder();
holder.code = (TextView) convertView.findViewById(R.id.code);
holder.name = (CheckBox) convertView.findViewById(R.id.checkBox1);
convertView.setTag(holder);
holder.name.setOnClickListener( new View.OnClickListener() {
public void onClick(View v) {
CheckBox cb = (CheckBox) v ;
Country country = (Country) cb.getTag();
Toast.makeText(getApplicationContext(),
"Clicked on Checkbox: " + cb.getText() +
" is " + cb.isChecked(),
Toast.LENGTH_LONG).show();
country.setSelected(cb.isChecked());
}
});
}
else {
holder = (ViewHolder) convertView.getTag();
}
Country country = countryList.get(position);
holder.code.setText(" (" + country.getCode() + ")");
holder.name.setText(country.getName());
holder.name.setChecked(country.isSelected());
holder.name.setTag(country);
return convertView;
}
更多信息请见下文Link:
http://www.mysamplecode.com/2012/07/android-listview-checkbox-example.html
当您滚动列表时,第一项离开屏幕。当项目关闭时,将调用 getView 并使用视图。您需要保持该特定对象的状态才能正确显示在 UI 级别
只要您点击复选框,OnCheckedChangeListener
就会被激活。当您向下或向上滚动时,将调用 OTHER OnCheckedChangeListener
以显示屏幕上的 NEW ROW。隐藏线的OnCheckedChangeListener
没有被调用
在你的情况下,我想,你正在将新行的复选框值显示到屏幕上,这让你感到困惑。尝试区分 OnCheckedChangeListener's
行中引用的商店对象:
ShopListItem shop = (ShopListItem) viewHolder.checkBox.getTag();
虽然这是我想要的行为,但有人可以解释一下为什么会这样吗?
我有一个 ListView
,其中包含一个 CheckBox
和一个 TextView
的自定义行。这是行布局代码:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="match_parent"
android:layout_width="match_parent"
android:orientation="horizontal">
<CheckBox
android:id="@+id/shop_list_checkbox"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/shop_list_textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Sample text"
/>
</LinearLayout>
然后我有了这个简单的数据模型(省略了一些方法和字段),基本上是一个有名称和选择状态的商店:
public class ShopListItem {
//name of the shop
private String name;
//shop selection state
private boolean isSelected;
public ShopListItem(String shopKey, String name, boolean isSelected) {
this.shopKey = shopKey;
this.name = name;
this.isSelected = isSelected;
}
public boolean isSelected() {
return isSelected;
}
public void setSelected(boolean selectionState) {
this.isSelected = selectionState;
}
}
这是我自定义的相关代码 ArrayAdapter
:
public View getView(int position, View convertView, @NonNull ViewGroup parent) {
// Temporary view to be returned as converterView
View view;
// Checking if view is reused or not
if (convertView == null) { //new view is generated
// Inflate new view and associate its views with viewHodler
view = inflater.inflate(R.layout.row_shop_list, parent, false);
final ViewHolder viewHolder = new ViewHolder();
viewHolder.shopNameTextView = (TextView) view.findViewById(R.id.shop_list_textView);
viewHolder.checkBox = (CheckBox) view.findViewById(R.id.shop_list_checkbox);
// Asign check change listener
viewHolder.checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
Log.i("i", "isChecked value is: "+isChecked+", buttonView.isChecked() value is: "+buttonView.isChecked());
// Get reference to the shop (data model) of checked row
ShopListItem shop = (ShopListItem) viewHolder.checkBox.getTag();
// Update selection state in data model
shop.setSelected(buttonView.isChecked());
listSelections(shopsArrayList);
}
});
// Store viewHolder inside temporary view
view.setTag(viewHolder);
// Store reference to a shop (data model) of this row in the checkbox reference
viewHolder.checkBox.setTag(shopsArrayList.get(position));
} else { //existing view reused
// Assign view from adapter to temporary view
view = convertView;
// Get viewholder from view which was stored while generating new view
ViewHolder storedHolder = (ViewHolder) view.getTag();
//Store reference to a shop (data model) of this row in the checkbox reference
storedHolder.checkBox.setTag(shopsArrayList.get(position));
}
// Get viewholder from view (from new view or converview)
ViewHolder holder = (ViewHolder) view.getTag();
// Populate holder references with data from shops array list
holder.shopNameTextView.setText(shopsArrayList.get(position).getName());
holder.checkBox.setChecked(shopsArrayList.get(position).isSelected());
//return view
return view;
}
谜底在于OnCheckedChangeListener
。每当我单击第一行的复选框时,侦听器都会收到正确的状态并更新商店数组列表的第一项(为真或假)。但是,如果我选中第一个复选框并向下滚动列表,一旦第一行离开屏幕,就会以状态 false
调用 OnCheckedChangeListener
,但商店数组列表的第一项不会更新至 false
。为什么会这样?为什么它只在单击框时更新,而不是在隐藏调用时不更新?
在您的数据列表中添加一个标志以维护选中的数据项>
public boolean isSelected() {
return selected;
}
public void setSelected(boolean selected) {
this.selected = selected;
}
并在 Adapter 的 getView() 中为复选框添加代码
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
Log.v("ConvertView", String.valueOf(position));
if (convertView == null) {
LayoutInflater vi = (LayoutInflater)getSystemService(
Context.LAYOUT_INFLATER_SERVICE);
convertView = vi.inflate(R.layout.country_info, null);
holder = new ViewHolder();
holder.code = (TextView) convertView.findViewById(R.id.code);
holder.name = (CheckBox) convertView.findViewById(R.id.checkBox1);
convertView.setTag(holder);
holder.name.setOnClickListener( new View.OnClickListener() {
public void onClick(View v) {
CheckBox cb = (CheckBox) v ;
Country country = (Country) cb.getTag();
Toast.makeText(getApplicationContext(),
"Clicked on Checkbox: " + cb.getText() +
" is " + cb.isChecked(),
Toast.LENGTH_LONG).show();
country.setSelected(cb.isChecked());
}
});
}
else {
holder = (ViewHolder) convertView.getTag();
}
Country country = countryList.get(position);
holder.code.setText(" (" + country.getCode() + ")");
holder.name.setText(country.getName());
holder.name.setChecked(country.isSelected());
holder.name.setTag(country);
return convertView;
}
更多信息请见下文Link:
http://www.mysamplecode.com/2012/07/android-listview-checkbox-example.html
当您滚动列表时,第一项离开屏幕。当项目关闭时,将调用 getView 并使用视图。您需要保持该特定对象的状态才能正确显示在 UI 级别
只要您点击复选框,OnCheckedChangeListener
就会被激活。当您向下或向上滚动时,将调用 OTHER OnCheckedChangeListener
以显示屏幕上的 NEW ROW。隐藏线的OnCheckedChangeListener
没有被调用
在你的情况下,我想,你正在将新行的复选框值显示到屏幕上,这让你感到困惑。尝试区分 OnCheckedChangeListener's
行中引用的商店对象:
ShopListItem shop = (ShopListItem) viewHolder.checkBox.getTag();