You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

带EditText子项的ExpandableListView的Adapter实现技术问询

带EditText的ExpandableListView Adapter完整实现指南

嗨,我来帮你搞定这个带EditText子项的ExpandableListView问题!结合你的代码片段,我会给出完整的Adapter实现,同时解决EditText数据保存和视图复用的核心痛点。

一、先定义数据模型

首先要让每个子项的EditText数据和对应的模型绑定,这样不管视图怎么复用,数据都不会错乱。我们定义两个模型类:

// 组项模型
public class DeviceGroup {
    private String groupName;
    private List<Device> deviceList;

    public DeviceGroup(String groupName, List<Device> deviceList) {
        this.groupName = groupName;
        this.deviceList = deviceList;
    }

    // getter和setter省略
    public String getGroupName() { return groupName; }
    public void setGroupName(String groupName) { this.groupName = groupName; }
    public List<Device> getDeviceList() { return deviceList; }
    public void setDeviceList(List<Device> deviceList) { this.deviceList = deviceList; }
}

// 子项模型,新增字段保存EditText输入内容
public class Device {
    private String deviceName;
    private String code; // 对应hint_code的输入内容
    private String password; // 对应hint_pass的输入内容

    public Device(String deviceName) {
        this.deviceName = deviceName;
        this.code = "";
        this.password = "";
    }

    // getter和setter必须要有
    public String getDeviceName() { return deviceName; }
    public void setDeviceName(String deviceName) { this.deviceName = deviceName; }
    public String getCode() { return code; }
    public void setCode(String code) { this.code = code; }
    public String getPassword() { return password; }
    public void setPassword(String password) { this.password = password; }
}

二、完整的Adapter实现(含ViewHolder+数据绑定)

这里核心是用ViewHolder模式优化视图复用,同时给EditText绑定TextWatcher,实时把输入内容同步到Device模型里:

public class DeviceExpandableAdapter extends BaseExpandableListAdapter {
    private Context mContext;
    private List<DeviceGroup> mGroupList;

    public DeviceExpandableAdapter(Context context, List<DeviceGroup> groupList) {
        this.mContext = context;
        this.mGroupList = groupList;
    }

    // -------------- 组项相关方法 --------------
    @Override
    public int getGroupCount() {
        return mGroupList.size();
    }

    @Override
    public DeviceGroup getGroup(int groupPosition) {
        return mGroupList.get(groupPosition);
    }

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

