【问题标题】:How to check/uncheck CheckedTextView (child items) items inside ExpandableListView?如何选中/取消选中 ExpandableListView 中的 CheckedTextView(子项)项?
【发布时间】:2020-09-11 10:54:15
【问题描述】:

我的设计:我为我的 ExpandableListView 创建了一个自定义适配器(SignalsExpandableListAdapter)with CheckedTextView。

public class SignalsExpandableListAdapter extends BaseExpandableListAdapter {
private Context context;
private List<String> listDataHandler;
private HashMap<String,List<String>> listHashMap;

public SignalsExpandableListAdapter(Context context, List<String> listDataHandler, HashMap<String, List<String>> listHashMap) {
    this.context = context;
    this.listDataHandler = listDataHandler;
    this.listHashMap = listHashMap;
}

然后数据被填充到一个片段中,HashMap 和 List 如下所示。这很好用。我可以在我的 ExpandableListView 中看到子元素和父元素。

ArrayList<List<String[]>> deviceMList = new ArrayList<>();
final List<String[]> mList = new ArrayList<>();
deviceMList.add(mList);

我在寻找什么我:当我选择任何孩子(可以是多个)时,我想使用 CheckedTextView 用勾号表示该选择。当我选择任何选中的子项目时,我也想取消选中。 (一个简单的检查/取消检查设计)。以及选择任何孩子时,调用另一个函数以进行绘图。这就是我卡住的地方。每次当我选择一个孩子时,随机多个孩子 checkTextViews 也被指示为选中,而不仅仅是我选择的那个。

经过一番搜索,我尝试了 3 次尝试来克服这个问题并只检查我选择的孩子。但似乎我的任何尝试都没有奏效。我不确定发生了什么。我的尝试如下所述。我真的很感激对此的任何建议。

我尝试了尝试 4(在下文第 II 节中描述。​​)但似乎仍然没有实时进行检查/取消检查。

尝试 1: 尝试在 Fragment 内部进行检查

listAdapter = new SignalsExpandableListAdapter(context,signalsListDataHeader,signalsListHash);
signalsExpandableListView.setAdapter(listAdapter);
signalsExpandableListView.setChoiceMode(AbsListView.CHOICE_MODE_MULTIPLE);
//tickCheckboxes();

signalsExpandableListView.setOnChildClickListener(new ExpandableListView.OnChildClickListener() {
    @Override
    public boolean onChildClick(ExpandableListView parent, View v, int groupPosition, int childPosition, long id) {
        CheckedTextView sensorCheckedView  = (CheckedTextView) v.findViewById(R.id.sensorsCheckedTextView);
        if(!mService.mPlotManager.ifPropertyExist(deviceMList.get(groupPosition).get(childPosition))) {
            sensorCheckedView.setChecked(true);
            try {
                if(Plot==null) {
                    Log.e(LOG_TAG, "Plot is null!");
                }
                mService.mPlotManager.addSignal(deviceMList.get(groupPosition).get(childPosition), Plot);
            } catch (Exception e) {
                Log.e(LOG_TAG, "Error! + e);
                e.printStackTrace();
            }
        } else {
            mService.mPlotManager.removeSignal(deviceMList.get(groupPosition).get(childPosition));
            //signalsExpandableListView.setItemChecked(childPosition,false);
            sensorCheckedView.setChecked(false);
        }
        return true;
    }
});

尝试 2: 创建一个不同的函数并在 Fragment 中调用它。创建的函数如下,函数调用在尝试1上面注释。

//loop through groups and then children
public void tickCheckboxes() {
    //CheckedTextView sensorCheckedView  = (CheckedTextView) signalsExpandableListView.findViewById(R.id.sensorsCheckedTextView);
    for (int i = 0; i < deviceMList.size(); i++) {
        for (int j = 0; j < deviceMList.get(i).size(); j++) {
            if (mService.mPlotManager.ifPropertyExist(deviceMList.get(i).get(j))) {
                signalsExpandableListView.setItemChecked(j, true);
                //sensorCheckedView.setChecked(true);
            } else {
                signalsExpandableListView.setItemChecked(j, false);
                //sensorCheckedView.setChecked(false);
            }
        }
    }
}

尝试 3: 使用 ExpandableListView 适配器访问子元素。我更新了getChildView() 方法如下。

@Override
public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View view, ViewGroup parent) {
    final String childText = (String) getChild(groupPosition,childPosition);
    if(view == null){
        LayoutInflater inflater = (LayoutInflater) this.context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        view = inflater.inflate(R.layout.sensors_list_items,null);
    }
    CheckedTextView txtListChild = (CheckedTextView) view.findViewById((R.id.sensorsCheckedTextView));
    txtListChild.setText(childText);
    txtListChild.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            if (txtListChild.isChecked()) {
                txtListChild.setChecked(false);
            } else {
                txtListChild.setChecked(true);
            }
        }
    });
    return view;
}

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

