自己实现的android树控件,android TreeView
16lz
2021-01-26
1.开发原因
在项目中经常需要一个需要一个树状框架,这是非常常见的控件。不过可能是谷歌考虑到android是手机系统,界面宽度有限,
所以只提供了只有二级的ExpandableListView。虽然这个控件可以满足很多需求,但是无数级的树在某些情况下还是需要的,所以我花了一天时间
(大部分时间都在调试动画去了,不过现在动画还有点问题,具体原因不明。。如果某位大神能找到原因灰常感谢)。
注:今早起来终于修复了最后一个bug,现在的动画效果已经非常完美了,等下就把加了注释的代码贴上来。
2.原理
网上很多都是扩展listview实现的,不过listview貌似不支持复杂控件的事件?而且做动画也不方便,所有我决定扩展linearlayout,在里面增加子节点的方式实现。
3.上图
图片是gif,真实效果要更加流畅一些,但是bug也还在的,就是开始隐藏的节点无法获取高度,可以通过自己事先设置解决。
3.代码
TreeView.java:
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:
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
测试代码:
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 @Override17 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 @Override38 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 @Override53 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 @Override68 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 @Override82 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(安卓)UI设计与开发】第04期:引导界面(四)仿人人网V5.9.2
- Android(安卓)UI - 右侧滑动实现A-Z的快速定位
- Android模拟开关按钮点击打开动画(属性动画之平移动画)
- Android属性动画-Property Animation(四) 组合动画
- Android之增长的数字(仿支付宝资产数字)
- Android(安卓)Layout标签之 - viewStub,requestFocus,merge,include
- Android(安卓)补间动画之平移动画TranslateAnimation
- Android—启动内置APK和动态发送接收自定义广播