自己实现的android树控件,android TreeView

  1.开发原因

  在项目中经常需要一个需要一个树状框架,这是非常常见的控件。不过可能是谷歌考虑到android是手机系统,界面宽度有限,

所以只提供了只有二级的ExpandableListView。虽然这个控件可以满足很多需求,但是无数级的树在某些情况下还是需要的,所以我花了一天时间

(大部分时间都在调试动画去了,不过现在动画还有点问题,具体原因不明。。如果某位大神能找到原因灰常感谢)。

注:今早起来终于修复了最后一个bug,现在的动画效果已经非常完美了,等下就把加了注释的代码贴上来。

  2.原理

  网上很多都是扩展listview实现的,不过listview貌似不支持复杂控件的事件?而且做动画也不方便,所有我决定扩展linearlayout,在里面增加子节点的方式实现。

  3.上图

自己实现的android树控件,android TreeView

图片是gif,真实效果要更加流畅一些,但是bug也还在的,就是开始隐藏的节点无法获取高度,可以通过自己事先设置解决。

  3.代码

TreeView.java:

自己实现的android树控件,android TreeView
  1 package net.memornote.android.ui.view;
  2 
  3 import java.util.ArrayList;
  4 import java.util.Collection;
  5 import java.util.List;
  6 import java.util.Timer;
  7 import java.util.TimerTask;
  8 
  9 import android.content.Context;
 10 import android.graphics.Rect;
 11 import android.util.AttributeSet;
 12 import android.view.View;
 13 import android.view.animation.Animation;
 14 import android.view.animation.Animation.AnimationListener;
 15 import android.view.animation.ScaleAnimation;
 16 import android.view.animation.TranslateAnimation;
 17 import android.widget.LinearLayout;
 18 
 19 public class TreeView extends LinearLayout{
 20     
 21 //    private List<TreeItem> items;
 22     private List<TreeItem> sortedItems;
 23     private int animTime;
 24 
 25     public TreeView(Context context, AttributeSet attrs) {
 26         super(context, attrs);
 27         setOrientation(LinearLayout.VERTICAL);
 28     }
 29     /**
 30      * initialize data,you must make sure that each item has parent except the top ones.
 31      * @param items the data to show 
 32      * @param index the index of the tree to insert to
 33      * @param viewHeight each view‘s height
 34      * @param animTime if you want expand animation, 
 35      * you can set the time(ms) of animation,otherwise you can set 0.
 36      * 
 37      */
 38     public void initData(Collection<TreeItem> items,int index){
 39         
 40         if(items==null||items.size()==0){
 41             return ;
 42         }
 43 
 44         sortItemList(items);
 45         
 46         int size=sortedItems.size();
 47         
 48         initAddIndex=index<0?-Integer.MAX_VALUE:index;
 49         
 50         for (int i=0;i<size;i++) {
 51             TreeItem item=sortedItems.get(i);
 52             recuseShow(item);
 53         }
 54         
 55     }
 56     
 57     private boolean isAnim=false;
 58     /**
 59      * 这个方法还有很 严重的bug,无法使用。。
 60      * 设置为0则关闭动画
 61      * @param animTime
 62      */
 63     public void enabledAnim(int animTime) {
 64         if(animTime<0){
 65             isAnim=false;
 66             return ;
 67         }
 68         this.animTime=animTime;
 69         isAnim=true;
 70     }
 71     
 72     private int initAddIndex; 
 73     
 74     private void recuseShow(TreeItem item){
 75         View view=item.getView();
 76         addView(view,initAddIndex);
 77         if(item.getParent()!=null){
 78             view.setVisibility(View.GONE);
 79         }else {
 80             view.setVisibility(View.VISIBLE);
 81         }
 82         initAddIndex++;
 83         List<TreeItem> childrens=item.getChildrens();
 84         if(childrens.size()>0){
 85             for (TreeItem it : childrens) {
 86                 recuseShow(it);
 87             }
 88         }
 89     }
 90     
 91     private void sortItemList(Collection<TreeItem> items) {
 92         //把items按照层级关系存放,sortedItems只存放顶层的item
 93         sortedItems=new ArrayList<TreeItem>(5);
 94         for (TreeItem item : items) {
 95             if(item.getParent()==null){
 96                 sortedItems.add(item);
 97             }else {
 98                 item.getParent().getChildrens().add(item);
 99             }
100         }
101         
102     }
103     
104     
105     private int viewIndex=0;
106     private int animHeight=0;
107     private void addChild(TreeItem item,boolean isRecurse){
108         if(item.getChildrens().size()>0){
109             List<TreeItem> list=item.getChildrens();
110             for (TreeItem it :    list) {
111                 View view=it.getView();
112                 if(view.isShown()){
113                     continue;
114                 }
115                 viewIndex++;
116                 view.setVisibility(View.VISIBLE);
117                 if(isAnim){
118                     animHeight-=it.getViewHeight();
119                 }
120                 it.nextIsExpand=true;
121                 if(isRecurse){
122                     addChild(it,true);
123                 }
124             }
125         }
126     }
127     private int removeCount=0;
128     private synchronized void removeChild(TreeItem item,boolean isRecurse){
129         if(item.getChildrens().size()>0){
130             List<TreeItem> list=item.getChildrens();
131             for (TreeItem it :    list) {
132                 View view=it.getView();
133                 if(!view.isShown()){
134                     continue;
135                 }
136                 
137                 TranslateAnimation ta=new TranslateAnimation(0, 0, 0, 0);
138                 ta.setFillBefore(true);
139                 ta.setDuration(1000);
140                 view.startAnimation(ta);
141                 removeCount++;
142                 view.setVisibility(View.GONE);
143                 if(isAnim){
144                     animHeight+=it.getViewHeight();
145                 }
146                 if(isRecurse){
147                     removeChild(it,true);
148                 }
149             }
150         }
151     }
152 
153     private void animAdd(){
154         TranslateAnimation ta=new TranslateAnimation(
155                 Animation.ABSOLUTE, 0, 
156                 Animation.ABSOLUTE, 0, 
157                 Animation.ABSOLUTE, animHeight, 
158                 Animation.ABSOLUTE, 0);
159         ta.setFillBefore(true);
160         ta.setFillAfter(false);
161         ta.setDuration(animTime);
162         
163         for (int i = viewIndex+1; i < getChildCount(); i++) {
164             View view=getChildAt(i);
165             if(view.isShown()){
166                 view.startAnimation(ta);
167             }
168         }
169         animHeight=0;
170     }
171     private void animRemove(){
172         TranslateAnimation ta=new TranslateAnimation(
173                 Animation.ABSOLUTE, 0, 
174                 Animation.ABSOLUTE, 0, 
175                 Animation.ABSOLUTE, animHeight, 
176                 Animation.ABSOLUTE, 0);
177         ta.setFillAfter(false);
178         ta.setFillBefore(true);
179         ta.setDuration(animTime);
180         
181         int startAnimIndex;
182         startAnimIndex=viewIndex+1;
183         for (int i = startAnimIndex; i < getChildCount(); i++) {
184             View view=getChildAt(i);
185             if(view.isShown()){
186                 view.startAnimation(ta);
187             }
188         }
189         animHeight=0;
190     }
191     public void expand(TreeItem item){
192         viewIndex=indexOfChild(item.getView());
193         addChild(item,false);
194         if(isAnim){
195             animAdd();
196         }
197     }
198     
199 
200     public void expandAllChildren(TreeItem item) {
201         viewIndex=indexOfChild(item.getView());
202         addChild(item,true);
203         if(isAnim){
204             animAdd();
205         }
206     }
207     
208     public void expandAll(){
209         if(sortedItems==null){
210             return ;
211         }
212         for (TreeItem item : sortedItems) {
213             expandAllChildren(item);
214         }
215     }
216     
217     public void contractAllChildren(TreeItem item) {
218         viewIndex=indexOfChild(item.getView())+1;
219         removeChild(item,true);
220         if(isAnim){
221             animRemove();
222         }
223     }
224     
225     public void contractAll(){
226         if(sortedItems==null){
227             return ;
228         }
229         for (TreeItem item : sortedItems) {
230             contractAllChildren(item);
231         }
232     }
233     
234     public void bind(TreeItem item) {
235         if(item.nextIsExpand){
236             expand(item);
237         }else {
238             contractAllChildren(item);
239         }
240         item.nextIsExpand=!item.nextIsExpand;
241     }
242     
243     
244 }
TreeView

 