列出项目 xml:

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

    <CheckedTextView
        android:id="@+id/sensorsCheckedTextView"
        android:layout_width="match_parent"
        android:layout_height="?android:attr/listPreferredItemHeightSmall"
        android:textAppearance="?android:attr/textAppearanceListItemSmall"
        android:gravity="center_vertical"
        android:checkMark="?android:attr/listChoiceIndicatorMultiple"
        android:paddingLeft="?android:attr/expandableListPreferredChildPaddingLeft"
        android:textColor="@color/black"
        android:paddingTop="5dp"
        android:paddingBottom="5dp"/>


</LinearLayout>

组 xml:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">
    <TextView
        android:id="@+id/deviceNameTextView"
        android:layout_width="match_parent"
        android:layout_height="?android:attr/listPreferredItemHeightSmall"
        android:textAppearance="?android:attr/textAppearanceListItemSmall"
        android:gravity="center_vertical"
        android:text="Device Name"
        android:paddingTop="5dp"
        android:paddingBottom="5dp"
        android:background="@color/colorAccent"
        android:textColor="@color/colorPrimary"
        android:paddingLeft="?android:attr/expandableListPreferredItemPaddingLeft"/>
</LinearLayout>

我在寻找什么 II: 对于检查/取消检查,我在下面提到的 answer 的帮助下修改了适配器,如尝试 4 中所述。我无法实时看到checked 标记。当我单击一个孩子时,我想向上/向下滚动以直观地看到复选标记。与取消选中相同。它很奇怪。似乎需要一种“刷新”才能实时查看选中/取消选中状态。我将不胜感激有关如何摆脱这种情况的任何建议。

谢谢。

尝试 4: 修改适配器。这适用于选中/取消选中,但无法实时直观地看到它。

public class SignalsExpandableListAdapter extends BaseExpandableListAdapter {
    private Context context;
    private List<String> listDataHandler;
    private HashMap<String,List<String>> listHashMap;
    private MService mService;
    private ArrayList<List<String[]>> deviceMList ;

    public SignalsExpandableListAdapter(Context context, List<String> listDataHandler, HashMap<String, List<String>> listHashMap,mService service, ArrayList<List<String[]>> dMList ) {
        this.context = context;
        this.listDataHandler = listDataHandler;
        this.listHashMap = listHashMap;
        this.mService = service;
        this.deviceMList =dMList;
    }

@Override
public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View view, ViewGroup parent) {
    final String childText = (String) getChild(groupPosition,childPosition);
    if(view == null){
        LayoutInflater inflater = (LayoutInflater) this.context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        view = inflater.inflate(R.layout.sensors_list_items,null);
    }
    CheckedTextView txtListChild = (CheckedTextView) view.findViewById((R.id.sensorsCheckedTextView));
    txtListChild.setText(childText);

if(mService.mPlotManager.ifPropertyExist(deviceMList.get(groupPosition).get(childPosition))) {
        txtListChild.setChecked(true);
    } else {
        txtListChild.setChecked(false);
    }
    return view;
}

