/** * 原理: 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; } }
无限级联树形菜单
最新推荐文章于 2024-04-15 09:10:44 发布