jjzjj

android - ExpandableListView 的子项中的 CheckBox 状态

coder 2023-12-07 原文

我已经阅读了大约 30 页的 SO 以及关于在列表中跟踪检查状态的教程,但是在可扩展 ListView 中这样做的信息(尤其是工作信息)很少。

我已经让 children 填充了复选框,但是当我选中一组 child 的复选框时,其他组中的随机 child 也会选中。我需要阻止这一切。我认为我读到的最好的信息是将复选框设置为单独的标签,但我不知道如何设置多个标签,当我尝试时,将复选框与转换 View 进行比较时出现类不匹配错误。

有没有人遇到过跟踪可扩展 ListView 的子项中的复选框状态的好方法?

我进行了多项更改,因此我放入了整个适配器代码。请检查 getGroupView 的前几行和整个 getChildView 并帮助我看看我做错了什么。

编辑: 现在发生的情况是,当我选中一个框然后展开另一个组时,所有选中的框都取消选中:

public class MyExpandableListAdapter extends BaseExpandableListAdapter
    implements OnCheckedChangeListener {

private Context mContext;

private ArrayList<ContactNameItems> mListDataHeader;
private HashMap<String, List<ContactPhoneItems>> mListDataChild;

private boolean[] mGetChecked;
private HashMap<String, boolean[]> mChildCheckStates;

private ArrayList<String> selectedNumbers;

private ChildViewHolder childViewHolder;
private GroupViewHolder groupViewHolder;

private String numberText;

public MyExpandableListAdapter(Context context,
        ArrayList<ContactNameItems> listDataHeader,
        HashMap<String, List<ContactPhoneItems>> listDataChild,
        ArrayList<String> listOfNumbers) {

    mContext = context;
    mListDataHeader = listDataHeader;
    mListDataChild = listDataChild;
    selectedNumbers = listOfNumbers;

    mChildCheckStates = new HashMap<String, boolean[]>();
}

@Override
public int getGroupCount() {
    return mListDataHeader.size();
}

@Override
public ContactNameItems getGroup(int groupPosition) {
    return mListDataHeader.get(groupPosition);
}

@Override
public long getGroupId(int groupPosition) {
    return groupPosition;
}

@Override
public View getGroupView(int groupPosition, boolean isExpanded,
        View convertView, ViewGroup parent) {

    String contactName = getGroup(groupPosition).getName();
    Bitmap contactImage = getGroup(groupPosition).getImage();

    mGetChecked = new boolean[getChildrenCount(groupPosition)];
    mChildCheckStates.put(contactName, mGetChecked);

    if (convertView == null) {

        LayoutInflater inflater = (LayoutInflater) mContext
                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        convertView = inflater.inflate(R.layout.contact_name_item, null);

        groupViewHolder = new GroupViewHolder();

        groupViewHolder.mContactName = (TextView) convertView
                .findViewById(R.id.lblListHeader);

        groupViewHolder.mContactImage = (ImageView) convertView
                .findViewById(R.id.ivContactPhoto);

        convertView.setTag(groupViewHolder);
    } else {

        groupViewHolder = (GroupViewHolder) convertView.getTag();
    }

    if (contactImage != null) {
        groupViewHolder.mContactImage.setImageBitmap(contactImage);

    } else {
        groupViewHolder.mContactImage
                .setImageResource(R.drawable.default_contact);
    }

    groupViewHolder.mContactName.setText(contactName);

    return convertView;
}

@Override
public int getChildrenCount(int groupPosition) {
    return mListDataChild.get(mListDataHeader.get(groupPosition).getName())
            .size();
}

@Override
public ContactPhoneItems getChild(int groupPosition, int childPosition) {
    return mListDataChild.get(mListDataHeader.get(groupPosition).getName())
            .get(childPosition);
}

@Override
public long getChildId(int groupPosition, int childPosition) {
    return childPosition;
}

@Override
public View getChildView(int groupPosition, int childPosition,
        boolean isLastChild, View convertView, ViewGroup parent) {

    final String contactName = getGroup(groupPosition).getName();
    final int mChildPosition = childPosition;

    numberText = getChild(groupPosition, childPosition).getNumber();
    String typeText = getChild(groupPosition, childPosition).getPhoneType();

    mGetChecked = mChildCheckStates.get(contactName);

    if (convertView == null) {

        LayoutInflater inflater = (LayoutInflater) this.mContext
                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        convertView = inflater.inflate(R.layout.contact_detail_item, null);

        childViewHolder = new ChildViewHolder();

        childViewHolder.mPhoneNumber = (TextView) convertView
                .findViewById(R.id.tv_phone_number);

        childViewHolder.mPhoneType = (TextView) convertView
                .findViewById(R.id.tv_phone_type);

        childViewHolder.mCheckBox = (CheckBox) convertView
                .findViewById(R.id.checkBox);

        convertView.setTag(R.layout.contact_detail_item, childViewHolder);

    } else {

        childViewHolder = (ChildViewHolder) convertView
                .getTag(R.layout.contact_detail_item);
    }

    childViewHolder.mPhoneNumber.setText(numberText);
    childViewHolder.mPhoneType.setText(typeText);

    childViewHolder.mCheckBox.setChecked(mGetChecked[mChildPosition]);
    childViewHolder.mCheckBox.setOnClickListener(new OnClickListener() {

        @Override
        public void onClick(View v) {

            boolean isChecked = childViewHolder.mCheckBox.isChecked();

            Log.d("Debug", "isChecked = " + String.valueOf(isChecked));

            if (isChecked) {

                selectedNumbers.add(numberText);

            } else {

                selectedNumbers.remove(numberText);
            }

            childViewHolder.mCheckBox.setChecked(isChecked);

            mGetChecked[mChildPosition] = isChecked;
            mChildCheckStates.put(contactName, mGetChecked);
        }
    });

    return convertView;
}

@Override
public boolean isChildSelectable(int groupPosition, int childPosition) {
    return false;
}

@Override
public boolean hasStableIds() {
    return false;
}

public ArrayList<String> getSelectedNumbers() {

    return selectedNumbers;

}

public final class GroupViewHolder {

    TextView mContactName;
    ImageView mContactImage;
}

public final class ChildViewHolder {

    TextView mPhoneNumber;
    TextView mPhoneType;
    CheckBox mCheckBox;
}

@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
    // TODO Auto-generated method stub
    Log.d("Debug", "onCheckChangedListener : " + String.valueOf(isChecked));
}