【问题讨论】:

    标签: java android android-fragments expandablelistview checkedtextview


    【解决方案1】:

    我假设您的 mService 带有一个名为 PlottingService 的类,然后尝试对适配器进行以下更改:

    public class SignalsExpandableListAdapter extends BaseExpandableListAdapter {
        private Context context;
        private List<String> listDataHandler;
        private HashMap<String,List<String>> listHashMap;
        private PlottingService mService;
    
        public SignalsExpandableListAdapter(Context context, List<String> listDataHandler, HashMap<String, List<String>> listHashMap, PlottingService ps) {
            this.context = context;
            this.listDataHandler = listDataHandler;
            this.listHashMap = listHashMap;
            this.mService = ps;
        }
    
        @Override
        public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View view, ViewGroup parent) {
            final String childText = (String) getChild(groupPosition,childPosition);
            if(view == null){
                LayoutInflater inflater = (LayoutInflater) this.context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
                view = inflater.inflate(R.layout.sensors_list_items,null);
            }
            CheckedTextView txtListChild = (CheckedTextView) view.findViewById((R.id.sensorsCheckedTextView));
            txtListChild.setText(childText);
    
            // Always set check state according to data.
            if(mService.mPlotManager.ifPropertyExist(deviceMList.get(groupPosition).get(childPosition))) {
                txtListChild.setChecked(true);
            } else {
                txtListChild.setChecked(false);
            }
    
            return view;
        }
    

    在 Fragment 内部,尝试 1,只更改: listAdapter = new SignalsExpandableListAdapter(context,signalsListDataHeader,signalsListHash);listAdapter = new SignalsExpandableListAdapter(context,signalsListDataHeader,signalsListHash, mService);

    希望有帮助!

    更新:- 在 Fragment 中,注释掉 OnChildClickListener 并在适配器中,使用以下 getChildView:

    public View getChildView(final int groupPosition, final int childPosition, boolean isLastChild, View view, ViewGroup parent) {
        String childText = (String) getChild(groupPosition,childPosition);
        if(view == null){
            LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            view = inflater.inflate(R.layout.sensors_list_items,null);
        }
        CheckedTextView txtListChild = (CheckedTextView) view.findViewById((R.id.sensorsCheckedTextView));
        txtListChild.setText(childText);
    
        // Always set check state according to data.
        if(mService.mPlotManager.ifPropertyExist(deviceMList.get(groupPosition).get(childPosition))) {
            txtListChild.setChecked(true);
        } else {
            txtListChild.setChecked(false);
        }
    
        txtListChild.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                CheckedTextView sensorCheckedView = (CheckedTextView)v;
                if(!sensorCheckedView.isChecked()) {
                    sensorCheckedView.setChecked(true);
                    try {
                        if(Plot==null) {
                            Log.e(LOG_TAG, "Plot is null!");
                        }
                        mService.mPlotManager.addSignal(deviceMList.get(groupPosition).get(childPosition), Plot);
                    } catch (Exception e) {
                        Log.e(LOG_TAG, "Error!" + e);
                        e.printStackTrace();
                    }
                } else {
                    mService.mPlotManager.removeSignal(deviceMList.get(groupPosition).get(childPosition));
                    sensorCheckedView.setChecked(false);
                }
            }
        });
        return view;
    }
    

    更新 2:- 在 Fragment 中,注释掉 OnChildClickListener 并在适配器中,添加一个内部类并使用以下 getChildView:

    class Position {
        int group, child;
        Position(int group, int child) {
            this.group = group;
            this.child = child;
        }
    }
    
    @Override
    public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View view, ViewGroup parent) {
        String childText = (String) getChild(groupPosition,childPosition);
        if(view == null){
            LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            view = inflater.inflate(R.layout.sensors_list_items,null);
        }
        CheckedTextView txtListChild = (CheckedTextView) view.findViewById((R.id.sensorsCheckedTextView));
        txtListChild.setText(childText);
    
        // Always set check state according to data.
        if(mService.mPlotManager.ifPropertyExist(deviceMList.get(groupPosition).get(childPosition))) {
            txtListChild.setChecked(true);
        } else {
            txtListChild.setChecked(false);
        }
    
        txtListChild.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                CheckedTextView sensorCheckedView = (CheckedTextView)v;
                int groupPosition = ((Position)v.getTag()).group;
                int childPosition = ((Position)v.getTag()).child;
                if(!sensorCheckedView.isChecked()) {
                    sensorCheckedView.setChecked(true);
                    try {
                        if(Plot==null) {
                            Log.e(LOG_TAG, "Plot is null!");
                        }
                        mService.mPlotManager.addSignal(deviceMList.get(groupPosition).get(childPosition), Plot);
                    } catch (Exception e) {
                        Log.e(LOG_TAG, "Error!" + e);
                        e.printStackTrace();
                    }
                } else {
                    mService.mPlotManager.removeSignal(deviceMList.get(groupPosition).get(childPosition));
                    sensorCheckedView.setChecked(false);
                }
            }
        });
        txtListChild.setTag(new Position(groupPosition, childPosition));
        return view;
    }
    

    如果Updated 2仍然存在多选的问题,那么可能的原因是mService.mPlotManager.ifPropertyExist(deviceMList.get(groupPosition).get(childPosition))。与其使用 mService 来验证是否选择了某个项目,不如将 listHashMapHashMap&lt;String, List&lt;String&gt;&gt; 更改为 HashMap&lt;String, List&lt;ChildItem&gt;&gt; [The accepted answer in Values of counter changes after scrolling ExpendableListView,您需要一个布尔字段而不是整数字段数量]。然后当点击子项时,检查并更新列表。

    【讨论】:

    • 感谢您的建议。我试过这个。我无法访问适配器内部的deviceMList,所以我也将private ArrayList&lt;List&lt;String[]&gt;&gt; deviceMList 添加到适配器中,并对其进行了类似的修改。现在我可以选中/取消选中。但似乎还是有什么不对劲。我无法实时看到checked 标记。当我单击一个孩子时,我想向上/向下滚动以直观地看到复选标记。与取消选中相同。它很奇怪。请问有什么想法吗?我将用这种尝试和观察到的奇怪行为来更新我的问题。
    • 我没有发现任何关于这种奇怪结果的信息。尝试更新,看看它是否解决了问题。
    • 谢谢,我试过了(除了适配器更新了 plot & Log_TAG 作为输入参数),但很奇怪。现在我可以在单击孩子后立即看到复选标记,但是当我将列表滚动到底部时,另一个项目也被选中(复选标记正在运行)。当我检查一次我观察到的内容总是在屏幕上时,无论它是什么,都会有一个签入项目。检查状态的种类正在更改为当前的可视化项目。如果我检查 2 个项目并向下滚动,我可以看到另外 2 个项目总是在屏幕上被检查,以某种方式检查了 2 个项目。
    • 我不确定为什么“复选”标记移动不修复。我有一个完整的主要活动>在该服务活动中>在该可扩展列表中位于一个片段中。
    • 我做了一个 Updated 2 并添加了一些建议。如果这解决了你的问题,那么我给你另一个建议:如果 childText 很短,使用列表格式有点浪费空间,那么看看我在这两个链接中的答案:@ 987654322@, stackoverflow.com/questions/50288713/… 希望有帮助!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-10-01
    • 1970-01-01
    • 2018-10-14
    • 2012-09-20
    • 1970-01-01
    • 2013-01-19
    • 1970-01-01
    相关资源
    最近更新 更多