无限级联树形菜单

/**
 * 原理: ListView·s item + paddingLeft(level) + expand include
 * 系统中的数据Bean -> Node
 * 反射 + 注解
 * 1.List<T> -> List<Node>  将用户的数据转化为我们的树节点Node
 * 2.设置节点间的关联关系
 * 3.排序
 * 4.过滤出需要显示的数据
 *
 * Node {Object -> t}
 *
 * 1. 树形结构数据的知识
 * 2. 数据结构的进一步加强
 * 3. 反射 + 注解的使用(注解可以被命名规范所替代)
 * 4. 封装与设计我们的Adapter
 */

public class MainActivity extends AppCompatActivity {

    private ListView mTree;
    private SimpleTreeListViewAdapter<FileBean> mAdapter;
    private List<FileBean> mDatas;

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

        initView();
    }

    private void initView() {
        mTree = (ListView) findViewById(R.id.id_lv);
        initDatas();
        try {
            mAdapter = new SimpleTreeListViewAdapter<>(mTree, this, mDatas, 1);
            mTree.setAdapter(mAdapter);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        mAdapter.setListener(new TreeListViewAdapter.OnTreeNodeClickListener() {
            @Override
            public void onClick(Node node, int position) {
                if (node.isLeaf()) {
                    Toast.makeText(MainActivity.this, node.getName(), Toast.LENGTH_SHORT).show();
                } else {

                }
            }
        });

        mTree.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
            @Override
            public boolean onItemLongClick(AdapterView<?> parent, View view, final int position, long id) {

                final EditText et = new EditText(MainActivity.this);
                new AlertDialog.Builder(MainActivity.this).setTitle("Add Node").setView(et).setPositiveButton("sure", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        if (TextUtils.isEmpty(et.getText().toString())) {
                            return;
                        }
                        mAdapter.addExtraNode(position, et.getText().toString());
                    }
                }).setNegativeButton("Cancel", null).show();
                return true;
            }
        });
    }

    private void initDatas() {

        mDatas = new ArrayList<>();
        FileBean bean = new FileBean(1, 0, "根目录1");
        mDatas.add(bean);
        bean = new FileBean(2, 0, "根目录2");
        mDatas.add(bean);
        bean = new FileBean(3, 0, "根目录3");
        mDatas.add(bean);
        bean = new FileBean(4, 1, "根目录1-1");
        mDatas.add(bean);
        bean = new FileBean(5, 1, "根目录1-2");
        mDatas.add(bean);
        bean = new FileBean(6, 5, "根目录1-2-1");
        mDatas.add(bean);
        bean = new FileBean(7, 3, "根目录3-1");
        mDatas.add(bean);
        bean = new FileBean(8, 3, "根目录3-2");
        mDatas.add(bean);
    }
}
public class TreeHelper {

    private static final String TAG = TreeHelper.class.getSimpleName().toString();

    /**
     * 将用户的数据转化为节点数据
     *
     * @param datas
     * @param <T>
     * @return
     */
    public static <T> List<Node> convertDatas2Nodes(List<T> datas) throws IllegalAccessException {

        List<Node> nodes = new ArrayList<>();
        Node node = null;
        for (T t : datas) {
            int id = -1;
            int pid = -1;
            String label = null;
            Class clazz = t.getClass();
            Field[] fields = clazz.getDeclaredFields();
            for (Field field : fields) {
                if (field.getAnnotation(TreeNodeId.class) != null) {
//                    TreeNodeId annotation = field.getAnnotation(TreeNodeId.class);
//                    Class type = annotation.type();
                    field.setAccessible(true);
                    id = field.getInt(t);
                }
                if (field.getAnnotation(TreeNodePid.class) != null) {
                    field.setAccessible(true);
                    pid = field.getInt(t);
                }
                if (field.getAnnotation(TreeNodeLabel.class) != null) {
                    field.setAccessible(true);
                    label = (String) field.get(t);
                }
            }
            node = new Node(id, pid, label);
            nodes.add(node);
        }

        /**
         * 设置Node间的节点关系
         */
        for (int i = 0; i < nodes.size(); i++) {
            Node n = nodes.get(i);
            for (int j = i + 1; j < nodes.size(); j++) {
                Node m = nodes.get(j);
                if (m.getpId() == n.getId()) {
                    n.getChildren().add(m);
                    m.setParent(n);
                } else if (m.getId() == n.getpId()) {
                    m.getChildren().add(n);
                    n.setParent(m);
                }
            }
        }

        for (Node n : nodes) {
            setNodeIcon(n);
        }
        return nodes;
    }

