標籤:des android style blog http io ar os 使用
樹狀列表前端挺常用的,還有人專門寫過Ztree,Android中有的時候也需要使用到樹狀列表,上篇文章寫了一下ExpandableListView,ExpandableListView最多支援兩級結構,Android中沒有三層結構的組件,這個時候需要自己去擴充,可以擴充ExpandableListView,也可以選擇擴充ListView。個人覺得擴充ListView更簡單一點,多級列表你可以一次性載入出來,也可以分級載入出來,一般分級比較好,點了某一級可以控制點擊事件然後去載入子級,這樣實現起來會想容易一點,也好理解一點。
基礎維護
建立一個activity_tree.xml布局檔案,裡面放一個單獨的ListView:
<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="com.example.googletree.TreeActivity" > <ListView android:id="@+id/tree_list" android:layout_width="match_parent" android:layout_height="match_parent"/></RelativeLayout>
tree_item布局檔案,其中包括一個圖片,一個文本:
<?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/homeImg" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerVertical="true" android:layout_alignParentLeft="true"/> <TextView android:id="@+id/treeText" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerVertical="true" android:layout_toRightOf="@id/homeImg"/> </RelativeLayout>
Demo實現
需要展示一個樹狀結構的資料,先定義一個TreeNode類,需要展示資料其中主要講下幾個欄位,父級和子級之間的關係你可以用ParentId判斷也可以使用Code來區分,看個人喜好:
文本:contentText
ID號:ID
ParentId:父級ID
hasChildren:判斷是不是末級
isExpanded:item是否展開
public class TreeNode {/** 文字內容 */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 TreeNode(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;}}
為了填充視圖的中ListView,需要自訂TreeAdapter,之前的文章關於ListView中使用Adapter介紹過,如果需要具體怎麼使用的可以看下之前的文章,主要使用了一個ViewHolder的最佳化:
public class TreeAdapter extends BaseAdapter {/** 所有的資料集合 */private ArrayList<TreeNode> allNodes;/** 頂層元素結合*/private ArrayList<TreeNode> topNodes;/** LayoutInflater */private LayoutInflater inflater;/** item的行首縮排基數 */private int indentionBase;public TreeViewAdapter(ArrayList<TreeNode> topNodes, ArrayList<TreeNode> allNodes, LayoutInflater inflater) {this.topNodes = topNodes;this.allNodes = allNodes;this.inflater = inflater;indentionBase =20;}public ArrayList<TreeNode> getTopNodes() {return topNodes;}public ArrayList<TreeNode> getAllNodes() {return allNodes;}@Overridepublic int getCount() {return topNodes.size();}@Overridepublic Object getItem(int position) {return topNodes.get(position);}@Overridepublic long getItemId(int position) {return position;}@Overridepublic View getView(int position, View convertView, ViewGroup parent) {ViewHolder holder = null;if (convertView == null) {holder = new ViewHolder();convertView = inflater.inflate(R.layout.tree_item, null);holder.homeImg = (ImageView) convertView.findViewById(R.id.homeImg);holder.treeText = (TextView) convertView.findViewById(R.id.treeText);convertView.setTag(holder);} else {holder = (ViewHolder) convertView.getTag();}TreeNode element = topNodes.get(position);int level =element.getLevel();holder.homeImg.setPadding(indentionBase * (level + 1), holder.homeImg.getPaddingTop(), holder.homeImg.getPaddingRight(), holder.homeImg.getPaddingBottom());holder.treeText.setText(element.getContentText());if (element.isHasChildren() && !element.isExpanded()) {holder.homeImg.setImageResource(R.drawable.open);//這裡要主動設定一下icon可見,因為convertView有可能是重用了"設定了不可見"的view,下同。holder.homeImg.setVisibility(View.VISIBLE);} else if (element.isHasChildren() && element.isExpanded()) {holder.homeImg.setImageResource(R.drawable.open);holder.homeImg.setVisibility(View.VISIBLE);} else if (!element.isHasChildren()) {holder.homeImg.setImageResource(R.drawable.open);holder.homeImg.setVisibility(View.VISIBLE);}return convertView;}static class ViewHolder{ImageView homeImg;TextView treeText;}}
由於每次都是載入一層節點,需要自己控制點擊事件,自訂一個TreeItemClickListener來監聽事件:
public class TreeItemClickListener implements OnItemClickListener {/** 定義的適配器 */private TreeViewAdapter treeViewAdapter;public TreeViewItemClickListener(TreeViewAdapter treeViewAdapter) {this.treeViewAdapter = treeViewAdapter;}@Overridepublic void onItemClick(AdapterView<?> parent, View view, int position,long id) {//點擊的item代表的元素TreeNode treeNode = (TreeNode) treeViewAdapter.getItem(position);//樹中頂層的元素ArrayList<TreeNode> topNodes = treeViewAdapter.getTopNodes();//元素的資料來源ArrayList<TreeNode> allNodes = treeViewAdapter.getAllNodes();//點擊沒有子項的item直接返回if (!treeNode.isHasChildren()) {return;}if (treeNode.isExpanded()) {treeNode.setExpanded(false);//刪除節點內部對應子節點資料,包括子節點的子節點...ArrayList<TreeNode> elementsToDel = new ArrayList<TreeNode>();for (int i = position + 1; i < topNodes.size(); i++) {if (treeNode.getLevel() >= topNodes.get(i).getLevel())break;elementsToDel.add(topNodes.get(i));}topNodes.removeAll(elementsToDel);treeViewAdapter.notifyDataSetChanged();} else {treeNode.setExpanded(true);//從資料來源中提取子節點資料添加進樹,注意這裡只是添加了下一級子節點,為了簡化邏輯int i = 1;//注意這裡的計數器放在for外面才能保證計數有效for (TreeNode e : allNodes) {if (e.getParendId() == treeNode.getId()) {e.setExpanded(false);topNodes.add(position + i, e);i ++;}}treeViewAdapter.notifyDataSetChanged();}}}
activity初始化資料:
private void init() {topNodes = new ArrayList<TreeNode>();allNodes = new ArrayList<TreeNode>();//添加節點 -- 節點名稱,節點level,節點id,父節點id,是否有子節點,是否展開//添加最外層節點TreeNode node1= new TreeNode("北京市", TreeNode.TOP_LEVEL, 0, TreeNode.NO_PARENT, true, false);//添加第一層節點TreeNode node2= new TreeNode("海澱區", TreeNode.TOP_LEVEL + 1, 1, node1.getId(), true, false);//添加第二層節點TreeNode node3= new TreeNode("西二旗", TreeNode.TOP_LEVEL + 2, 2, node2.getId(), true, false);//添加第三層節點//TreeNode node7= new TreeNode("輝煌國際", TreeNode.TOP_LEVEL + 3, 6, node3.getId(), false, false);//添加第一層節點TreeNode node4= new TreeNode("河南省", TreeNode.TOP_LEVEL , 3, TreeNode.NO_PARENT, true, false);//添加第二層節點TreeNode node5= new TreeNode("鄭州市", TreeNode.TOP_LEVEL + 1, 4, node4.getId(), true, false);//添加第三層節點TreeNode node6= new TreeNode("金水區", TreeNode.TOP_LEVEL + 2, 5, node5.getId(), false, false);//添加初始樹元素topNodes.add(node1);topNodes.add(node4);//建立資料來源allNodes.add(node1);allNodes.add(node2);allNodes.add(node3);allNodes.add(node4);allNodes.add(node5);allNodes.add(node6);//allNodes.add(node7);}
oncreate方法中調用:
LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);init();ListView treeview = (ListView) findViewById(R.id.tree_list);TreeViewAdapter treeViewAdapter = new TreeViewAdapter(topNodes, allNodes, inflater);TreeViewItemClickListener treeViewItemClickListener = new TreeViewItemClickListener(treeViewAdapter);treeview.setAdapter(treeViewAdapter);treeview.setOnItemClickListener(treeViewItemClickListener);
效果如下:
全部展開:
這個時候把treeActivity中那個node7注釋取消,也就是加了第三層節點的效果:
TreeNode node7= new TreeNode("輝煌國際", TreeNode.TOP_LEVEL + 3, 6, node3.getId(), false, false);
效果如下:
Android中的樹狀(tree)列表