zoukankan      html  css  js  c++  java
  • Android UI 之实现多级列表TreeView

        所谓TreeView就是在Windows中常见的多级列表树,在Android中系统只默认提供了ListView和ExpandableListView两种列表,最多只支持到二级列表的实现,所以如果想要实现三级和更多层次的列表,就需要我们自己来做一些处理了。

        其实这个效果很久以前就有人想办法实现了,但是实现的效果有一些问题,我的实现思路主要也是来自于网络,但是在其基础上修正了逻辑上的一些错误,做了一些优化。

        先来看一下效果:

    然后大体说一下思路:

        其实这里实现的多级列表只是一个视觉效果,我们看到的分级效果是由于每行的缩进不同造成的。比如在上面的效果中,山东省和广东省是级别最高的层次,山东省下的青岛市作为山东省的子项,我们增加他的左缩进,这样看起来就有了层次感了。其他的层次也是同理。

        也就是说,我们只用了一个ListView,工作的重点就在于不断变化ListView显示的数据,根据用户的操作,将数据修改为用户想要看到的数据内容,并根据每个数据项的不同,在显示效果上做不同的缩进处理,最终呈现出一个TreeView的效果。

        具体的实现思路参考下面的项目结构和具体代码:

    Element.java:

    package com.example.androidtreeviewdemo.treeview;
    /**
     * Element类
     * @author carrey
     *
     */
    public class Element {
    	/** 文字内容 */
    	private String contentText;
    	/** 在tree中的层级 */
    	private int level;
    	/** 元素的id */
    	private int id;
    	/** 父元素的id */
    	private int parendId;
    	/** 是否有子元素 */
    	private boolean hasChildren;
    	/** item是否展开 */
    	private boolean isExpanded;
    	
    	/** 表示该节点没有父元素,也就是level为0的节点 */
    	public static final int NO_PARENT = -1;
    	/** 表示该元素位于最顶层的层级 */
    	public static final int TOP_LEVEL = 0;
    	
    	public Element(String contentText, int level, int id, int parendId,
    			boolean hasChildren, boolean isExpanded) {
    		super();
    		this.contentText = contentText;
    		this.level = level;
    		this.id = id;
    		this.parendId = parendId;
    		this.hasChildren = hasChildren;
    		this.isExpanded = isExpanded;
    	}
    
    	public boolean isExpanded() {
    		return isExpanded;
    	}
    
    	public void setExpanded(boolean isExpanded) {
    		this.isExpanded = isExpanded;
    	}
    
    	public String getContentText() {
    		return contentText;
    	}
    
    	public void setContentText(String contentText) {
    		this.contentText = contentText;
    	}
    
    	public int getLevel() {
    		return level;
    	}
    
    	public void setLevel(int level) {
    		this.level = level;
    	}
    
    	public int getId() {
    		return id;
    	}
    
    	public void setId(int id) {
    		this.id = id;
    	}
    
    	public int getParendId() {
    		return parendId;
    	}
    
    	public void setParendId(int parendId) {
    		this.parendId = parendId;
    	}
    
    	public boolean isHasChildren() {
    		return hasChildren;
    	}
    
    	public void setHasChildren(boolean hasChildren) {
    		this.hasChildren = hasChildren;
    	}
    }
    

    TreeViewAdapter.java:

    package com.example.androidtreeviewdemo.treeview;
    
    import java.util.ArrayList;
    
    import com.example.androidtreeviewdemo.R;
    
    import android.view.LayoutInflater;
    import android.view.View;
    import android.view.ViewGroup;
    import android.widget.BaseAdapter;
    import android.widget.ImageView;
    import android.widget.TextView;
    /**
     * TreeViewAdapter
     * @author carrey
     *
     */
    public class TreeViewAdapter extends BaseAdapter {
    	/** 元素数据源 */
    	private ArrayList<Element> elementsData;
    	/** 树中元素 */
    	private ArrayList<Element> elements;
    	/** LayoutInflater */
    	private LayoutInflater inflater;
    	/** item的行首缩进基数 */
    	private int indentionBase;
    	
    	public TreeViewAdapter(ArrayList<Element> elements, ArrayList<Element> elementsData, LayoutInflater inflater) {
    		this.elements = elements;
    		this.elementsData = elementsData;
    		this.inflater = inflater;
    		indentionBase = 50;
    	}
    	
    	public ArrayList<Element> getElements() {
    		return elements;
    	}
    	
    	public ArrayList<Element> getElementsData() {
    		return elementsData;
    	}
    	
    	@Override
    	public int getCount() {
    		return elements.size();
    	}
    
    	@Override
    	public Object getItem(int position) {
    		return elements.get(position);
    	}
    
    	@Override
    	public long getItemId(int position) {
    		return position;
    	}
    
    	@Override
    	public View getView(int position, View convertView, ViewGroup parent) {
    		ViewHolder holder = null;
    		if (convertView == null) {
    			holder = new ViewHolder();
    			convertView = inflater.inflate(R.layout.treeview_item, null);
    			holder.disclosureImg = (ImageView) convertView.findViewById(R.id.disclosureImg);
    			holder.contentText = (TextView) convertView.findViewById(R.id.contentText);
    			convertView.setTag(holder);
    		} else {
    			holder = (ViewHolder) convertView.getTag();
    		}
    		Element element = elements.get(position);
    		int level = element.getLevel();
    		holder.disclosureImg.setPadding(
    				indentionBase * (level + 1), 
    				holder.disclosureImg.getPaddingTop(), 
    				holder.disclosureImg.getPaddingRight(), 
    				holder.disclosureImg.getPaddingBottom());
    		holder.contentText.setText(element.getContentText());
    		if (element.isHasChildren() && !element.isExpanded()) {
    			holder.disclosureImg.setImageResource(R.drawable.close);
    			//这里要主动设置一下icon可见,因为convertView有可能是重用了"设置了不可见"的view,下同。
    			holder.disclosureImg.setVisibility(View.VISIBLE);
    		} else if (element.isHasChildren() && element.isExpanded()) {
    			holder.disclosureImg.setImageResource(R.drawable.open);
    			holder.disclosureImg.setVisibility(View.VISIBLE);
    		} else if (!element.isHasChildren()) {
    			holder.disclosureImg.setImageResource(R.drawable.close);
    			holder.disclosureImg.setVisibility(View.INVISIBLE);
    		}
    		return convertView;
    	}
    	
    	/**
    	 * 优化Holder
    	 * @author carrey
    	 *
    	 */
    	static class ViewHolder{
    		ImageView disclosureImg;
    		TextView contentText;
    	}
    }
    

    TreeViewItemClickListener.java:

    package com.example.androidtreeviewdemo.treeview;
    
    import java.util.ArrayList;
    
    import android.view.View;
    import android.widget.AdapterView;
    import android.widget.AdapterView.OnItemClickListener;
    /**
     * TreeView item点击事件
     * @author carrey
     *
     */
    public class TreeViewItemClickListener implements OnItemClickListener {
    	/** adapter */
    	private TreeViewAdapter treeViewAdapter;
    	
    	public TreeViewItemClickListener(TreeViewAdapter treeViewAdapter) {
    		this.treeViewAdapter = treeViewAdapter;
    	}
    	
    	@Override
    	public void onItemClick(AdapterView<?> parent, View view, int position,
    			long id) {
    		//点击的item代表的元素
    		Element element = (Element) treeViewAdapter.getItem(position);
    		//树中的元素
    		ArrayList<Element> elements = treeViewAdapter.getElements();
    		//元素的数据源
    		ArrayList<Element> elementsData = treeViewAdapter.getElementsData();
    		
    		//点击没有子项的item直接返回
    		if (!element.isHasChildren()) {
    			return;
    		}
    		
    		if (element.isExpanded()) {
    			element.setExpanded(false);
    			//删除节点内部对应子节点数据,包括子节点的子节点...
    			ArrayList<Element> elementsToDel = new ArrayList<Element>();
    			for (int i = position + 1; i < elements.size(); i++) {
    				if (element.getLevel() >= elements.get(i).getLevel())
    					break;
    				elementsToDel.add(elements.get(i));
    			}
    			elements.removeAll(elementsToDel);
    			treeViewAdapter.notifyDataSetChanged();
    		} else {
    			element.setExpanded(true);
    			//从数据源中提取子节点数据添加进树,注意这里只是添加了下一级子节点,为了简化逻辑
    			int i = 1;//注意这里的计数器放在for外面才能保证计数有效
    			for (Element e : elementsData) {
    				if (e.getParendId() == element.getId()) {
    					e.setExpanded(false);
    					elements.add(position + i, e);
    					i ++;
    				}
    			}
    			treeViewAdapter.notifyDataSetChanged();
    		}
    	}
    
    }
    

    MainActivity.java:

    package com.example.androidtreeviewdemo;
    
    import java.util.ArrayList;
    
    import com.example.androidtreeviewdemo.treeview.Element;
    import com.example.androidtreeviewdemo.treeview.TreeViewAdapter;
    import com.example.androidtreeviewdemo.treeview.TreeViewItemClickListener;
    
    import android.os.Bundle;
    import android.app.Activity;
    import android.content.Context;
    import android.view.LayoutInflater;
    import android.view.Menu;
    import android.widget.ListView;
    
    public class MainActivity extends Activity {
    	/** 树中的元素集合 */
    	private ArrayList<Element> elements;
    	/** 数据源元素集合 */
    	private ArrayList<Element> elementsData;
    	
    	@Override
    	protected void onCreate(Bundle savedInstanceState) {
    		super.onCreate(savedInstanceState);
    		setContentView(R.layout.activity_main);
    		
    		LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    		
    		init();
    		
    		ListView treeview = (ListView) findViewById(R.id.treeview);
    		TreeViewAdapter treeViewAdapter = new TreeViewAdapter(
    				elements, elementsData, inflater);
    		TreeViewItemClickListener treeViewItemClickListener = new TreeViewItemClickListener(treeViewAdapter);
    		treeview.setAdapter(treeViewAdapter);
    		treeview.setOnItemClickListener(treeViewItemClickListener);
    	}
    	
    	private void init() {
    		elements = new ArrayList<Element>();
    		elementsData = new ArrayList<Element>();
    		
    		//添加节点  -- 节点名称,节点level,节点id,父节点id,是否有子节点,是否展开
    		
    		//添加最外层节点
    		Element e1 = new Element("山东省", Element.TOP_LEVEL, 0, Element.NO_PARENT, true, false);
    		
    		//添加第一层节点
    		Element e2 = new Element("青岛市", Element.TOP_LEVEL + 1, 1, e1.getId(), true, false);
    		//添加第二层节点
    		Element e3 = new Element("市南区", Element.TOP_LEVEL + 2, 2, e2.getId(), true, false);
    		//添加第三层节点
    		Element e4 = new Element("香港中路", Element.TOP_LEVEL + 3, 3, e3.getId(), false, false);
    		
    		//添加第一层节点
    		Element e5 = new Element("烟台市", Element.TOP_LEVEL + 1, 4, e1.getId(), true, false);
    		//添加第二层节点
    		Element e6 = new Element("芝罘区", Element.TOP_LEVEL + 2, 5, e5.getId(), true, false);
    		//添加第三层节点
    		Element e7 = new Element("凤凰台街道", Element.TOP_LEVEL + 3, 6, e6.getId(), false, false);
    		
    		//添加第一层节点
    		Element e8 = new Element("威海市", Element.TOP_LEVEL + 1, 7, e1.getId(), false, false);
    		
    		//添加最外层节点
    		Element e9 = new Element("广东省", Element.TOP_LEVEL, 8, Element.NO_PARENT, true, false);
    		//添加第一层节点
    		Element e10 = new Element("深圳市", Element.TOP_LEVEL + 1, 9, e9.getId(), true, false);
    		//添加第二层节点
    		Element e11 = new Element("南山区", Element.TOP_LEVEL + 2, 10, e10.getId(), true, false);
    		//添加第三层节点
    		Element e12 = new Element("深南大道", Element.TOP_LEVEL + 3, 11, e11.getId(), true, false);
    		//添加第四层节点
    		Element e13 = new Element("10000号", Element.TOP_LEVEL + 4, 12, e12.getId(), false, false);
    		
    		//添加初始树元素
    		elements.add(e1);
    		elements.add(e9);
    		//创建数据源
    		elementsData.add(e1);
    		elementsData.add(e2);
    		elementsData.add(e3);
    		elementsData.add(e4);
    		elementsData.add(e5);
    		elementsData.add(e6);
    		elementsData.add(e7);
    		elementsData.add(e8);
    		elementsData.add(e9);
    		elementsData.add(e10);
    		elementsData.add(e11);
    		elementsData.add(e12);
    		elementsData.add(e13);
    	}
    
    	@Override
    	public boolean onCreateOptionsMenu(Menu menu) {
    		// Inflate the menu; this adds items to the action bar if it is present.
    		getMenuInflater().inflate(R.menu.activity_main, menu);
    		return true;
    	}
    
    }
    

    treeview_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/disclosureImg"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerVertical="true"
            android:layout_alignParentLeft="true"/>
        
        <TextView 
            android:id="@+id/contentText"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerVertical="true"
            android:layout_toRightOf="@id/disclosureImg"/>
    
    </RelativeLayout>

    activity_main.xml:

    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity" >
    
        <ListView 
            android:id="@+id/treeview"
            android:layout_width="match_parent"
            android:layout_height="match_parent"/>
    
    </RelativeLayout>

    转载请注明:http://blog.csdn.net/carrey1989/article/details/10227165

    源码下载


  • 相关阅读:
    各国语言缩写列表,各国语言缩写-各国语言简称,世界各国域名缩写
    How to see log files in MySQL?
    git 设置和取消代理
    使用本地下载和管理的免费 Windows 10 虚拟机测试 IE11 和旧版 Microsoft Edge
    在Microsoft SQL SERVER Management Studio下如何完整输出NVARCHAR(MAX)字段或变量的内容
    windows 10 x64系统下在vmware workstation pro 15安装macOS 10.15 Catelina, 并设置分辨率为3840x2160
    在Windows 10系统下将Git项目签出到磁盘分区根目录的方法
    群晖NAS(Synology NAS)环境下安装GitLab, 并在Windows 10环境下使用Git
    使用V-2ray和V-2rayN搭建本地代理服务器供局域网用户连接
    windows 10 专业版安装VMware虚拟机碰到的坑
  • 原文地址:https://www.cnblogs.com/pangblog/p/3279932.html
Copyright © 2011-2022 走看看