最佳答案

这是可行的解决方案

好吧,虽然互联网上似乎有很多关于可扩展 ListView 中的复选框的建议,但没有一个对我有用。我能够在一群人的帮助下让它工作,尤其是 anddev84。现在我不会声称这是完美的或万无一失的。我只是声称它对我有用。我已经在 2 台设备上进行了测试,对此我感到非常满意。

因此,我采用了我的工作代码,将其缩减为基本部分,并添加了一堆有用的注释,以便任何需要它的人都可以使用它。希望它对你和我一样有效。享受

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import android.annotation.SuppressLint;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseExpandableListAdapter;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.TextView;

// Eclipse wanted me to use a sparse array instead of my hashmaps, I just suppressed that suggestion
@SuppressLint("UseSparseArrays")
public class MyExpandableListAdapter extends BaseExpandableListAdapter {

    // Define activity context
    private Context mContext;

    /*
     * Here we have a Hashmap containing a String key 
     * (can be Integer or other type but I was testing 
     * with contacts so I used contact name as the key)
    */ 
    private HashMap<String, List<ExpListChildItems>> mListDataChild;

    // ArrayList that is what each key in the above 
    // hashmap points to
    private ArrayList<ExpListGroupItems> mListDataGroup;

    // Hashmap for keeping track of our checkbox check states
    private HashMap<Integer, boolean[]> mChildCheckStates;

    // Our getChildView & getGroupView use the viewholder patter
    // Here are the viewholders defined, the inner classes are
    // at the bottom
    private ChildViewHolder childViewHolder;
    private GroupViewHolder groupViewHolder;

    /*  
     *  For the purpose of this document, I'm only using a single
     *  textview in the group (parent) and child, but you're limited only
     *  by your XML view for each group item :)
    */ 
    private String groupText;
    private String childText

