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, "歌词一"));



	}



}

  

你可能感兴趣的:(android)