Android studio 自定义树状下拉框,且用AlertDialog弹出

目录

一. 简介

1.1 效果图

二. 设计思路

2.1 数据设计

2.2 递归判断层级

三. 开发过程

3.1 创建一个我的节点类

3.2 再创建一个节点NodeVo类

3.3 把下面两张图片放入res/drawable

3.4 创建TreeUtil工具类

 3.5 右键创建一个Empty Activity 命名为:AlertTreectivity

3.6 在res/layout创建一个view_tree.xml文件

3.7 在res/layout下创建tree_item.xml文件

3.8 创建一个TreeListViewAdapter适配器类

3.9 创建一个属于自己自定义的MyTreeListViewAdapter适配器类

3.10 修改AlertTreectivity类里代码

3.11 更改AndroidManifest配置文件,使运行的时候访问AlertTreectivity类

四. 总结

五. 代码开源

六. 参考文献


一. 简介

        最近接到一个需求,需要开发一个树状的下拉框,于是就开始百度,经过大量的查找和自己一些的改进,终于做出一个实用简单的 Android 树状下拉框。

1.1 效果图

二. 设计思路

2.1 数据设计

        树状下拉框第一个想到的就层级的判断 ,例如谁是谁的上一层,谁是谁的下一层,所以数据只需要3个值就行了,分别是节点ID,节点名称,父节点;如下图表,湖南和广东都是中国的,所以他们的父节点都是1,深圳的级别在广东之下,所以父节点对应的广东ID就是3。

         设计表如下:

           ID             节点名称              父节点

            1                 中国                        0

            2                 湖南                        1

            3                 广东                        1

            4                 深圳                        3 

2.2 递归判断层级

        数据设计完成,下一步就是如何判断他们层级关系,就要用到递归了。由于本人是萌新,递归的逻辑是在网上参考别人的,所以大致原理是这样,让我们直接进入开发过程。

三. 开发过程

3.1 创建一个我的节点类

package top.xiewenwen.alerttreeview.vo;

public class MyNodeVo {
    /**
     * 节点Id
     */
    private int id;
    /**
     * 节点父id
     */
    private int pId;
    /**
     * 节点name
     */
    private String name;
    /**
     *
     */
    private String desc;
    /**
     * 节点名字长度
     */
    private long length;


    public MyNodeVo(int id, int pId, String name) {
        super();
        this.id = id;
        this.pId = pId;
        this.name = name;
    }

    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public int getPid() {
        return pId;
    }
    public void setPid(int pId) {
        this.pId = pId;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getDesc() {
        return desc;
    }
    public void setDesc(String desc) {
        this.desc = desc;
    }
    public long getLength() {
        return length;
    }
    public void setLength(long length) {
        this.length = length;
    }
}

3.2 再创建一个节点NodeVo类

package top.xiewenwen.alerttreeview.vo;

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

public class NodeVo {
    /**
     * 节点id
     */
    private int id;
    /**
     * 父节点id
     */
    private int pId;
    /**
     * 是否展开
     */
    private boolean isExpand = false;
    private boolean isChecked = false;
    private boolean isHideChecked = true;
    /**
     * 节点名字
     */
    private String name;
    /**
     * 节点级别
     */
    private int level;
    /**
     * 节点展示图标
     */
    private int icon;
    /**
     * 节点所含的子节点
     */
    private List<NodeVo> childrenNodeVos = new ArrayList<>();
    /**
     * 节点的父节点
     */
    private NodeVo parent;

