Android 二級列表控件ExpandableListView 的簡單使用

簡述

在Android 開發(fā)中多多少少會碰到需要二級列表,之前已經(jīng)寫過一篇RecyclerView 二級列表 其實現(xiàn)方式是通過根據(jù)不同ViewHolder 來顯示是一級還是二級列表,想起谷歌官方自己就有自帶二級列表控件ExpandableListView,如果不需要復(fù)雜效果,建議直接使用官方控件,故有了今天這一篇文章。

老規(guī)矩,先上圖:


視頻錄制效果.gif

如圖所見滑動出屏幕或者點擊checkbox時會出現(xiàn)錯位等一些問題也解決了,具體方法請往下瀏覽(文末附上github 地址)

頁面布局

布局很簡單,ExpandableListView 加底部一個Button,直接上布局截圖,相信各位能看懂


activity_main.png

由于其控件會默認自帶箭頭(如下圖)


默認自帶的指示器箭頭.png

我們可以通過XML中在ExpandableListView控件加上

     android:groupIndicator="@null"

取消掉其自帶的指示器箭頭,當(dāng)然除了在xml上,也可通過在代碼中,當(dāng)綁定完控件后調(diào)用代碼也可實現(xiàn)取消效果

 expandableListView.setGroupIndicator(null);

接下來是我們重點要研究的適配器StudentExpandableAdapter,繼承并重寫了BaseExpandableListAdapter這個類的相關(guān)函數(shù),其中注釋我已經(jīng)詳細寫在代碼中,若是不懂或者寫錯,希望各位可以交流或指出,大家一起加深對其認識。

public class StudentExpandableAdapter extends BaseExpandableListAdapter {

    private Context context;
    private List<DataEntity> dataEntity;
    private CheckBoxListener checkBoxListener;

    public StudentExpandableAdapter(Context context, List<DataEntity> dataEntity) {
        this.context = context;
        this.dataEntity = dataEntity;
    }

    /**
     * 獲取組的數(shù)目
     *
     * @return 返回一級列表組的數(shù)量
     */
    @Override
    public int getGroupCount() {
        return dataEntity == null ? 0 : dataEntity.size();
    }

    /**
     * 獲取指定組中的子節(jié)點數(shù)量
     *
     * @param groupPosition 子元素組所在的位置
     * @return 返回指定組中的子數(shù)量
     */
    @Override
    public int getChildrenCount(int groupPosition) {
        return dataEntity.get(groupPosition).getChildrenDataList().size();
    }

    /**
     * 獲取與給定組相關(guān)聯(lián)的對象
     *
     * @param groupPosition 子元素組所在的位置
     * @return 返回指定組的子數(shù)據(jù)
     */
    @Override
    public Object getGroup(int groupPosition) {
        return dataEntity.get(groupPosition).getTitle();
    }


    /**
     * 獲取與給定組中的給定子元素關(guān)聯(lián)的數(shù)據(jù)
     *
     * @param groupPosition 子元素組所在的位置
     * @param childPosition 子元素的位置
     * @return 返回子元素的對象
     */
    @Override
    public Object getChild(int groupPosition, int childPosition) {
        return dataEntity.get(groupPosition).getChildrenDataList().get(childPosition);
    }

    /**
     * 獲取組在給定位置的ID(唯一的)
     *
     * @param groupPosition 子元素組所在的位置
     * @return 返回關(guān)聯(lián)組ID
     */
    @Override
    public long getGroupId(int groupPosition) {
        return groupPosition;
    }


    /**
     * 獲取給定組中給定子元素的ID(唯一的)
     *
     * @param groupPosition 子元素組所在的位置
     * @param childPosition 子元素的位置
     * @return 返回子元素關(guān)聯(lián)的ID
     */
    @Override
    public long getChildId(int groupPosition, int childPosition) {
        return childPosition;
    }

    /**
     * @return 確定id 是否總是指向同一個對象
     */
    @Override
    public boolean hasStableIds() {
        return true;
    }