    /*  Here's the constructor we'll use to pass in our calling
     *  activity's context, group items, and child items
    */ 
    public MyExpandableListAdapter(Context context,
            ArrayList<ExpListGroupItems> listDataGroup, HashMap<String, List<ExpListChildItems>> listDataChild) {

        mContext = context;
        mListDataGroup = listDataGroup;
        mListDataChild = listDataChild;

        // Initialize our hashmap containing our check states here
        mChildCheckStates = new HashMap<Integer, boolean[]>();
    }

    @Override
    public int getGroupCount() {
        return mListDataGroup.size();
    }

    /*  
     * This defaults to "public object getGroup" if you auto import the methods
     * I always make a point to change it from "object" to whatever item
     * I passed through the constructor
    */ 
    @Override
    public ExpListGroupItems getGroup(int groupPosition) {
        return mListDataGroup.get(groupPosition);
    }

    @Override
    public long getGroupId(int groupPosition) {
        return groupPosition;
    }

    @Override
    public View getGroupView(int groupPosition, boolean isExpanded,
            View convertView, ViewGroup parent) {

        //  I passed a text string into an activity holding a getter/setter
        //  which I passed in through "ExpListGroupItems".
        //  Here is where I call the getter to get that text
        groupText = getGroup(groupPosition).getGroupText();

        if (convertView == null) {

            LayoutInflater inflater = (LayoutInflater) mContext
                    .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            convertView = inflater.inflate(R.layout.group_item, null);

            // Initialize the GroupViewHolder defined at the bottom of this document
            groupViewHolder = new GroupViewHolder();

            groupViewHolder.mGroupText = (TextView) convertView.findViewById(R.id.groupTextView);

            convertView.setTag(groupViewHolder);
        } else {

            groupViewHolder = (GroupViewHolder) convertView.getTag();
        }

        groupViewHolder.mGroupText.setText(groupText);

        return convertView;
    }

    @Override
    public int getChildrenCount(int groupPosition) {
        return mListDataChild.get(mListDataGroup.get(groupPosition).getMyText()).size();
    }

    /*  
     * This defaults to "public object getChild" if you auto import the methods
     * I always make a point to change it from "object" to whatever item
     * I passed through the constructor
    */ 
    @Override
    public ExpListChildItems getChild(int groupPosition, int childPosition) {
        return mListDataChild.get(mListDataGroup.get(groupPosition).getMyText()).get(childPosition);
    }

    @Override
    public long getChildId(int groupPosition, int childPosition) {
        return childPosition;
    }

