zoukankan      html  css  js  c++  java
  • android多层树形结构列表学习笔记

        android中既有组件只有 expandlistview来实现二级的分级目录,但是如果是任意级的分级目录呢?今天看到了一个DEMO,学习一下~

        一、filebean类

       simpleadapter的数据源的数据单元(需转化为nodes),包含以下三个属性和构造函数

    private int _id;
    	private int parentId;
    	private String name;
    	private long length;
    	public FileBean(int _id, int parentId, String name)
    	{
    		super();
    		this._id = _id;
    		this.parentId = parentId;
    		this.name = name;
    	}
    

      二、nodes类

      simpleadapter的数据源的数据单元(已转正),一大堆属性:ID、parentId、名称、级别、是否展开、下一级的孩子list、父节点、图标,可判断是否展开,是否为叶子节点,是否为根节点。

    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;
    
    	/**
    	 * 下一级的子Node
    	 */
    	private List<Node> children = new ArrayList<Node>();
    
    	/**
    	 * 父Node
    	 */
    	private Node parent;
    
    	public Node()
    	{
    	}
    
    	public Node(int id, int pId, String name)
    	{
    		super();
    		this.id = id;
    		this.pId = pId;
    		this.name = name;
    	}
    
    	public int getIcon()
    	{
    		return icon;
    	}
    
    	public void setIcon(int icon)
    	{
    		this.icon = icon;
    	}
    
    	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 void setLevel(int level)
    	{
    		this.level = level;
    	}
    
    	public boolean isExpand()
    	{
    		return isExpand;
    	}
    
    	public List<Node> getChildren()
    	{
    		return children;
    	}
    
    	public void setChildren(List<Node> children)
    	{
    		this.children = children;
    	}
    
    	public Node getParent()
    	{
    		return parent;
    	}
    
    	public void setParent(Node parent)
    	{
    		this.parent = parent;
    	}
    
    	/**
    	 * 是否为跟节点
    	 * 
    	 * @return
    	 */
    	public boolean isRoot()
    	{
    		return parent == null;
    	}
    
    	/**
    	 * 判断父节点是否展开
    	 * 
    	 * @return
    	 */
    	public boolean isParentExpand()
    	{
    		if (parent == null)
    			return false;
    		return parent.isExpand();
    	}
    
    	/**
    	 * 是否是叶子界点
    	 * 
    	 * @return
    	 */
    	public boolean isLeaf()
    	{
    		return children.size() == 0;
    	}
    
    	/**
    	 * 获取level
    	 */
    	public int getLevel()
    	{
    		return parent == null ? 0 : parent.getLevel() + 1;
    	}
    
    	/**
    	 * 设置展开
    	 * 
    	 * @param isExpand
    	 */
    	public void setExpand(boolean isExpand)
    	{
    		this.isExpand = isExpand;
    		if (!isExpand)
    		{
    
    			for (Node node : children)
    			{
    				node.setExpand(isExpand);
    			}
    		}
    	}
    
    }
    

      三、TreeListViewAdapter

        继承BaseAdapter的抽象类,主要控制listView的点击事件,并提供了获取convertview的抽象方法。

    public abstract class TreeListViewAdapter<T> extends BaseAdapter
    {
    
    	protected Context mContext;
    	/**
    	 * 存储所有可见的Node
    	 */
    	protected List<Node> mNodes;
    	protected LayoutInflater mInflater;
    	/**
    	 * 存储所有的Node
    	 */
    	protected List<Node> mAllNodes;
    
    	/**
    	 * 点击的回调接口
    	 */
    	private OnTreeNodeClickListener onTreeNodeClickListener;
    
    	public interface OnTreeNodeClickListener
    	{
    		void onClick(Node node, int position);
    	}
    
    	public void setOnTreeNodeClickListener(
    			OnTreeNodeClickListener onTreeNodeClickListener)
    	{
    		this.onTreeNodeClickListener = onTreeNodeClickListener;
    	}
    
    	/**
    	 * 
    	 * @param mTree
    	 * @param context
    	 * @param datas
    	 * @param defaultExpandLevel
    	 *            默认展开几级树
    	 * @throws IllegalArgumentException
    	 * @throws IllegalAccessException
    	 */
    	public TreeListViewAdapter(ListView mTree, Context context, List<T> datas,
    			int defaultExpandLevel) throws IllegalArgumentException,
    			IllegalAccessException
    	{
    		mContext = context;
    		/**
    		 * 对所有的Node进行排序
    		 */
    		mAllNodes = TreeHelper.getSortedNodes(datas, defaultExpandLevel);
    		/**
    		 * 过滤出可见的Node
    		 */
    		mNodes = TreeHelper.filterVisibleNode(mAllNodes);
    		mInflater = LayoutInflater.from(context);
    
    		/**
    		 * 设置节点点击时,可以展开以及关闭;并且将ItemClick事件继续往外公布
    		 */
    		mTree.setOnItemClickListener(new OnItemClickListener()
    		{
    			@Override
    			public void onItemClick(AdapterView<?> parent, View view,
    					int position, long id)
    			{
    				expandOrCollapse(position);
    
    				if (onTreeNodeClickListener != null)
    				{
    					onTreeNodeClickListener.onClick(mNodes.get(position),
    							position);
    				}
    			}
    
    		});
    
    	}
    
    	/**
    	 * 相应ListView的点击事件 展开或关闭某节点
    	 * 
    	 * @param position
    	 */
    	public void expandOrCollapse(int position)
    	{
    		Node n = mNodes.get(position);
    
    		if (n != null)// 排除传入参数错误异常
    		{
    			if (!n.isLeaf())
    			{
    				n.setExpand(!n.isExpand());
    				mNodes = TreeHelper.filterVisibleNode(mAllNodes);
    				notifyDataSetChanged();// 刷新视图
    			}
    		}
    	}
    
    	@Override
    	public int getCount()
    	{
    		return mNodes.size();
    	}
    
    	@Override
    	public Object getItem(int position)
    	{
    		return mNodes.get(position);
    	}
    
    	@Override
    	public long getItemId(int position)
    	{
    		return position;
    	}
    
    	@Override
    	public View getView(int position, View convertView, ViewGroup parent)
    	{
    		Node node = mNodes.get(position);
    		convertView = getConvertView(node, position, convertView, parent);
    		// 设置内边距
    		convertView.setPadding(node.getLevel() * 30, 3, 3, 3);
    		return convertView;
    	}
    
    	public abstract View getConvertView(Node node, int position,
    			View convertView, ViewGroup parent);
    
    }
    

      四、treehelper类

        提供了许多帮助我们的静态方法:

         convertData2Node:将传入的bean转化为node,并通过两两比较,得出他们父子间的关系。

         getRootNodes:遍历列表,筛选出root节点,返回父亲列表

         addNode:采用递归的方式,将属于一个父节点的子节点挂上去

         getSortedNodes:利用以上三个方法,将输入的无序nodes排列为层次分明的list

         filterVisibleNode:遍历列表,筛选出正在出现的notes,以此作为listview的填充数据(筛选的标准就是看expand)

         setNodeIcon:设置所有节点的icon,并根据是否展开设置不同的icon

    public class TreeHelper
    {
    	/**
    	 * 传入我们的普通bean,转化为我们排序后的Node
    	 * 
    	 * @param datas
    	 * @param defaultExpandLevel
    	 * @return
    	 * @throws IllegalArgumentException
    	 * @throws IllegalAccessException
    	 */
    	public static <T> List<Node> getSortedNodes(List<T> datas,
    			int defaultExpandLevel) throws IllegalArgumentException,
    			IllegalAccessException
    
    	{
    		List<Node> result = new ArrayList<Node>();
    		// 将用户数据转化为List<Node>
    		List<Node> nodes = convetData2Node(datas);
    		// 拿到根节点
    		List<Node> rootNodes = getRootNodes(nodes);
    		// 排序以及设置Node间关系
    		for (Node node : rootNodes)
    		{
    			addNode(result, node, defaultExpandLevel, 1);
    		}
    		return result;
    	}
    
    	/**
    	 * 过滤出所有可见的Node
    	 * 
    	 * @param nodes
    	 * @return
    	 */
    	public static List<Node> filterVisibleNode(List<Node> nodes)
    	{
    		List<Node> result = new ArrayList<Node>();
    
    		for (Node node : nodes)
    		{
    			// 如果为跟节点,或者上层目录为展开状态
    			if (node.isRoot() || node.isParentExpand())
    			{
    				setNodeIcon(node);
    				result.add(node);
    			}
    		}
    		return result;
    	}
    
    	/**
    	 * 将我们的数据转化为树的节点
    	 * 
    	 * @param datas
    	 * @return
    	 * @throws NoSuchFieldException
    	 * @throws IllegalAccessException
    	 * @throws IllegalArgumentException
    	 */
    	private static <T> List<Node> convetData2Node(List<T> datas)
    			throws IllegalArgumentException, IllegalAccessException
    
    	{
    		List<Node> nodes = new ArrayList<Node>();
    		Node node = null;
    
    		for (T t : datas)
    		{
    			int id = -1;
    			int pId = -1;
    			String label = null;
    			Class<? extends Object> clazz = t.getClass();
    			Field[] declaredFields = clazz.getDeclaredFields();
    			for (Field f : declaredFields)
    			{
    				if (f.getAnnotation(TreeNodeId.class) != null)
    				{
    					f.setAccessible(true);
    					id = f.getInt(t);
    				}
    				if (f.getAnnotation(TreeNodePid.class) != null)
    				{
    					f.setAccessible(true);
    					pId = f.getInt(t);
    				}
    				if (f.getAnnotation(TreeNodeLabel.class) != null)
    				{
    					f.setAccessible(true);
    					label = (String) f.get(t);
    				}
    				if (id != -1 && pId != -1 && label != null)
    				{
    					break;
    				}
    			}
    			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;
    	}
    
    	private static List<Node> getRootNodes(List<Node> nodes)
    	{
    		List<Node> root = new ArrayList<Node>();
    		for (Node node : nodes)
    		{
    			if (node.isRoot())
    				root.add(node);
    		}
    		return root;
    	}
    
    	/**
    	 * 把一个节点上的所有的内容都挂上去
    	 */
    	private static void addNode(List<Node> nodes, Node node,
    			int defaultExpandLeval, int currentLevel)
    	{
    
    		nodes.add(node);
    		if (defaultExpandLeval >= currentLevel)
    		{
    			node.setExpand(true);
    		}
    
    		if (node.isLeaf())
    			return;
    		for (int i = 0; i < node.getChildren().size(); i++)
    		{
    			addNode(nodes, node.getChildren().get(i), defaultExpandLeval,
    					currentLevel + 1);
    		}
    	}
    
    	/**
    	 * 设置节点的图标
    	 * 
    	 * @param node
    	 */
    	private static void setNodeIcon(Node node)
    	{
    		if (node.getChildren().size() > 0 && node.isExpand())
    		{
    			node.setIcon(R.drawable.tree_ex);
    		} else if (node.getChildren().size() > 0 && !node.isExpand())
    		{
    			node.setIcon(R.drawable.tree_ec);
    		} else
    			node.setIcon(-1);
    
    	}
    
    }
    

      五、SimpleTreeAdapter类

        listview的适配器,使用viewholder作为view的载体

    public class SimpleTreeAdapter<T> extends TreeListViewAdapter<T>
    {   
    	int[] resids = new int[]{R.drawable.delete,R.drawable.rename};
    	List<mButton> btList = new ArrayList<mButton>();
        PopupWindow popupWindow= new PopupWindow(mContext);;
    	public SimpleTreeAdapter(ListView mTree, Context context, List<T> datas,
    			int defaultExpandLevel) throws IllegalArgumentException,
    			IllegalAccessException
    	{
    		super(mTree, context, datas, defaultExpandLevel);
    	}
    
    	@Override
    	public View getConvertView(Node node , int position,  View convertView, ViewGroup parent)
    	{
    		ViewHolder viewHolder = null;
    		if (convertView == null)
    		{
    			convertView = mInflater.inflate(R.layout.list_item, parent, false);
    			viewHolder = new ViewHolder();
    			viewHolder.icon = (ImageView) convertView
    					.findViewById(R.id.id_treenode_icon);
    			viewHolder.label = (TextView) convertView
    					.findViewById(R.id.id_treenode_label);
    			viewHolder.operate = (Button)convertView.findViewById(R.id.id_treenode_operate);
    			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());
    		viewHolder.operate.setOnClickListener(new OnClickListener() {
    			//以下设置管理按钮的点击事件添加解除popupwindow
    			@Override
    			public void onClick(View v) {
    				// TODO Auto-generated method stub
    		    if (popupWindow.isShowing()) {
    		    	Log.d("test", "coming");
    				popupWindow.dismiss();
    				//popupWindow = null;
    			}
    		    else {
    		    	 LinearLayout pop_layout = (LinearLayout)mInflater.inflate(R.layout.pop_layout, null);
    				    fillButton(resids);
    				    for (mButton button: btList) {
    						pop_layout.addView(button);
    					}
    				
    				    popupWindow.setContentView(pop_layout);
    				    popupWindow.setWidth(LayoutParams.WRAP_CONTENT);
    				    popupWindow.setHeight(LayoutParams.WRAP_CONTENT);
    				   
    				    popupWindow.showAtLocation((((MainActivity)mContext).findViewById(R.id.container)), Gravity.CENTER, 0, 0);
    				    Log.d("test2",String.valueOf(popupWindow.isShowing()));
    			}
    			}
    		});
    		
    		return convertView;
    	}
    	/**
    	 * 利用给定资源,定制button,并添加到btlist中
    	 * */
       private void fillButton(int[] resids){
    	   if (!btList.isEmpty()) {
    		btList.clear();
    	}
    	   for (int i = 0; i < resids.length; i++) {
    		mButton button = ButtonUtil.getButton(mContext, i, resids[i]);
    		btList.add(button);
    	}
       }
    	private final class ViewHolder
    	{
    		ImageView icon;
    		TextView label;
    		Button operate;
    	}
    
    }
    

      六、mainactivity

          一个listview适配即可

    public class MainActivity extends Activity
    {
    	private List<Bean> mDatas = new ArrayList<Bean>();
    	private List<FileBean> mDatas2 = new ArrayList<FileBean>();
    	private ListView mTree;
    	private SimpleTreeAdapter<FileBean> mAdapter;
    
    	@Override
    	protected void onCreate(Bundle savedInstanceState)
    	{
    		super.onCreate(savedInstanceState);
    		setContentView(R.layout.activity_main);
    
    		initDatas();
    		mTree = (ListView) findViewById(R.id.id_tree);
    		try
    		{
    			mAdapter = new SimpleTreeAdapter<FileBean>(mTree, this, mDatas2, 10);
    			mAdapter.setOnTreeNodeClickListener(new OnTreeNodeClickListener()
    			{
    				@Override
    				public void onClick(Node node, int position)
    				{
    					if (node.isLeaf())
    					{
    						Toast.makeText(getApplicationContext(), node.getName(),
    								Toast.LENGTH_SHORT).show();
    					}
    				}
    
    			});
    
    		} catch (Exception e)
    		{
    			e.printStackTrace();
    		}
    		mTree.setAdapter(mAdapter);
    	}
    
    	private void initDatas()
    	{
    		mDatas.add(new Bean(1, 0, "根目录1"));
    		mDatas.add(new Bean(2, 0, "根目录2"));
    		mDatas.add(new Bean(3, 0, "根目录3"));
    		mDatas.add(new Bean(4, 0, "根目录4"));
    		mDatas.add(new Bean(5, 1, "子目录1-1"));
    		mDatas.add(new Bean(6, 1, "子目录1-2"));
    
    		mDatas.add(new Bean(7, 5, "子目录1-1-1"));
    		mDatas.add(new Bean(8, 2, "子目录2-1"));
    
    		mDatas.add(new Bean(9, 4, "子目录4-1"));
    		mDatas.add(new Bean(10, 4, "子目录4-2"));
    
    		mDatas.add(new Bean(11, 10, "子目录4-2-1"));
    		mDatas.add(new Bean(12, 10, "子目录4-2-3"));
    		mDatas.add(new Bean(13, 10, "子目录4-2-2"));
    		mDatas.add(new Bean(14, 9, "子目录4-1-1"));
    		mDatas.add(new Bean(15, 9, "子目录4-1-2"));
    		mDatas.add(new Bean(16, 9, "子目录4-1-3"));
    
    		mDatas2.add(new FileBean(1, 0, "音乐"));
    		mDatas2.add(new FileBean(2, 1, "流行音乐"));
    		mDatas2.add(new FileBean(3, 1, "爵士音乐"));
    		mDatas2.add(new FileBean(4, 1, "古典音乐"));
    		mDatas2.add(new FileBean(5, 2, "约定"));
    		mDatas2.add(new FileBean(6, 2, "大男人"));
    
    		mDatas2.add(new FileBean(7, 4, "bottoms up"));
    		mDatas2.add(new FileBean(8, 4, "goodbye allis"));
    
    		mDatas2.add(new FileBean(9, 7, "歌词一"));
    		mDatas2.add(new FileBean(10, 7, "歌词二"));
    		mDatas2.add(new FileBean(11, 7, "歌词三"));
    		mDatas2.add(new FileBean(12, 8, "歌词一"));
    
    	}
    
    }
    

      

  • 相关阅读:
    进制转换
    体验mssql-cli
    从Windows迁移SQL Server到Linux
    CentOS7脱机安装SQL Server 2017
    基础知识:数据类型优先级
    SQL Server 2016正式版安装(超多图)
    制造高CPU使用率的简单方法
    SQL Server启动的几种方法
    SQL Server 2016 RC0 安装(超多图)
    机器学习:Python实现单层Rosenblatt感知器
  • 原文地址:https://www.cnblogs.com/gangmiangongjue/p/4567104.html
Copyright © 2011-2022 走看看