Android入门第28天-ListView嵌套CheckBox在滚动时失去选中状态的问题

Posted TGITCIC

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android入门第28天-ListView嵌套CheckBox在滚动时失去选中状态的问题相关的知识,希望对你有一定的参考价值。

问题的描述

来看这个例子,我们有一个ListView,然后在这个ListView内有若干checkbox。

此时ListView有一个问题,即焦点失去的问题,这个问题会体现在什么场景下呢?

我们假设我们这个ListView内的CheckBox有多行,行数多到需要“垂直”滚屏来做翻页显示。

而此时我的操作为:

  1. 先把第一第二行选中;
  2. 然后我滚屏到下一页什么都不做;
  3. 再滚屏到第一页,我们会发觉在第一页之前我勾选的第一第二行的选中状态没了;

这个问题是一个很经典的ListView和内嵌控件抢焦点导致的问题,如以下截屏。

怎么处理呢?

很简单。看以下设计。

设计

  1. 我们设计一个private Map<Integer, Boolean> map = new HashMap<Integer, Boolean>()结构,用于记录每一次checkbox在被点击时当前的状态。此处的Key为Integer即每一行的positon;
  2. 如果是:true那么我们往这个结构内添加。同时当listview产生滚屏翻页动作时,我们把这些状态重新给之前选中状态的checkbox给再次set(true);
  3. 如果是反过来即把选中状态的勾去掉,那么我们就从这个结构中把这条记录去除;

来看核心代码,它位于getView()方法体内

@Override
public View getView(int i, View view, ViewGroup viewGroup) 
    Log.i("app", ">>>>>>into getView method");
    CheckBoxViewHolder vh = null;
    if (view == null) 
        view = LayoutInflater.from(ctx).inflate(R.layout.customized_layout, viewGroup, false);
        vh = new CheckBoxViewHolder();
        vh.colorCheckBox = (CheckBox) view.findViewById(R.id.colorCheckBox);
        view.setTag(vh);
     else 
        vh = (CheckBoxViewHolder) view.getTag();
    
    if (data != null) 
        Log.i("app", ">>>>>>into getView set data");
        vh.colorCheckBox.setText(data.get(i).getDescription());
        vh.colorCheckBox.setChecked(data.get(i).isChecked());
        //checkbox位于listview中当翻页时,checkbox的选中状态会丢失,因此作下面的checkbox是否在前一页被选中时的位置及状态的记录
        final CheckBox checkBox = (CheckBox) view.findViewById(R.id.colorCheckBox);
        checkBox.setOnClickListener(new View.OnClickListener() 
            @Override
            public void onClick(View v) 
                if (checkBox.isChecked()) 
                    map.put(i, true);
 
                 else 
                    map.remove(i);
 
                
            
        );
        //由于在滚动到下一页时原选的选中状态会丢失因此用之前记录的状态重新渲染一下checkbox在滚动发生前是否被选中
        if (map != null && map.containsKey(i)) 
            checkBox.setChecked(true);
         else 
            checkBox.setChecked(false);
        
        return view;
    
    return null;

 

下面给出全代码。

全代码

UI端

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
    android:orientation="vertical"
    tools:context=".MainActivity">
 
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">
        <Button
            android:id="@+id/addItemButton"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="添加一行"/>
        <Button
            android:id="@+id/removeItemButton"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="删除选中行"/>
 
    </LinearLayout>
    <ListView
        android:id="@+id/listView"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</LinearLayout>

customized_layout.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
    android:orientation="vertical"
    tools:context=".MainActivity">
 
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">
        <Button
            android:id="@+id/addItemButton"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="添加一行"/>
        <Button
            android:id="@+id/removeItemButton"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="删除选中行"/>
 
    </LinearLayout>
    <ListView
        android:id="@+id/listView"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</LinearLayout>

 

后端JAVA

CheckBoxBean

package org.mk.android.demo;
 
import java.io.Serializable;
 
public class CheckBoxBean implements Serializable 
 
    public CheckBoxBean(String value,String description,boolean checked)
        this.value=value;
        this.description=description;
        this.checked=checked;
    
    private String value = "";
    private String description = "";
 
    public String getValue() 
        return value;
    
 
    public void setValue(String value) 
        this.value = value;
    
 
    public String getDescription() 
        return description;
    
 
    public void setDescription(String description) 
        this.description = description;
    
 
    public boolean isChecked() 
        return checked;
    
 
    public void setChecked(boolean checked) 
        this.checked = checked;
    
 
    private boolean checked = false;

 CheckBoxAdapter

package org.mk.android.demo;
 
import android.content.Context;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.CheckBox;
 
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
 