    @Override
    public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) {

        final int mGroupPosition = groupPosition;
        final int mChildPosition = childPosition;

        //  I passed a text string into an activity holding a getter/setter
        //  which I passed in through "ExpListChildItems".
        //  Here is where I call the getter to get that text
        childText = getChild(mGroupPosition, mChildPosition).getChildText();

        if (convertView == null) {

            LayoutInflater inflater = (LayoutInflater) this.mContext
                    .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            convertView = inflater.inflate(R.layout.child_item, null);

            childViewHolder = new ChildViewHolder();

            childViewHolder.mChildText = (TextView) convertView
                    .findViewById(R.id.childTextView);

            childViewHolder.mCheckBox = (CheckBox) convertView
                    .findViewById(R.id.checkBox);

            convertView.setTag(R.layout.child_item, childViewHolder);

        } else {

            childViewHolder = (ChildViewHolder) convertView
                    .getTag(R.layout.child_item);
        }

        childViewHolder.mChildText.setText(childText);
            /* 
         * You have to set the onCheckChangedListener to null
         * before restoring check states because each call to 
         * "setChecked" is accompanied by a call to the 
         * onCheckChangedListener
        */ 
        childViewHolder.mCheckBox.setOnCheckedChangeListener(null);

        if (mChildCheckStates.containsKey(mGroupPosition)) {
            /*
             * if the hashmap mChildCheckStates<Integer, Boolean[]> contains
             * the value of the parent view (group) of this child (aka, the key),
             * then retrive the boolean array getChecked[]
            */
            boolean getChecked[] = mChildCheckStates.get(mGroupPosition);

            // set the check state of this position's checkbox based on the 
            // boolean value of getChecked[position]
            childViewHolder.mCheckBox.setChecked(getChecked[mChildPosition]);

        } else {

            /*
            * if the hashmap mChildCheckStates<Integer, Boolean[]> does not
            * contain the value of the parent view (group) of this child (aka, the key),
            * (aka, the key), then initialize getChecked[] as a new boolean array
            *  and set it's size to the total number of children associated with 
            *  the parent group
            */
            boolean getChecked[] = new boolean[getChildrenCount(mGroupPosition)];

            // add getChecked[] to the mChildCheckStates hashmap using mGroupPosition as the key
            mChildCheckStates.put(mGroupPosition, getChecked);

            // set the check state of this position's checkbox based on the 
            // boolean value of getChecked[position]
            childViewHolder.mCheckBox.setChecked(false);
        }

        childViewHolder.mCheckBox.setOnCheckedChangeListener(new OnCheckedChangeListener() {

                    @Override
                    public void onCheckedChanged(CompoundButton buttonView,
                            boolean isChecked) {

                        if (isChecked) {

                                boolean getChecked[] = mChildCheckStates.get(mGroupPosition);
                                getChecked[mChildPosition] = isChecked;
                                mChildCheckStates.put(mGroupPosition, getChecked);

                        } else {

                                boolean getChecked[] = mChildCheckStates.get(mGroupPosition);
                                getChecked[mChildPosition] = isChecked;
                                mChildCheckStates.put(mGroupPosition, getChecked);
                        }
                    }
                });

        return convertView;
    }

    @Override
    public boolean isChildSelectable(int groupPosition, int childPosition) {
        return false;
    }

    @Override
    public boolean hasStableIds() {
        return false;
    }

    public final class GroupViewHolder {

        TextView mGroupText;
    }

    public final class ChildViewHolder {

        TextView mChildText;
        CheckBox mCheckBox;
    }
}

关于android - ExpandableListView 的子项中的 CheckBox 状态,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21494042/