    public NodeVo(int id, int pId, String name) {
        super();
        this.id = id;
        this.pId = pId;
        this.name = name;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public int getpId() {
        return pId;
    }

    public void setpId(int pId) {
        this.pId = pId;
    }

    public boolean isExpand() {
        return isExpand;
    }

    /**
     * 当父节点收起,其子节点也收起
     * @param isExpand
     */
    public void setExpand(boolean isExpand) {
        this.isExpand = isExpand;
        if (!isExpand) {

            for (NodeVo NodeVo : childrenNodeVos) {
                NodeVo.setExpand(isExpand);
            }
        }
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getLevel() {
        return parent == null ? 0 : parent.getLevel() + 1;
    }

    public void setLevel(int level) {
        this.level = level;
    }

    public int getIcon() {
        return icon;
    }

    public void setIcon(int icon) {
        this.icon = icon;
    }

    public List<NodeVo> getChildrenNodeVos() {
        return childrenNodeVos;
    }

    public NodeVo getParent() {
        return parent;
    }

    public void setParent(NodeVo parent) {
        this.parent = parent;
    }

    /**
     * 判断是否是根节点
     *
     * @return
     */
    public boolean isRoot() {
        return parent == null;
    }

    /**
     * 判断是否是叶子节点
     *
     * @return
     */
    public boolean isLeaf() {
        return childrenNodeVos.size() == 0;
    }


    /**
     * 判断父节点是否展开
     *
     * @return
     */
    public boolean isParentExpand()
    {
        if (parent == null)
            return false;
        return parent.isExpand();
    }

    public boolean isChecked() {
        return isChecked;
    }

    public void setChecked(boolean isChecked) {
        this.isChecked = isChecked;
    }

    public boolean isHideChecked() {
        return isHideChecked;
    }

    public void setHideChecked(boolean isHideChecked) {
        this.isHideChecked = isHideChecked;
    }
}

3.3 把下面两张图片放入res/drawable

tree_econpand.png  命名为:tree_econpand.png                  命名为:tree_expand.png

3.4 创建TreeUtil工具类

package top.xiewenwen.alerttreeview.util;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;

import top.xiewenwen.alerttreeview.R;
import top.xiewenwen.alerttreeview.vo.NodeVo;

public class TreeUtil {

    /**
     * 根据所有节点获取可见节点
     *
     * @param allNodeVos
     * @return
     */
    public static List<NodeVo> filterVisibleNodeVo(List<NodeVo> allNodeVos) {
        List<NodeVo> visibleNodeVos = new ArrayList<NodeVo>();
        for (NodeVo NodeVo : allNodeVos) {
            // 如果为根节点,或者上层目录为展开状态
            if (NodeVo.isRoot() || NodeVo.isParentExpand()) {
                setNodeVoIcon(NodeVo);
                visibleNodeVos.add(NodeVo);
            }
        }
        return visibleNodeVos;
    }

    /**
     * 获取排序的所有节点
     *
     * @param datas
     * @param defaultExpandLevel
     * @return
     * @throws IllegalArgumentException
     * @throws IllegalAccessException
     */
    public static <T> List<NodeVo> getSortedNodeVos(List<T> datas,
                                                int defaultExpandLevel, boolean isHide)
            throws IllegalAccessException, IllegalArgumentException {
        List<NodeVo> sortedNodeVos = new ArrayList<NodeVo>();
        // 将用户数据转化为List<NodeVo>
        List<NodeVo> NodeVos = convertData2NodeVos(datas, isHide);
        // 拿到根节点
        List<NodeVo> rootNodeVos = getRootNodeVos(NodeVos);
        // 排序以及设置NodeVo间关系
        for (NodeVo NodeVo : rootNodeVos) {
            addNodeVo(sortedNodeVos, NodeVo, defaultExpandLevel, 1);
        }
        return sortedNodeVos;
    }

    /**
     * 把一个节点上的所有的内容都挂上去
     */
    private static void addNodeVo(List<NodeVo> NodeVos, NodeVo NodeVo,
                                int defaultExpandLeval, int currentLevel) {

        NodeVos.add(NodeVo);
        if (defaultExpandLeval >= currentLevel) {
            NodeVo.setExpand(true);
        }

        if (NodeVo.isLeaf())
            return;
        for (int i = 0; i < NodeVo.getChildrenNodeVos().size(); i++) {
            addNodeVo(NodeVos, NodeVo.getChildrenNodeVos().get(i), defaultExpandLeval,
                    currentLevel + 1);
        }
    }

    /**
     * 获取所有的根节点
     *
     * @param NodeVos
     * @return
     */
    public static List<NodeVo> getRootNodeVos(List<NodeVo> NodeVos) {
        List<NodeVo> rootNodeVos = new ArrayList<NodeVo>();
        for (NodeVo NodeVo : NodeVos) {
            if (NodeVo.isRoot()) {
                rootNodeVos.add(NodeVo);
            }
        }

        return rootNodeVos;
    }

    /**
     * 将泛型datas转换为NodeVo
     *
     * @param datas
     * @return
     * @throws IllegalArgumentException
     * @throws IllegalAccessException
     */
    public static <T> List<NodeVo> convertData2NodeVos(List<T> datas, boolean isHide)
            throws IllegalAccessException, IllegalArgumentException {
        List<NodeVo> NodeVos = new ArrayList<NodeVo>();
        NodeVo NodeVo = null;

        for (T t : datas) {
            int id = -1;
            int pId = -1;
            String name = null;

            Class<? extends Object> clazz = t.getClass();
            Field[] declaredFields = clazz.getDeclaredFields();
            /**
             * 与MyNodeVoBean实体一一对应
             */
            for (Field f : declaredFields) {
                if ("id".equals(f.getName())) {
                    f.setAccessible(true);
                    id = f.getInt(t);
                }

                if ("pId".equals(f.getName())) {
                    f.setAccessible(true);
                    pId = f.getInt(t);
                }

                if ("name".equals(f.getName())) {
                    f.setAccessible(true);
                    name = (String) f.get(t);
                }

                if ("desc".equals(f.getName())) {
                    continue;
                }

                if ("length".equals(f.getName())) {
                    continue;
                }

                if (id == -1 && pId == -1 && name == null) {
                    break;
                }
            }

            NodeVo = new NodeVo(id, pId, name);
            NodeVo.setHideChecked(isHide);
            NodeVos.add(NodeVo);
        }

        /**
         * 比较NodeVos中的所有节点,分别添加children和parent
         */
        for (int i = 0; i < NodeVos.size(); i++) {
            NodeVo n = NodeVos.get(i);
            for (int j = i + 1; j < NodeVos.size(); j++) {
                NodeVo m = NodeVos.get(j);
                if (n.getId() == m.getpId()) {
                    n.getChildrenNodeVos().add(m);
                    m.setParent(n);
                } else if (n.getpId() == m.getId()) {
                    n.setParent(m);
                    m.getChildrenNodeVos().add(n);
                }
            }
        }

        for (NodeVo n : NodeVos) {
            setNodeVoIcon(n);
        }
        return NodeVos;
    }

    /**
     * 设置打开,关闭icon
     *
     * @param NodeVo
     */
    public static void setNodeVoIcon(NodeVo NodeVo) {
        if (NodeVo.getChildrenNodeVos().size() > 0 && NodeVo.isExpand()) {
            NodeVo.setIcon(R.drawable.tree_expand);
        } else if (NodeVo.getChildrenNodeVos().size() > 0 && !NodeVo.isExpand()) {
            NodeVo.setIcon(R.drawable.tree_econpand);
        } else
            NodeVo.setIcon(-1);
    }

    public static void setNodeVoChecked(NodeVo NodeVo, boolean isChecked) {
        // 自己设置是否选择
        NodeVo.setChecked(isChecked);
        /**
         * 非叶子节点,子节点处理
         */
        setChildrenNodeVoChecked(NodeVo, isChecked);
        /** 父节点处理 */
        setParentNodeVoChecked(NodeVo);

    }

    /**
     * 非叶子节点,子节点处理
     */
    private static void setChildrenNodeVoChecked(NodeVo NodeVo, boolean isChecked) {
        NodeVo.setChecked(isChecked);
        if (!NodeVo.isLeaf()) {
            for (NodeVo n : NodeVo.getChildrenNodeVos()) {
                // 所有子节点设置是否选择
                setChildrenNodeVoChecked(n, isChecked);
            }
        }
    }

    /**
     * 设置父节点选择
     *
     * @param NodeVo
     */
    private static void setParentNodeVoChecked(NodeVo NodeVo) {

        /** 非根节点 */
        if (!NodeVo.isRoot()) {
            NodeVo rootNodeVo = NodeVo.getParent();
            boolean isAllChecked = true;
            for (NodeVo n : rootNodeVo.getChildrenNodeVos()) {
                if (!n.isChecked()) {
                    isAllChecked = false;
                    break;
                }
            }

            if (isAllChecked) {
                rootNodeVo.setChecked(true);
            } else {
                rootNodeVo.setChecked(false);
            }
            setParentNodeVoChecked(rootNodeVo);
        }
    }

}

 3.5 右键创建一个Empty Activity 命名为:AlertTreectivity

创建Activity会在res/layout自动创建一个activity_alert_treectivity.xml文件,修改这个布局文件里的代码

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".AlertTreectivity">

    <EditText
        android:id="@+id/getTreeIdName"
        android:layout_width="0dp"
        android:layout_height="48dp"
        android:layout_marginStart="32dp"
        android:layout_marginTop="32dp"
        android:layout_marginEnd="32dp"
        android:ems="10"
        android:hint="请点击下面的选择按钮"
        android:inputType="textPersonName"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <Button
        android:id="@+id/choice"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="20dp"
        android:text="选择"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/getTreeIdName" />
</androidx.constraintlayout.widget.ConstraintLayout>

3.6 在res/layout创建一个view_tree.xml文件

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/white">


    <ListView
        android:id="@+id/tree_lv"
        android:layout_width="0dp"
        android:layout_height="430dp"
        android:layout_marginStart="32dp"
        android:layout_marginTop="32dp"
        android:layout_marginEnd="32dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="1.0"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent">

    </ListView>

    <Button
        android:id="@+id/cancel"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="取消"
        android:textSize="22sp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/tree_lv" />

</androidx.constraintlayout.widget.ConstraintLayout>

3.7 在res/layout下创建tree_item.xml文件

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

    <ImageView
        android:id="@+id/id_treenode_icon"
        android:layout_width="20dp"
        android:layout_height="20dp"
        android:layout_centerVertical="true"
        android:layout_marginLeft="5dp"
        android:layout_marginRight="5dp"
        android:src="@drawable/tree_econpand" />

    <TextView
        android:id="@+id/id_treenode_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerVertical="true"
        android:layout_toRightOf="@id/id_treenode_icon"
        android:textSize="18sp" />

</RelativeLayout>

3.8 创建一个TreeListViewAdapter适配器类

package top.xiewenwen.alerttreeview.Adapter;

import java.util.List;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ListView;
import android.widget.RelativeLayout;

import top.xiewenwen.alerttreeview.util.TreeUtil;
import top.xiewenwen.alerttreeview.vo.NodeVo;

/**
 * tree适配器
 *
 * @param <T>
 */
public abstract class TreeListViewAdapter<T> extends BaseAdapter {

    protected Context mContext;
    /**
     * 存储所有可见的NodeVo
     */
    protected List<NodeVo> mNodeVos;
    protected LayoutInflater mInflater;
    /**
     * 存储所有的NodeVo
     */
    protected List<NodeVo> mAllNodeVos;

    /**
     * 点击的回调接口
     */
    private OnTreeNodeClickListener onTreeNodeVoClickListener;

    public interface OnTreeNodeClickListener {
        /**
         * 处理NodeVo click事件
         *
         * @param Node 节点对象
         * @param position 位置
         */
        void onClick(NodeVo Node, int position);

    }

    public void setOnTreeNodeClickListener(
            OnTreeNodeClickListener onTreeNodeVoClickListener) {
        this.onTreeNodeVoClickListener = onTreeNodeVoClickListener;
    }

    /**
     * @param mTree
     * @param context
     * @param datas
     * @param defaultExpandLevel 默认展开几级树
     * @throws IllegalArgumentException
     * @throws IllegalAccessException
     */
    public TreeListViewAdapter(ListView mTree, Context context, List<T> datas, int defaultExpandLevel, boolean isHide)
            throws IllegalArgumentException, IllegalAccessException {
        mContext = context;
        /**
         * 对所有的Node进行排序
         */
        mAllNodeVos = TreeUtil
                .getSortedNodeVos(datas, defaultExpandLevel, isHide);
        /**
         * 过滤出可见的Node
         */
        mNodeVos = TreeUtil.filterVisibleNodeVo(mAllNodeVos);
        mInflater = LayoutInflater.from(context);

        /**
         * 设置节点点击时,可以展开以及关闭;并且将ItemClick事件继续往外公布
         */
        mTree.setOnItemClickListener((parent, view, position, id) -> {
            expandOrCollapse(position);

            if (onTreeNodeVoClickListener != null) {
                onTreeNodeVoClickListener.onClick(mNodeVos.get(position), position);
            }
        });

    }

    /**
     * 相应ListView的点击事件 展开或关闭某节点
     *
     * @param position 位置
     */
    public void expandOrCollapse(int position) {
        NodeVo n = mNodeVos.get(position);

        if (n != null)// 排除传入参数错误异常
        {
            if (!n.isLeaf()) {
                n.setExpand(!n.isExpand());
                mNodeVos = TreeUtil.filterVisibleNodeVo(mAllNodeVos);
                notifyDataSetChanged();// 刷新视图
            }
        }
    }

    @Override
    public int getCount() {
        return mNodeVos.size();
    }

    @Override
    public Object getItem(int position) {
        return mNodeVos.get(position);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public View getView(final int position, View convertView, ViewGroup parent) {
        final NodeVo NodeVo = mNodeVos.get(position);

        convertView = getConvertView(NodeVo, position, convertView, parent);
        // 设置内边距
        convertView.setPadding(NodeVo.getLevel() * 30, 3, 3, 3);
        if (!NodeVo.isHideChecked()) {
            //获取各个节点所在的父布局
            RelativeLayout myView = (RelativeLayout) convertView;
            //父布局下的CheckBox
            myView.getChildAt(1);
        }

        return convertView;
    }

    public abstract View getConvertView(NodeVo NodeVo, int position, View convertView, ViewGroup parent);

}

3.9 创建一个属于自己自定义的MyTreeListViewAdapter适配器类

package top.xiewenwen.alerttreeview.Adapter;

import java.util.List;

import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;

import top.xiewenwen.alerttreeview.R;
import top.xiewenwen.alerttreeview.vo.NodeVo;

public class MyTreeListViewAdapter<T> extends TreeListViewAdapter<T> {

    public MyTreeListViewAdapter(ListView mTree, Context context,
                                 List<T> datas, int defaultExpandLevel, boolean isHide)
            throws IllegalArgumentException, IllegalAccessException {
        super(mTree, context, datas, defaultExpandLevel, isHide);
    }

    @Override
    public View getConvertView(NodeVo node, int position, View convertView,
                               ViewGroup parent) {
        ViewHolder viewHolder;
        if (convertView == null) {
            convertView = mInflater.inflate(R.layout.tree_item, parent, false);
            viewHolder = new ViewHolder();
            viewHolder.icon = convertView.findViewById(R.id.id_treenode_icon);
            viewHolder.label = convertView.findViewById(R.id.id_treenode_name);
            convertView.setTag(viewHolder);
        } else {
            viewHolder = (ViewHolder) convertView.getTag();
        }
        if (node.getIcon() == -1) {
            viewHolder.icon.setVisibility(View.INVISIBLE);
        } else {
            viewHolder.icon.setVisibility(View.VISIBLE);
            viewHolder.icon.setImageResource(node.getIcon());
        }

        viewHolder.label.setText(node.getName());
        return convertView;
    }

    private static final class ViewHolder {
        ImageView icon;
        TextView label;
    }
}

3.10 修改AlertTreectivity类里代码

package top.xiewenwen.alerttreeview;

import androidx.appcompat.app.AppCompatActivity;

import android.app.AlertDialog;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Button;
import android.widget.ListView;
import android.widget.TextView;

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

import top.xiewenwen.alerttreeview.Adapter.MyTreeListViewAdapter;
import top.xiewenwen.alerttreeview.vo.MyNodeVo;

public class AlertTreectivity extends AppCompatActivity {

    private final List<MyNodeVo> mDatas = new ArrayList<>();
    private AlertDialog mAlert;
    private Button choice;
    private MyTreeListViewAdapter<MyNodeVo> adapter;
    private TextView getTreeIdName;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_alert_treectivity);

        getTreeIdName=findViewById(R.id.getTreeIdName);
        choice=findViewById(R.id.choice);

        initTreeListView();
    }

    private void initTreeListView() {
        // 从布局文件中加载 AlertDialog 需要显示的 view
        LayoutInflater inflater = this.getLayoutInflater();
        View customView = inflater.inflate(R.layout.view_tree, null, false);

        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        // 指定 AlertDialog 需要显示的 view
        builder.setView(customView);
        // 点击空白处是否自动隐藏对话框(默认值为 true)
        builder.setCancelable(true);
        // 创建 AlertDialog 对象
        mAlert = builder.create();

        // 弹出自定义对话框
        choice.setOnClickListener(v -> mAlert.show());

        // 设置自定义 view 中的显示内容
        ListView treeLv = customView.findViewById(R.id.tree_lv);
        initDatas();
        try {
            adapter = new MyTreeListViewAdapter<>(treeLv, this,
                    mDatas, 0, true);

            adapter.setOnTreeNodeClickListener((node, position) -> {
                if (node.isLeaf()) {
                    getTreeIdName.setText(node.getName());
                    mAlert.dismiss();
                }
            });
        } catch (IllegalArgumentException | IllegalAccessException e) {
            e.printStackTrace();
        }
        treeLv.setAdapter(adapter);

        // 自定义 view 中的关闭按钮的点击事件
        customView.findViewById(R.id.cancel).setOnClickListener(v -> mAlert.dismiss());
    }

    private void initDatas() {
        mDatas.add(new MyNodeVo(1, 0, "中国"));
        mDatas.add(new MyNodeVo(2, 1, "湖南"));
        mDatas.add(new MyNodeVo(3, 1, "广东"));
        mDatas.add(new MyNodeVo(4, 3, "深圳"));
    }

}

3.11 更改AndroidManifest配置文件,使运行的时候访问AlertTreectivity类

  <activity
            android:name=".AlertTreectivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

四. 总结

学习了3种数据可以创建可视化的树状结构,用递归来循环判断他们的子集关系,学会了怎么通过A获取到B的View视图,学会了怎么重写Adapter适配器类。

五. 代码开源

Android TreeListView: 安卓点击按钮弹出树状下拉框,选择后显示在EditText上 (gitee.com)

个人博客:橘子披の博客 (xiewenwen.top)

六. 参考文献

TreeListView: TreeListView (gitee.com)

一手遮天 Android - view(弹出类): PopupWindow 基础 - webabcd - 博客园 (cnblogs.com)

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值