    /**
     * @return 返回指定組的對應(yīng)的視圖 (一級列表樣式)
     */
    @Override
    public View getGroupView(final int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) {
        ParentHolder parentHolder;
        if (convertView == null) {
            convertView = LayoutInflater.from(context).inflate(R.layout.parent_item, null);
            parentHolder = new ParentHolder();
            parentHolder.tvParent = convertView.findViewById(R.id.tv_parent);
            parentHolder.img_right = convertView.findViewById(R.id.img_right);
            convertView.setTag(parentHolder);
        } else {
            parentHolder = (ParentHolder) convertView.getTag();
        }
        parentHolder.tvParent.setText(dataEntity.get(groupPosition).getTitle());
     

        //共用一個右箭頭,如果展開則順時針旋轉(zhuǎn)90°選擇,否則不旋轉(zhuǎn)
        if (isExpanded) parentHolder.img_right.setRotation(90F);
        else parentHolder.img_right.setRotation(0F);

        return convertView;
    }

    /**
     * @return 返回指定位置對應(yīng)子視圖的視圖
     */
    @Override
    public View getChildView(final int groupPosition, final int childPosition, boolean isLastChild, View convertView, ViewGroup parent) {
        final ChildrenHolder childrenHolder;
        if (convertView == null) {
            convertView = LayoutInflater.from(context).inflate(R.layout.childrens_item, null);
            childrenHolder = new ChildrenHolder();
            childrenHolder.tvChild = convertView.findViewById(R.id.tv_child);
            childrenHolder.checkBox = convertView.findViewById(R.id.checkbox);
            convertView.setTag(childrenHolder);
        } else {
            childrenHolder = (ChildrenHolder) convertView.getTag();
        }


        //Log.e("666","班級:"+dataEntity.get(groupPosition).getTitle()+"    學(xué)生:"+dataEntity.get(groupPosition).getChildrenDataList().get(childPosition).getSubContent()+"   isChecked:"+dataEntity.get(groupPosition).getChildrenDataList().get(childPosition).isSelect());
        childrenHolder.checkBox.setChecked(dataEntity.get(groupPosition).getChildrenDataList().get(childPosition).isSelect());
        childrenHolder.tvChild.setText(dataEntity.get(groupPosition).getChildrenDataList().get(childPosition).getSubContent());
        childrenHolder.checkBox.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                boolean isChecked = !dataEntity.get(groupPosition).getChildrenDataList().get(childPosition).isSelect();
                dataEntity.get(groupPosition).getChildrenDataList().get(childPosition).setSelect(!isChecked);
                Log.e("groupPosition:" + groupPosition, "childPosition:" + childPosition + " isChecked:" + isChecked);
                checkBoxListener.checkStateListener(groupPosition, childPosition, isChecked);
            }
        });


        return convertView;
    }

    /**
     * 指定位置的子元素是否可選
     *
     * @param groupPosition 子元素組所在的位置
     * @param childPosition 子元素的位置
     * @return 返回是否可選
     */

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


    class ParentHolder {
        TextView tvParent;
        ImageView img_right;
    }


    class ChildrenHolder {
        TextView tvChild;
        CheckBox checkBox;
    }


    /**
     * 用于提供對外復(fù)選框修改通知接口
     */
    public interface CheckBoxListener {
        void checkStateListener(int groupPosition, int childPosition, boolean isChecked);
    }

    public void setCheckBoxListener(CheckBoxListener checkBoxListener) {
        this.checkBoxListener = checkBoxListener;
    }


    /**
     * 用于刷新更新后的數(shù)據(jù)
     */
    public void reFreshData(List<DataEntity> dataEntity) {
        this.dataEntity = dataEntity;
        notifyDataSetChanged();
    }


}

父布局使用的xml:


parent_item.png

子布局使用的xml:


childrens_item.png

注意在getGroupView中g(shù)etGroupView的控件不能設(shè)置一些搶占焦點的事件或?qū)傩裕琰c擊事件或者在代碼布局里設(shè)置了focusable屬性為true,都會導(dǎo)致無法展開子列表。