TreeItem.java:

自己实现的android树控件,android TreeView
package net.memornote.android.ui.view;

import java.util.ArrayList;
import java.util.List;

import android.view.View;

public class TreeItem {
    private View view;
    private TreeItem parent;
    private List<TreeItem> childrens=new ArrayList<TreeItem>(0);
    public boolean nextIsExpand=true;
    private int viewHeight;  
    
    
    public TreeItem(){}
    /**
     * 初始化TreeItem
     * @param view
     * @param parent
     */
    public TreeItem(View view, TreeItem parent) {
        super();
        this.view = view;
        this.parent = parent;
    }
    /**
     * 初始化TreeItem
     * @param view
     * @param parent
     */
    public TreeItem(View view, TreeItem parent,int viewHeight) {
        super();
        this.view = view;
        this.parent = parent;
        this.viewHeight=viewHeight;
    }

    public View getView() {
        if(view!=null){
            view.setPadding(getLevel()*20,0,0,0);
        }
        return view;
    }

    public void setView(View view) {
        this.view = view;
    }

    public TreeItem getParent() {
        return parent;
    }

    public void setParent(TreeItem parent) {
        this.parent = parent;
    }

    /**
     * 动态获取该节点的级数
     * @return
     */
    public int getLevel() {
        int level=0;
        TreeItem localParent=parent;
        
        while (localParent!=null) {
            level++;
            localParent=localParent.getParent();
        }
        
        return level;
    }
    public List<TreeItem> getChildrens() {
        return childrens;
    }
    public int getViewHeight() {
        if(view==null||view.getHeight()==0){
            return viewHeight;
        }
        return view.getHeight();
    }
    public void setViewHeight(int viewHeight) {
        this.viewHeight = viewHeight;
    }

}
TreeItem

 