有关android - ExpandableListView 的子项中的 CheckBox 状态的更多相关文章

  1. ruby - 如何从 ruby​​ 中的字符串运行任意对象方法? - 2

    总的来说,我对ruby​​还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用

  2. ruby - 其他文件中的 Rake 任务 - 2

    我试图在一个项目中使用rake,如果我把所有东西都放到Rakefile中,它会很大并且很难读取/找到东西,所以我试着将每个命名空间放在lib/rake中它自己的文件中,我添加了这个到我的rake文件的顶部:Dir['#{File.dirname(__FILE__)}/lib/rake/*.rake'].map{|f|requiref}它加载文件没问题,但没有任务。我现在只有一个.rake文件作为测试,名为“servers.rake”,它看起来像这样:namespace:serverdotask:testdoputs"test"endend所以当我运行rakeserver:testid时

  3. ruby-on-rails - Ruby net/ldap 模块中的内存泄漏 - 2

    作为我的Rails应用程序的一部分,我编写了一个小导入程序,它从我们的LDAP系统中吸取数据并将其塞入一个用户表中。不幸的是,与LDAP相关的代码在遍历我们的32K用户时泄漏了大量内存,我一直无法弄清楚如何解决这个问题。这个问题似乎在某种程度上与LDAP库有关,因为当我删除对LDAP内容的调用时,内存使用情况会很好地稳定下来。此外,不断增加的对象是Net::BER::BerIdentifiedString和Net::BER::BerIdentifiedArray,它们都是LDAP库的一部分。当我运行导入时,内存使用量最终达到超过1GB的峰值。如果问题存在,我需要找到一些方法来更正我的代

  4. ruby - 在 Ruby 程序执行时阻止 Windows 7 PC 进入休眠状态 - 2

    我需要在客户计算机上运行Ruby应用程序。通常需要几天才能完成(复制大备份文件)。问题是如果启用sleep,它会中断应用程序。否则,计算机将持续运行数周,直到我下次访问为止。有什么方法可以防止执行期间休眠并让Windows在执行后休眠吗?欢迎任何疯狂的想法;-) 最佳答案 Here建议使用SetThreadExecutionStateWinAPI函数,使应用程序能够通知系统它正在使用中,从而防止系统在应用程序运行时进入休眠状态或关闭显示。像这样的东西:require'Win32API'ES_AWAYMODE_REQUIRED=0x0

  5. ruby-on-rails - Rails 3 中的多个路由文件 - 2

    Rails2.3可以选择随时使用RouteSet#add_configuration_file添加更多路由。是否可以在Rails3项目中做同样的事情? 最佳答案 在config/application.rb中:config.paths.config.routes在Rails3.2(也可能是Rails3.1)中,使用:config.paths["config/routes"] 关于ruby-on-rails-Rails3中的多个路由文件,我们在StackOverflow上找到一个类似的问题

  6. ruby-on-rails - Rails - 一个 View 中的多个模型 - 2

    我需要从一个View访问多个模型。以前,我的links_controller仅用于提供以不同方式排序的链接资源。现在我想包括一个部分(我假设)显示按分数排序的顶级用户(@users=User.all.sort_by(&:score))我知道我可以将此代码插入每个链接操作并从View访问它,但这似乎不是“ruby方式”,我将需要在不久的将来访问更多模型。这可能会变得很脏,是否有针对这种情况的任何技术?注意事项:我认为我的应用程序正朝着单一格式和动态页面内容的方向发展,本质上是一个典型的网络应用程序。我知道before_filter但考虑到我希望应用程序进入的方向,这似乎很麻烦。最终从任何

  7. ruby-on-rails - Rails 3.2.1 中 ActionMailer 中的未定义方法 'default_content_type=' - 2

    我在我的项目中添加了一个系统来重置用户密码并通过电子邮件将密码发送给他,以防他忘记密码。昨天它运行良好(当我实现它时)。当我今天尝试启动服务器时,出现以下错误。=>BootingWEBrick=>Rails3.2.1applicationstartingindevelopmentonhttp://0.0.0.0:3000=>Callwith-dtodetach=>Ctrl-CtoshutdownserverExiting/Users/vinayshenoy/.rvm/gems/ruby-1.9.3-p0/gems/actionmailer-3.2.1/lib/action_mailer

  8. ruby-on-rails - Rails 应用程序中的 Rails : How are you using application_controller. rb 是新手吗? - 2

    刚入门rails,开始慢慢理解。有人可以解释或给我一些关于在application_controller中编码的好处或时间和原因的想法吗?有哪些用例。您如何为Rails应用程序使用应用程序Controller?我不想在那里放太多代码,因为据我了解,每个请求都会调用此Controller。这是真的? 最佳答案 ApplicationController实际上是您应用程序中的每个其他Controller都将从中继承的类(尽管这不是强制性的)。我同意不要用太多代码弄乱它并保持干净整洁的态度,尽管在某些情况下ApplicationContr

  9. ruby-on-rails - form_for 中不在模型中的自定义字段 - 2

    我想向我的Controller传递一个参数,它是一个简单的复选框,但我不知道如何在模型的form_for中引入它,这是我的观点:{:id=>'go_finance'}do|f|%>Transferirde:para:Entrada:"input",:placeholder=>"Quantofoiganho?"%>Saída:"output",:placeholder=>"Quantofoigasto?"%>Nota:我想做一个额外的复选框,但我该怎么做,模型中没有一个对象,而是一个要检查的对象,以便在Controller中创建一个ifelse,如果没有检查,请帮助我,非常感谢,谢谢

  10. ruby - rspec 需要 .rspec 文件中的 spec_helper - 2

    我注意到像bundler这样的项目在每个specfile中执行requirespec_helper我还注意到rspec使用选项--require,它允许您在引导rspec时要求一个文件。您还可以将其添加到.rspec文件中,因此只要您运行不带参数的rspec就会添加它。使用上述方法有什么缺点可以解释为什么像bundler这样的项目选择在每个规范文件中都需要spec_helper吗? 最佳答案 我不在Bundler上工作,所以我不能直接谈论他们的做法。并非所有项目都checkin.rspec文件。原因是这个文件,通常按照当前的惯例,只

随机推荐