getChildView中子視圖,checkBox不調(diào)用setOnCheckedChangeListener是由于可能會因為選中的組展開觸發(fā)而導(dǎo)致混亂,這邊改為使用setOnClickListener,這是一種折中方案,因為狀態(tài)的改變不是來自事件onClick(也就是你點擊了不一定知道狀態(tài)是否成功更改),OnCheckChangedListener則是監(jiān)聽CheckBox的狀態(tài),成功后回調(diào)。

實體類DataEntity 代碼如下

public class DataEntity {
    private String title;//一級列表內(nèi)容
    private List<ChildrenData> childrenDataList;


    public DataEntity(String title, List<ChildrenData> childrenDataList) {
        this.title = title;
        this.childrenDataList = childrenDataList;
    }

    public List<ChildrenData> getChildrenDataList() {
        return childrenDataList;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public void setChildrenDataList(List<ChildrenData> childrenDataList) {
        this.childrenDataList = childrenDataList;
    }

    public static class ChildrenData{
        private String subContent;//子內(nèi)容
        private boolean select;//是否選中

        public ChildrenData(String subContent, boolean select) {
            this.subContent = subContent;
            this.select = select;
        }

        public String getSubContent() {
            return subContent;
        }

        public void setSubContent(String subContent) {
            this.subContent = subContent;
        }

        public boolean isSelect() {
            return select;
        }

        public void setSelect(boolean select) {
            this.select = select;
        }
    }
}

最后是在我們的主界面中實現(xiàn)代碼

public class MainActivity extends AppCompatActivity {
    private ExpandableListView expandableListView;
    private Button btn_select;
    private boolean selectAll;
    private List<DataEntity>  dataEntityList=new ArrayList<>();
    private StudentExpandableAdapter  studentExpandableAdapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        expandableListView =findViewById(R.id.listView);
        btn_select=findViewById(R.id.btn_select);
        initData();
        initAdapter();
        setOnClickEvent();
    }

    private void initData() {

        for(int i=0;i<5;i++){
            List<DataEntity.ChildrenData> childrenData=new ArrayList<>();
            for(int j=0;j<8;j++){
                DataEntity.ChildrenData children=new DataEntity.ChildrenData("學(xué)生"+(j+1),false);
                childrenData.add(children);
            }
            DataEntity dataEntity=new DataEntity((i+1)+"班",childrenData);
            dataEntityList.add(dataEntity);
        }
    }

    private void setOnClickEvent() {
        btn_select.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                selectAll=!selectAll;
                if(selectAll){
                    //遍歷設(shè)置全選
                    for(int i=0;i<dataEntityList.size();i++){
                        for(int j=0;j<dataEntityList.get(i).getChildrenDataList().size();j++){
                            dataEntityList.get(i).getChildrenDataList().get(j).setSelect(true);
                        }
                    }
                }else {
                    //遍歷設(shè)置取消全選
                    for(int i=0;i<dataEntityList.size();i++){
                        for(int j=0;j<dataEntityList.get(i).getChildrenDataList().size();j++){
                            dataEntityList.get(i).getChildrenDataList().get(j).setSelect(false);
                        }
                    }
                }


                studentExpandableAdapter.reFreshData(dataEntityList);

                btn_select.setText(selectAll? "取消全選":"全選");

            }
        });
    }

    private void initAdapter() {
        studentExpandableAdapter=new StudentExpandableAdapter(this,dataEntityList);
        expandableListView.setAdapter(studentExpandableAdapter);

        studentExpandableAdapter.setCheckBoxListener(new StudentExpandableAdapter.CheckBoxListener() {
            @Override
            public void checkStateListener(int groupPosition, int childPosition, boolean isChecked) {
                Log.e("MainActivity","isChecked:"+isChecked);
                dataEntityList.get(groupPosition).getChildrenDataList().get(childPosition).setSelect(isChecked);
                studentExpandableAdapter.reFreshData(dataEntityList);
            }
        });


        /**
         * 默認展開某個item
         * */
        //expandableListView.expandGroup(1);


    }
}

至此,簡單講完了ExpandableListView 的基礎(chǔ)使用,希望能對小伙伴們提供一點幫助。
最后附上該項目的github

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容