    public static <T> List<Node> getSortedNodes(List<T> datas, int defaultExpandLevel) throws IllegalAccessException {

        List<Node> result = new ArrayList<>();
        List<Node> nodes = convertDatas2Nodes(datas);
        // 获得树的根节点
        List<Node> rootNodes = getRootNodes(nodes);

        for (Node node : rootNodes) {
            addNode(result, node, defaultExpandLevel, 1);
        }
        return result;
    }

    /**
     * 把一个节点的所有孩子节点都放入result
     *
     * 根目录1
     *      根目录1-1
     *      根目录1-2
     *          根目录1-2-1
     *
     * @@@@@: node = 根目录1 , level = 1
     * @@@: node = 根目录1 , level = 1
     * @@@@@: node = 根目录1-1 , level = 2
     * @@@: node = 根目录1 , level = 1
     * @@@@@: node = 根目录1-2 , level = 2
     * @@@: node = 根目录1-2 , level = 2
     * @@@@@: node = 根目录1-2-1 , level = 3
     */
    private static void addNode(List<Node> result, Node node, int defaultExpandLevel, int currentLevel) {

        result.add(node);
        Log.d("@@@@@", "node = " + node.getName() + " , level = " + currentLevel);
        if (defaultExpandLevel >= currentLevel) {
            node.setExpand(true);
        }
        if (node.isLeaf()) {
            return;
        }
        for (int i = 0; i < node.getChildren().size(); i++) {
            Log.d("@@@", "node = " + node.getName() + " , level = " + currentLevel);
            addNode(result, node.getChildren().get(i), defaultExpandLevel, currentLevel + 1);
        }
    }

    /**
     * 从所有节点中过滤出根节点
     * @param nodes
     * @return
     */
    private static List<Node> getRootNodes(List<Node> nodes) {

        List<Node> roots = new ArrayList<>();
        for (Node node : nodes) {
            if (node.isRoot()) {
                roots.add(node);
            }
        }
        return roots;
    }

    /**
     * Node设置图标
     */
    public static void setNodeIcon(Node n) {

        if (n.getChildren().size() > 0 && n.isExpand()) {
            n.setIcon(R.mipmap.tree_ex);
        } else if (n.getChildren().size()  > 0 && !n.isExpand()) {
            n.setIcon(R.mipmap.tree_ec);
        } else {
            n.setIcon(-1);
        }
    }

    /**
     * 过滤出可见的节点 过滤条件:是否是根节点或者父节点是否已展开
     * @param nodes
     * @return
     */
    public static List<Node> filterVisibleNodes(List<Node> nodes) {
        List<Node> result = new ArrayList<>();

        for (Node node : nodes) {
            if (node.isRoot() || node.isParentExpand()) {
                setNodeIcon(node);
                result.add(node);
            }
        }
        return result;
    }


}


public class Node {

    private int id;
    /** 根节点的pid = 0 */
    private int pId = 0;
    private String name;

    /** 树的层级 */
    private int level;
    /** 是否是展开 */
    private boolean isExpand = false;
    private int icon;

    private Node parent;
    private List<Node> children = new ArrayList();

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

    /**
     * 判断当前父节点的收缩展开
     * @return
     */
    // TODO 第一次运行时总出现3条数据,而且不能展开发现是此处循环调用
    public boolean isParentExpand() {
        if (parent == null) {
            return false;
        }
        return parent.isExpand();
    }

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

    /**
     * 得到当前节点的层级
     * @return
     */
    public int getLevel() {
        return parent == null ? 0 : parent.getLevel() + 1;
    }

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

    public boolean isExpand() {
        return isExpand;
    }

    public void setExpand(boolean expand) {

        isExpand = expand;
        if (!isExpand) {
            for (Node node : children) {
                node.setExpand(false);
            }
        }
    }
}
@Target(ElementType.FIELD) /** 注解声明的位置 */
@Retention(RetentionPolicy.RUNTIME) /** 什么时候可见 */
public @interface TreeNodeId {
//    Class type();
}
public class FileBean {

//    @TreeNodeId(type = String.class)
    @TreeNodeId
    private int id;