    @Override
    public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) {
        GroupViewHolder groupViewHolder;
        if (convertView == null) {
            convertView = LayoutInflater.from(mContext).inflate(R.layout.group_list, parent, false);
            groupViewHolder = new GroupViewHolder();
            groupViewHolder.tvGroupName = convertView.findViewById(R.id.tv_group_name);
            convertView.setTag(groupViewHolder);
        } else {
            groupViewHolder = (GroupViewHolder) convertView.getTag();
        }
        // 设置组项名称
        groupViewHolder.tvGroupName.setText(getGroup(groupPosition).getGroupName());
        return convertView;
    }

    // -------------- 子项相关方法 --------------
    @Override
    public int getChildrenCount(int groupPosition) {
        return getGroup(groupPosition).getDeviceList().size();
    }

    @Override
    public Device getChild(int groupPosition, int childPosition) {
        return getGroup(groupPosition).getDeviceList().get(childPosition);
    }

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

    @Override
    public View getChildView(final int groupPosition, final int childPosition, boolean isLastChild, View convertView, ViewGroup parent) {
        ChildViewHolder childViewHolder;
        final Device currentDevice = getChild(groupPosition, childPosition);

        if (convertView == null) {
            convertView = LayoutInflater.from(mContext).inflate(R.layout.child_list, parent, false);
            childViewHolder = new ChildViewHolder();
            childViewHolder.hintCode = convertView.findViewById(R.id.hint_code);
            childViewHolder.hintPass = convertView.findViewById(R.id.hint_pass);
            // 给EditText设置初始的TextWatcher(后续复用会更新绑定的Device)
            childViewHolder.codeWatcher = new EditTextWatcher(currentDevice, true);
            childViewHolder.passWatcher = new EditTextWatcher(currentDevice, false);
            childViewHolder.hintCode.getEditText().addTextChangedListener(childViewHolder.codeWatcher);
            childViewHolder.hintPass.getEditText().addTextChangedListener(childViewHolder.passWatcher);
            convertView.setTag(childViewHolder);
        } else {
            childViewHolder = (ChildViewHolder) convertView.getTag();
            // 复用视图时,先移除旧的Watcher,避免绑定错误的Device
            childViewHolder.hintCode.getEditText().removeTextChangedListener(childViewHolder.codeWatcher);
            childViewHolder.hintPass.getEditText().removeTextChangedListener(childViewHolder.passWatcher);
            // 更新Watcher绑定的当前Device
            childViewHolder.codeWatcher.updateDevice(currentDevice);
            childViewHolder.passWatcher.updateDevice(currentDevice);
            // 重新添加Watcher
            childViewHolder.hintCode.getEditText().addTextChangedListener(childViewHolder.codeWatcher);
            childViewHolder.hintPass.getEditText().addTextChangedListener(childViewHolder.passWatcher);
        }

        // 设置EditText内容:先移除Watcher避免触发不必要的回调,设置完再添加回来
        childViewHolder.hintCode.getEditText().removeTextChangedListener(childViewHolder.codeWatcher);
        childViewHolder.hintCode.getEditText().setText(currentDevice.getCode());
        childViewHolder.hintCode.getEditText().addTextChangedListener(childViewHolder.codeWatcher);

        childViewHolder.hintPass.getEditText().removeTextChangedListener(childViewHolder.passWatcher);
        childViewHolder.hintPass.getEditText().setText(currentDevice.getPassword());
        childViewHolder.hintPass.getEditText().addTextChangedListener(childViewHolder.passWatcher);

        return convertView;
    }

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

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

    // -------------- ViewHolder定义 --------------
    static class GroupViewHolder {
        TextView tvGroupName;
    }

    static class ChildViewHolder {
        TextInputLayout hintCode;
        TextInputLayout hintPass;
        EditTextWatcher codeWatcher;
        EditTextWatcher passWatcher;
    }

    // -------------- 自定义TextWatcher,绑定Device模型 --------------
    private static class EditTextWatcher implements TextWatcher {
        private Device mDevice;
        private boolean isCode; // 区分是code输入框还是password输入框

        public EditTextWatcher(Device device, boolean isCode) {
            this.mDevice = device;
            this.isCode = isCode;
        }

        // 复用视图时更新绑定的Device
        public void updateDevice(Device device) {
            this.mDevice = device;
        }

        @Override
        public void beforeTextChanged(CharSequence s, int start, int count, int after) {}

        @Override
        public void onTextChanged(CharSequence s, int start, int before, int count) {}

        @Override
        public void afterTextChanged(Editable s) {
            // 实时把输入内容同步到Device模型
            if (isCode) {
                mDevice.setCode(s.toString());
            } else {
                mDevice.setPassword(s.toString());
            }
        }
    }
}

三、核心要点解析

1. 视图复用的关键处理

  • 必须使用ViewHolder:避免每次getChildView都调用findViewById,同时把TextWatcher和ViewHolder绑定,复用视图时可以直接获取并更新绑定的模型。
  • 复用视图时先移除旧的TextWatcher:防止旧的Watcher还绑定着之前的Device,导致输入内容同步到错误的模型里。

2. EditText数据处理

  • 把输入内容直接绑定到Device模型:不管视图怎么复用,数据都存在模型里,不会丢失或错乱。
  • 设置EditText文本时先移除Watcher:避免设置文本时触发afterTextChanged,导致不必要的模型更新。

3. 额外注意事项

  • 如果需要获取所有EditText的输入内容,直接遍历mGroupList里的Device对象即可,不用从视图中获取。
  • 若遇到焦点错乱问题,可以在getChildView里根据需求设置EditText的焦点状态,比如:
    // 示例:仅当当前子项是展开的组的第一个子项时获取焦点
    if (isExpanded && childPosition == 0) {
        childViewHolder.hintCode.getEditText().requestFocus();
    } else {
        childViewHolder.hintCode.getEditText().clearFocus();
    }
    

内容的提问来源于stack exchange,提问作者PPG

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 03:32:23