1.開發原因
在項目中經常需要一個需要一個樹狀框架,這是非常常見的控件。不過可能是谷歌考慮到android是手機系統,界面寬度有限,
所以只提供了只有二級的ExpandableListView。雖然這個控件可以滿足很多需求,但是無數級的樹在某些情況下還是需要的,所以我花了一天時間
(大部分時間都在調試動畫去了,不過現在動畫還有點問題,具體原因不明。。如果某位大神能找到原因灰常感謝)。
2.原理
網上很多都是擴展listview實現的,不過listview貌似不支持復雜控件的事件?而且做動畫也不方便,所有我決定擴展linearlayout,在裡面增加子節點的方式實現。
3.代碼
TreeView.java:
復制代碼
1 package net.memornote.android.ui.view;
2
3 import java.util.ArrayList;
4 import java.util.List;
5 import java.util.Timer;
6 import java.util.TimerTask;
7
8 import android.content.Context;
9 import android.graphics.Rect;
10 import android.util.AttributeSet;
11 import android.view.View;
12 import android.view.animation.Animation;
13 import android.view.animation.Animation.AnimationListener;
14 import android.view.animation.ScaleAnimation;
15 import android.view.animation.TranslateAnimation;
16 import android.widget.LinearLayout;
17
18 public class TreeView extends LinearLayout{
19
20 // private List<TreeItem> items;
21 private List<TreeItem> sortedItems;
22 private int animTime;
23
24 public TreeView(Context context, AttributeSet attrs) {
25 super(context, attrs);
26 setOrientation(LinearLayout.VERTICAL);
27 }
28 /**
29 * initialize data,you must make sure that each item has parent except the top ones.
30 * @param items the data to show
31 * @param index the index of the tree to insert to
32 * @param viewHeight each view's height
33 * @param animTime if you want expand animation,
34 * you can set the time(ms) of animation,otherwise you can set 0.
35 *
36 */
37 public void initData(List<TreeItem> items,int index){
38
39 if(items==null||items.size()==0){
40 return ;
41 }
42
43 sortItemList(items);
44
45 int size=sortedItems.size();
46
47 initAddIndex=index<0?-Integer.MAX_VALUE:index;
48
49 for (int i=0;i<size;i++) {
50 TreeItem item=sortedItems.get(i);
51 recuseShow(item);
52 }
53
54 }
55
56 private boolean isAnim=false;
57 /**
58 * 這個方法還有很 嚴重的bug,無法使用。。
59 * 設置為0則關閉動畫
60 * @param animTime
61 */
62 public void enabledAnim(int animTime) {
63 if(animTime<0){
64 isAnim=false;
65 return ;
66 }
67 this.animTime=animTime;
68 isAnim=true;
69 }
70
71 private int initAddIndex;
72
73 private void recuseShow(TreeItem item){
74 View view=item.getView();
75 addView(view,initAddIndex);
76 if(item.getParent()!=null){
77 view.setVisibility(View.GONE);
78 item.isShow=false;
79 }else {
80 view.setVisibility(View.VISIBLE);
81 item.isShow=true;
82 }
83 initAddIndex++;
84 List<TreeItem> childrens=item.getChildrens();
85 if(childrens.size()>0){
86 for (TreeItem it : childrens) {
87 recuseShow(it);
88 }
89 }
90 }
91
92 @Override
93 public void onWindowFocusChanged(boolean hasWindowFocus) {
94 super.onWindowFocusChanged(hasWindowFocus);
95 if(hasWindowFocus){
96
97 }
98 }
99
100 private void sortItemList(List<TreeItem> items) {
101 //把items按照層級關系存放,sortedItems只存放頂層的item
102 sortedItems=new ArrayList<TreeItem>(5);
103 for (TreeItem item : items) {
104 if(item.getParent()==null){
105 sortedItems.add(item);
106 }else {
107 item.getParent().getChildrens().add(item);
108 }
109 }
110
111 }
112
113
114 private int viewIndex=0;
115 private int animHeight=0;
116 private void addChild(TreeItem item,boolean isRecurse){
117 if(item.getChildrens().size()>0){
118 List<TreeItem> list=item.getChildrens();
119 for (TreeItem it : list) {
120 View view=it.getView();
121 if(it.isShow){
122 continue;
123 }
124 viewIndex++;
125 view.setVisibility(View.VISIBLE);
126 it.isShow=true;
127 if(isAnim){
128 animHeight-=it.getViewHeight();
129 }
130 it.nextIsExpand=true;
131 if(isRecurse){
132 addChild(it,true);
133 }
134 }
135 }
136 }
137 private int removeCount=0;
138 private synchronized void removeChild(TreeItem item,boolean isRecurse){
139 if(item.getChildrens().size()>0){
140 List<TreeItem> list=item.getChildrens();
141 for (TreeItem it : list) {
142 View view=it.getView();
143 if(!it.isShow){
144 continue;
145 }
146 // removeViewAt(viewIndex);
147
148 TranslateAnimation ta=new TranslateAnimation(0, 0, 0, 0);
149 ta.setFillAfter(true);
150 ta.setDuration(1000);
151 view.startAnimation(ta);
152 // viewIndex++;
153 removeCount++;
154 it.isShow=false;
155 view.setVisibility(View.GONE);
156 if(isAnim){
157 animHeight+=it.getViewHeight();
158 }
159 if(isRecurse){
160 removeChild(it,true);
161 }
162 }
163 }
164 }
165
166 private void animAdd(){
167 TranslateAnimation ta=new TranslateAnimation(
168 Animation.ABSOLUTE, 0,
169 Animation.ABSOLUTE, 0,
170 Animation.ABSOLUTE, animHeight,
171 Animation.ABSOLUTE, 0);
172 ta.setFillBefore(true);
173 // ta.setFillAfter(false);
174 ta.setDuration(animTime);
175
176 for (int i = viewIndex+1; i < getChildCount(); i++) {
177 View view=getChildAt(i);
178 view.startAnimation(ta);
179 }
180 animHeight=0;
181 }
182 private void animRemove(){
183 TranslateAnimation ta=new TranslateAnimation(
184 Animation.ABSOLUTE, 0,
185 Animation.ABSOLUTE, 0,
186 Animation.ABSOLUTE, animHeight,
187 Animation.ABSOLUTE, 0);
188 ta.setFillAfter(false);
189 ta.setFillBefore(true);
190 ta.setDuration(animTime);
191
192 int startAnimIndex;
193 startAnimIndex=viewIndex+1;
194 for (int i = startAnimIndex; i < getChildCount(); i++) {
195 View view=getChildAt(i);
196
197 view.startAnimation(ta);
198 }
199 animHeight=0;
200 }
201 public void expand(TreeItem item){
202 viewIndex=indexOfChild(item.getView());
203 addChild(item,false);
204 if(isAnim){
205 animAdd();
206 }
207 }
208
209
210 public void expandAllChildren(TreeItem item) {
211 viewIndex=indexOfChild(item.getView());
212 addChild(item,true);
213 if(isAnim){
214 animAdd();
215 }
216 }
217
218 public void expandAll(){
219 if(sortedItems==null){
220 return ;
221 }
222 for (TreeItem item : sortedItems) {
223 expandAllChildren(item);
224 }
225 }
226
227 public void contractAllChildren(TreeItem item) {
228 viewIndex=indexOfChild(item.getView())+1;
229 removeChild(item,true);
230 if(isAnim){
231 animRemove();
232 }
233 }
234
235 public void contractAll(){
236 if(sortedItems==null){
237 return ;
238 }
239 for (TreeItem item : sortedItems) {
240 contractAllChildren(item);
241 }
242 }
243
244 public void bind(TreeItem item) {
245 if(item.nextIsExpand){
246 expand(item);
247 }else {
248 contractAllChildren(item);
249 }
250 item.nextIsExpand=!item.nextIsExpand;
251 }
252
253
254 }
復制代碼
TreeItem.java
復制代碼
1 package net.memornote.android.ui.view;
2
3 import java.util.ArrayList;
4 import java.util.List;
5
6 import android.view.View;
7
8 public class TreeItem {
9 private View view;
10 private TreeItem parent;
11 private List<TreeItem> childrens=new ArrayList<TreeItem>(0);
12 public boolean nextIsExpand=true;
13 public boolean isShow=false;
14 private int viewHeight;
15
16
17 public TreeItem(){}
18 public TreeItem(int id,View view, TreeItem parent) {
19 super();
20 this.view = view;
21 this.parent = parent;
22 }
23
24 public View getView() {
25 if(view!=null){
26 view.setPadding(getLevel()*20,0,0,0);
27 }
28 return view;
29 }
30
31 public void setView(View view) {
32 this.view = view;
33 }
34
35 public TreeItem getParent() {
36 return parent;
37 }
38
39 public void setParent(TreeItem parent) {
40 this.parent = parent;
41 }
42
43 /**
44 * 動態獲取該節點的級數
45 * @return
46 */
47 public int getLevel() {
48 int level=0;
49 TreeItem localParent=parent;
50
51 while (localParent!=null) {
52 level++;
53 localParent=localParent.getParent();
54 }
55
56 return level;
57 }
58 public List<TreeItem> getChildrens() {
59 return childrens;
60 }
61 public int getViewHeight() {
62 if(view==null||view.getHeight()==0){
63 return viewHeight;
64 }
65 return view.getHeight();
66 }
67 public void setViewHeight(int viewHeight) {
68 this.viewHeight = viewHeight;
69 }
70
71
72 }
復制代碼
測試代碼:
復制代碼
1 package net.memornote.android.ui;
2
3 import net.memornote.android.R;
4 import net.memornote.android.ui.fragment.MainFragment;
5 import net.memornote.android.ui.fragment.MenuFragment;
6
7 import com.actionbarsherlock.view.Menu;
8 import com.jeremyfeinstein.slidingmenu.lib.SlidingMenu;
9 import com.jeremyfeinstein.slidingmenu.lib.app.SlidingFragmentActivity;
10
11 import android.os.Bundle;
12 import android.view.KeyEvent;
13
14 public class MainActivity extends SlidingFragmentActivity {
15
16
17 @Override
18 public void onCreate(Bundle savedInstanceState) {
19 super.onCreate(savedInstanceState);
20
21 setContentView(R.layout.slid_content_frame);
22
23 tv_notebook=(TreeView) findViewById(R.id.tv_notebook);
24
25 List<TreeItem> items=new ArrayList<TreeItem>();
26
27 initData(items);
28 tv_notebook.initData(items, 0);
29 tv_notebook.enabledAnim(500);
30
31 }
32 //初始化一些測試數據
33 public void initData(List<TreeItem> items) {
34 for (int i=0;i<10;i++) {
35 // TextView tv=new TextView(getActivity());
36 Button button=new Button(getActivity());
37 button.setText("item"+i);
38 final TreeItem item=new TreeItem(0, button, null);
39
40 button.setOnClickListener(new OnClickListener() {
41 @Override
42 public void onClick(View v) {
43 tv_notebook.bind(item);
44 }
45 });
46
47 items.add(item);
48
49 if(i%2==0){
50 Button bt1=new Button(getActivity());
51 bt1.setText("item"+i);
52 bt1.setClickable(true);
53 final TreeItem item_1=new TreeItem(0, bt1, null);
54
55 bt1.setOnClickListener(new OnClickListener() {
56 @Override
57 public void onClick(View v) {
58
59 tv_notebook.bind(item_1);
60 }
61 });
62 item_1.setParent(item);
63 items.add(item_1);
64
65 if(i%4==0){
66 Button bt_2=new Button(getActivity());
67 bt_2.setText("item"+i);
68 bt_2.setClickable(true);
69 final TreeItem item_2=new TreeItem(0, bt_2, null);
70
71 bt_2.setOnClickListener(new OnClickListener() {
72 @Override
73 public void onClick(View v) {
74 tv_notebook.bind(item_2);
75 }
76 });
77 item_2.setParent(item_1);
78 items.add(item_2);
79 }
80
81 }
82
83
84 }
85 }
86
87 @Override
88 public boolean onCreateOptionsMenu(Menu menu) {
89 return true;
90 }
91
92 }
93
復制代碼