public class CheckBoxAdapter extends BaseAdapter 
    private List<CheckBoxBean> data;
    private Context ctx;
    private Map<Integer, Boolean> map = new HashMap<Integer, Boolean>();
 
    @Override
    public int getCount() 
        if (data != null) 
            return data.size();
        
        return 0;
    
    //添加一个元素
    public void add(CheckBoxBean item) 
        if (data == null) 
            data = new ArrayList<CheckBoxBean>();
        
        data.add(item);
        notifyDataSetChanged();
    
    //删除选中元素
    public void remove() 
        for(Iterator it=map.keySet().iterator();it.hasNext();)
            int key=(int)it.next();
            if(map.get(key) ==true)
                it.remove();
                data.remove(key);
            
        
        notifyDataSetChanged();
    
    public CheckBoxAdapter(List<CheckBoxBean> data, Context ctx) 
        this.data = data;
        this.ctx = ctx;
    
 
    @Override
    public Object getItem(int i) 
        return null;
    
 
    @Override
    public long getItemId(int i) 
        return i;
    
 
    @Override
    public View getView(int i, View view, ViewGroup viewGroup) 
        Log.i("app", ">>>>>>into getView method");
        CheckBoxViewHolder vh = null;
        if (view == null) 
            view = LayoutInflater.from(ctx).inflate(R.layout.customized_layout, viewGroup, false);
            vh = new CheckBoxViewHolder();
            vh.colorCheckBox = (CheckBox) view.findViewById(R.id.colorCheckBox);
            view.setTag(vh);
         else 
            vh = (CheckBoxViewHolder) view.getTag();
        
        if (data != null) 
            Log.i("app", ">>>>>>into getView set data");
            vh.colorCheckBox.setText(data.get(i).getDescription());
            vh.colorCheckBox.setChecked(data.get(i).isChecked());
            //checkbox位于listview中当翻页时,checkbox的选中状态会丢失,因此作下面的checkbox是否在前一页被选中时的位置及状态的记录
            final CheckBox checkBox = (CheckBox) view.findViewById(R.id.colorCheckBox);
            checkBox.setOnClickListener(new View.OnClickListener() 
                @Override
                public void onClick(View v) 
                    if (checkBox.isChecked()) 
                        map.put(i, true);
 
                     else 
                        map.remove(i);
 
                    
                
            );
            //由于在滚动到下一页时原选的选中状态会丢失因此用之前记录的状态重新渲染一下checkbox在滚动发生前是否被选中
            if (map != null && map.containsKey(i)) 
                checkBox.setChecked(true);
             else 
                checkBox.setChecked(false);
            
            return view;
        
        return null;
    
 
    static class CheckBoxViewHolder 
        public CheckBox colorCheckBox;
    

MainActivity.java

package org.mk.android.demo;
 
import androidx.appcompat.app.AppCompatActivity;
 
import android.content.Context;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.ListView;
 
import java.util.ArrayList;
import java.util.List;
 
public class MainActivity extends AppCompatActivity 
    private List<CheckBoxBean> data = null;
    private Context ctx;
    private CheckBoxAdapter adapter = null;
    private ListView listView;
    private Button addItemButton;
    private Button removeItemButton;
    @Override
    protected void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        addItemButton=(Button)findViewById(R.id.addItemButton);
        removeItemButton=(Button)findViewById(R.id.removeItemButton);
 
        ctx = MainActivity.this;
        listView = (ListView) findViewById(R.id.listView);
        Log.i("app", ">>>>>>start to load data");
        data = new ArrayList<CheckBoxBean>();
        for (int i = 1; i < 18; i++) 
            String value = String.valueOf(i);
            StringBuffer description = new StringBuffer();
            description.append(value).append("号色");
            data.add(new CheckBoxBean(value, description.toString(), false));
        
        adapter = new CheckBoxAdapter((List<CheckBoxBean>) data, ctx);
        addItemButton.setOnClickListener(new View.OnClickListener() 
            @Override
            public void onClick(View view) 
                adapter.add(new CheckBoxBean("1001","哈哈",false));
            
        );
        removeItemButton.setOnClickListener(new View.OnClickListener() 
            @Override
            public void onClick(View view) 
                adapter.remove();
            
        );
        final LayoutInflater inflater = LayoutInflater.from(this);
        Log.i("app", ">>>>>>finish load data");
        listView.setAdapter(adapter);
    

我们还看到了界面中有一个【添加一行】和【删除选中行】的两个按钮。这个实现很简单,核心原理就是调用List里的add和remove加:notifyDataSetChanged方法。我们可以在我们的Adapter中暴露这两个方法给到MainActivity去调用。另外notifyDataSetChanged这个方法是局部刷新,因此不用担心性能。

如何添加一行

//添加一个元素
public void add(CheckBoxBean item) 
    if (data == null) 
        data = new ArrayList<CheckBoxBean>();
    
    data.add(item);
    notifyDataSetChanged();

如何删除选中行

//删除选中元素
   public void remove() 
       for(Iterator it=map.keySet().iterator();it.hasNext();)
           int key=(int)it.next();
           if(map.get(key) ==true)
               it.remove();
               data.remove(key);
           
       
       notifyDataSetChanged();
   

结束今天的教程。

以上是关于Android入门第28天-ListView嵌套CheckBox在滚动时失去选中状态的问题的主要内容,如果未能解决你的问题,请参考以下文章

Android入门第60天-MVVM中的Databinding与ListView结合使用

Android入门第26天-在Android里自定义Adapter

Android入门第24天-Adapter使用初步-最简单的一个Adapter的使用

Android入门第25天-Android里使用SimpleAdapter实现复杂的界面布局

Android入门第8天-几个Layout混用完成常用手机商城首页下部有3个按钮的设置

Android入门第33天-Android里的弹出式对话框