    @TreeNodePid
    private int pId;

    @TreeNodeLabel
    private String label;

    private String desc;

}


public abstract class TreeListViewAdapter<T> extends BaseAdapter {

    protected Context mContext;
    protected List<Node> mAllNodes; /** 所有的节点数据集合 */
    protected List<Node> mVisibleNodes; /** 实时更新的可见的节点数据集合 */
    protected LayoutInflater mInflater;
    protected ListView mTree;

    /**
     * 设置Node的点击回调
     */
    public interface OnTreeNodeClickListener {
        void onClick(Node node, int position);
    }

    private OnTreeNodeClickListener mListener;

    public void setListener(OnTreeNodeClickListener mListener) {
        this.mListener = mListener;
    }

    public TreeListViewAdapter(ListView tree, Context context, List<T> datas, int defaultExpandLevel) throws IllegalAccessException {
        mContext = context;
        mAllNodes = TreeHelper.getSortedNodes(datas, defaultExpandLevel);
        mVisibleNodes = TreeHelper.filterVisibleNodes(mAllNodes);
        mInflater = LayoutInflater.from(context);

        mTree = tree;
        mTree.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                expandOrCollapse(position);

                if (mListener != null) {
                    mListener.onClick(mVisibleNodes.get(position), position);
                }
            }
        });
    }

    /**
     * 点击收缩或者展开
     * @param position
     */
    private void expandOrCollapse(int position) {

        Node n = mVisibleNodes.get(position);
        if (n != null) {
            if (n.isLeaf()) return;
            // 引用的存在,此处修改后集合里面对应的数据就会更改
            n.setExpand(!n.isExpand());
            mVisibleNodes = TreeHelper.filterVisibleNodes(mAllNodes);
            notifyDataSetChanged();
        }
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {

        Node node = mVisibleNodes.get(position);
        convertView = getConvertView(node, position, convertView, parent);
        // 设置内边距 用paddingLeft来造成缩进显示成子节点的样式
        convertView.setPadding(node.getLevel() * 30, 3, 3, 3);
        return convertView;
    }

    public abstract View getConvertView(Node node, int position, View convertView, ViewGroup viewGroup);
}
public class SimpleTreeListViewAdapter<T> extends TreeListViewAdapter<T> {



    public SimpleTreeListViewAdapter(ListView tree, Context context, List<T> datas, int defaultExpandLevel) throws IllegalAccessException {
        super(tree, context, datas, defaultExpandLevel);
    }

    @Override
    public View getConvertView(Node node, int position, View convertView, ViewGroup viewGroup) {

        ViewHolder holder;
        if (convertView == null) {
            convertView = mInflater.inflate(R.layout.list_item_view, viewGroup, false);
            holder = new ViewHolder();
            holder.mIcon = (ImageView) convertView.findViewById(R.id.id_item_icon);
            holder.mText = (TextView) convertView.findViewById(R.id.id_item_text);
            convertView.setTag(holder);
        } else {
            holder = (ViewHolder) convertView.getTag();
        }
        if (node.getIcon() == -1) {
            holder.mIcon.setVisibility(View.INVISIBLE);
        } else {
            holder.mIcon.setVisibility(View.VISIBLE);
            holder.mIcon.setImageResource(node.getIcon());
        }
        holder.mText.setText(node.getName());
        return convertView;
    }

    /**
     * 动态插入节点
     *
     * @param position
     * @param string
     */
    public void addExtraNode(int position, String string)
    {
        Node node = mVisibleNodes.get(position);
        int indexOf = mAllNodes.indexOf(node);
        // Node
        Node extraNode = new Node(-1, node.getId(), string);
        extraNode.setParent(node);
        node.getChildren().add(extraNode);
        // 在集合对应位置添加数据,后面的数据整体向后挪动
        mAllNodes.add(indexOf + 1, extraNode);

        mVisibleNodes = TreeHelper.filterVisibleNodes(mAllNodes);
        notifyDataSetChanged();

    }

    private class ViewHolder {
        ImageView mIcon;
        TextView mText;
    }

}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值