测试代码:

自己实现的android树控件,android TreeView
 1 import java.util.ArrayList;
 2 import java.util.List;
 3 
 4 import net.yunstudio.util.view.treeview.TreeItem;
 5 import net.yunstudio.util.view.treeview.TreeView;
 6 import android.os.Bundle;
 7 import android.app.Activity;
 8 import android.view.Menu;
 9 import android.view.View;
10 import android.view.View.OnClickListener;
11 import android.widget.Button;
12 
13 public class MainActivity extends Activity {
14 
15     private TreeView treeView;
16     @Override
17     protected void onCreate(Bundle savedInstanceState) {
18         super.onCreate(savedInstanceState);
19         setContentView(R.layout.activity_main);
20         treeView=(TreeView) findViewById(R.id.treeview);
21         List<TreeItem> items=new ArrayList<TreeItem>();
22         
23         initData(items);
24         treeView.initData(items, 0);
25         treeView.enabledAnim(500);
26         
27     }
28     //初始化一些测试数据
29     public void initData(List<TreeItem> items) {
30         for (int i=0;i<10;i++) {
31 //            TextView tv=new TextView(getActivity());
32             Button button=new Button(this);
33             button.setText("item"+i);
34             final TreeItem item=new TreeItem(button, null);
35             
36             button.setOnClickListener(new OnClickListener() {
37                 @Override
38                 public void onClick(View v) {
39                     treeView.bind(item);
40                 }
41             });
42             
43             items.add(item);
44             
45             if(i%2==0){
46                 Button bt1=new Button(this);
47                 bt1.setText("item"+i);
48                 bt1.setClickable(true);
49                 final TreeItem item_1=new TreeItem(bt1, item);
50                 
51                 bt1.setOnClickListener(new OnClickListener() {
52                     @Override
53                     public void onClick(View v) {
54 
55                         treeView.bind(item_1);
56                     }
57                 });
58                 items.add(item_1);
59                 
60                 if(i%4==0){
61                     Button bt_2=new Button(this);
62                     bt_2.setText("item"+i);
63                     bt_2.setClickable(true);
64                     final TreeItem item_2=new TreeItem( bt_2, item_1);
65                     
66                     bt_2.setOnClickListener(new OnClickListener() {
67                         @Override
68                         public void onClick(View v) {
69                             treeView.bind(item_2);
70                         }
71                     });
72                     items.add(item_2);
73                 }
74                 
75             }
76             
77             
78         }
79     }
80 
81     @Override
82     public boolean onCreateOptionsMenu(Menu menu) {
83         // Inflate the menu; this adds items to the action bar if it is present.
84         getMenuInflater().inflate(R.menu.main, menu);
85         return true;
86     }
87 
88 }
View Code

 

 

 源码下载:https://github.com/yzhen334/android_treeview

自己实现的android树控件,android TreeView

上一篇:要啥网·商信:一款让所有商业活动一步跨入移动互联时代的APP


下一篇:『创意欣赏』20款精致的 iOS7 APP 图标设计