【Android】自定義樹形控制元件
2016年11月13日 第二篇
Android自定義樹形控制元件
注:根據鴻洋的Android自定義任意層級樹形控制元件 編寫
效果圖
節點類(Node.java)
package jfsl.treeviewdemo.utils;
import java.util.ArrayList;
import java.util.List;
/**
* Created by JFSL on 2016/9/1 15:27.
*/
public class Node
{
private int id;
/**
* 父節點
*/
private int pId;
/**
* 顯示的文字內容
*/
private String name;
/**
* 層級
*/
private int level;
/**
* 是否展開
*/
private boolean isExpand;
private int iconId;
/**
* 父節點
*/
private Node parent;
/**
* 子節點
*/
private List children = new ArrayList();
public Node(int id,int pId,String name)
{
this.id = id;
this.pId = pId;
this.name = name;
}
public int getId()
{
return id;
}
public void setId(int id)
{
this.id = id;
}
public int getpId()
{
return pId;
}
public void setpId(int pId)
{
this.pId = pId;
}
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
/**
* 獲取層級
* @return
*/
public int getLevel()
{
return parent == null ? 0 : parent.getLevel() + 1;
}
public void setLevel(int level)
{
this.level = level;
}
public boolean isExpand()
{
return isExpand;
}
/**
* 設定收縮狀態
* @param expand
*/
public void setExpand(boolean expand)
{
isExpand = expand;
//設定收縮狀態
if(!isExpand)
{
//所有子節點都設定成false
for(Node node : children)
{
node.setExpand(isExpand);
}
}
}
public int getIconId()
{
return iconId;
}
public void setIconId(int iconId)
{
this.iconId = iconId;
}
public Node getParent()
{
return parent;
}
public void setParent(Node parent)
{
this.parent = parent;
}
public List getChildren()
{
return children;
}
public void setChildren(List children)
{
this.children = children;
}
/**
* 判斷是否是根節點
*/
public boolean isRoot()
{
return parent == null;
}
/**
* 判斷父節點的展開狀態
*/
public boolean isParentExpand()
{
//根節點
if(parent == null)
return false;
return parent.isExpand();
}
/**
* 是否是葉子結點
*/
public boolean isLeaf()
{
return children.size() == 0;
}
}
節點輔助類(TreeViewHelper.java)
package jfsl.treeviewdemo.utils;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import jfsl.treeviewdemo.R;
import jfsl.treeviewdemo.annotation.TreeNodeId;
import jfsl.treeviewdemo.annotation.TreeNodeLabel;
import jfsl.treeviewdemo.annotation.TreeNodeParentId;
/**
* Created by JFSL on 2016/9/1 15:25.
*
*/
public class TreeViewHelper
{
public static final int ICON_NONE = - 1;
public static final int CURRENT_LEVEL = 1;
/**
* 轉換資料
* @param datas
* @param
* @return
* @throws IllegalAccessException
*/
public static List convertDatasToNodes(List datas) throws IllegalAccessException
{
List nodes = new ArrayList();
Node node = null;
//遍歷資料
for(T t : datas)
{
int id = - 1;
int pId = - 1;
String label = null;
/**
* 反射+註解 獲取成員變數的值
*/
Class clazz = t.getClass();
Field[] fields = clazz.getDeclaredFields();
//反射獲取值
for(Field field : fields)
{
//獲取id
if(field.getAnnotation(TreeNodeId.class) != null)
{
//設定可見
field.setAccessible(true);
id = field.getInt(t);
}
//獲取pId
if(field.getAnnotation(TreeNodeParentId.class) != null)
{
//設定可見
field.setAccessible(true);
pId = field.getInt(t);
}
//獲取label
if(field.getAnnotation(TreeNodeLabel.class) != null)
{
//設定可見
field.setAccessible(true);
label = (String)field.get(t);
}
}
//加入節點
node = new Node(id,pId,label);
nodes.add(node);
}
//設定節點之間的關係
settingNodeRelation(nodes);
//設定圖示狀態
settingNodeIcon(nodes);
return nodes;
}
/**
* 設定節點之間的關係
* @param nodes
*/
private static void settingNodeRelation(List nodes)
{
//設定關係
for(int i = 0;i nodes)
{
for(Node no : nodes)
{
setNodeIcon(no);
}
}
/**
* 設定節點圖示
*
* @param node
*/
public static void setNodeIcon(Node node)
{
//有子節點
if(node.getChildren().size() > 0)
{
//展開
if(node.isExpand())
{
node.setIconId(R.mipmap.icon_expand);
return;
}
//收縮
node.setIconId(R.mipmap.icon_collapse);
}
//沒有圖示
else
node.setIconId(ICON_NONE);
}
/**
* 獲取排序後的節點
*
* @param datas
* @param
* @return
*/
public static List getSortedNodes(List datas,int defaultLevel) throws IllegalAccessException
{
//已經排序後的節點
List newNodes = new ArrayList();
//未排序的節點,只是轉換的
List oldNodes = convertDatasToNodes(datas);
/**
* 類似於採用深度遍歷樹
* PS:另外的方法,前序遍歷樹
*/
//***********************************************************
//獲取根節點
List rootNodes = getRootNodes(oldNodes);
for(Node node : rootNodes)
{
//預設級別為1
addNode(newNodes,node,defaultLevel,CURRENT_LEVEL);
}
//***********************************************************
return newNodes;
}
/**
* 深度遍歷,並新增
*
* @param newNodes
* @param node
* @param defaultLevel
* @param currentLevel
*/
private static void addNode(List newNodes,Node node,int defaultLevel,int currentLevel)
{
//新增節點
newNodes.add(node);
//設定展開的級別
if(defaultLevel >= currentLevel)
{
node.setExpand(true);
}
//葉子節點,也就是最後了,不用再遍歷
if(node.isLeaf())
return;
//遍歷子節點
for(int i = 0;i filterVisibleNodes(List nodes)
{
List visibleNodes = new ArrayList();
for(Node node : nodes)
{
if(node.isRoot() || node.isParentExpand())
{
//設定節點的圖示
setNodeIcon(node);
visibleNodes.add(node);
}
}
return visibleNodes;
}
/**
* 獲取根節點
*
* @param oldNodes
* @return
*/
private static List getRootNodes(List oldNodes)
{
List rootNode = new ArrayList();
for(Node node : oldNodes)
{
if(node.isRoot())
{
rootNode.add(node);
}
}
return rootNode;
}
}
檔案Bean類,測試的資料類(FileBean.java)
package jfsl.treeviewdemo.bean;
import jfsl.treeviewdemo.annotation.TreeNodeId;
import jfsl.treeviewdemo.annotation.TreeNodeLabel;
import jfsl.treeviewdemo.annotation.TreeNodeParentId;
/**
* Created by JFSL on 2016/9/1 15:22.
*/
public class FileBean
{
@TreeNodeId
private int id;
@TreeNodeParentId
private int pId;
@TreeNodeLabel
private String name;
public FileBean(int id,int pId,String name)
{
this.id = id;
this.pId = pId;
this.name = name;
}
public int getId()
{
return id;
}
public void setId(int id)
{
this.id = id;
}
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
@Override
public String toString()
{
return "FileBean{" +
"id=" + id +
", name='" + name + ''' +
'}';
}
}
註解介面
1.TreeNodeId
package jfsl.treeviewdemo.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface TreeNodeId
{
}
2.TreeNodeLabel
package jfsl.treeviewdemo.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface TreeNodeLabel
{
}
3.TreeNodeParentId
package jfsl.treeviewdemo.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface TreeNodeParentId
{
}
1.資料介面卡(父類)(TreeViewAdapter.java)
package jfsl.treeviewdemo.adapter;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.ListView;
import java.util.List;
import jfsl.treeviewdemo.utils.Node;
import jfsl.treeviewdemo.utils.TreeViewHelper;
public abstract class TreeViewAdapter extends BaseAdapter
{
//上下文
protected Context mContext;
//所有的節點
protected List mAllNodes;
//顯示的節點
protected List mVisibleNodes;
//載入佈局
protected LayoutInflater mInflater;
//ListView
protected ListView mListTree;
/**
* 自定義回撥介面
*/
private OnTreeNodeClickListener mListener;
public interface OnTreeNodeClickListener
{
void onClick(Node node,int position);
}
public void setListener(OnTreeNodeClickListener listener)
{
mListener = listener;
}
public TreeViewAdapter(Context context,ListView listTree ,List datas,int defaultLevel) throws IllegalAccessException
{
mContext = context;
mAllNodes = TreeViewHelper.getSortedNodes(datas,defaultLevel);
mVisibleNodes = TreeViewHelper.filterVisibleNodes(mAllNodes);
mInflater = LayoutInflater.from(mContext);
mListTree = listTree;
//設定ListView的點選事件
mListTree.setOnItemClickListener(new AdapterView.OnItemClickListener()
{
@Override
public void onItemClick(AdapterView> adapterView,View view,int position,long id)
{
//展開或者收縮
expandOrCollapse(position);
//事件回撥
if(mListener != null)
{
mListener.onClick(mVisibleNodes.get(position),position);
}
}
});
}
/**
* 展開或者收縮
* @param position
*/
private void expandOrCollapse(int position)
{
Node node = mVisibleNodes.get(position);
if(node != null)
{
if(node.isLeaf())
return;
//反向選擇
node.setExpand(!node.isExpand());
//更新資料
mVisibleNodes = TreeViewHelper.filterVisibleNodes(mAllNodes);
notifyDataSetChanged();
}
}
@Override
public int getCount()
{
return mVisibleNodes.size();
}
@Override
public Object getItem(int position)
{
return mVisibleNodes.get(position);
}
@Override
public long getItemId(int position)
{
return position;
}
@Override
public View getView(int position,View view,ViewGroup viewGroup)
{
Node node = mVisibleNodes.get(position);
view = getConvertView(node,position,view,viewGroup);
//設定距離問題
view.setPadding(node.getLevel() * 70,3,3,3);
return view;
}
public abstract View getConvertView(Node node,int position,View view,ViewGroup viewGroup);
}
2.樹形資料介面卡(SimpleTreeAdapter.java)
package jfsl.treeviewdemo.adapter;
import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
import java.util.List;
import jfsl.treeviewdemo.R;
import jfsl.treeviewdemo.utils.Node;
/**
* Created by JFSL on 2016/9/1 20:36.
* 自定義Item的介面
* 可以根據情況修改
*/
public class SimpleTreeAdapter extends TreeViewAdapter
{
public SimpleTreeAdapter(Context context,ListView listTree,List datas,int defaultLevel) throws IllegalAccessException
{
super(context,listTree,datas,defaultLevel);
}
/**
* 主要用來自定義介面
* @param node
* @param position
* @param view
* @param viewGroup
* @return
*/
@Override
public View getConvertView(Node node,int position,View view,ViewGroup viewGroup)
{
ViewHolder holder = null;
if(view == null)
{
view = mInflater.inflate(R.layout.listview_item_treeview,viewGroup,false);
holder = new ViewHolder();
holder.icon = (ImageView)view.findViewById(R.id.id_item_icon);
holder.text = (TextView)view.findViewById(R.id.id_item_text);
view.setTag(holder);
} else
{
holder = (ViewHolder)view.getTag();
}
//沒有圖示,也就是沒有子節點的資料項
if(node.getIconId() == - 1)
{
holder.icon.setVisibility(View.INVISIBLE);
}
//有圖示
else
{
holder.icon.setVisibility(View.VISIBLE);
holder.icon.setImageResource(node.getIconId());
}
holder.text.setText(node.getName());
return view;
}
private class ViewHolder
{
ImageView icon;
TextView text;
}
}
測試的Activity
package jfsl.treeviewdemo;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.ListView;
import java.util.ArrayList;
import java.util.List;
import jfsl.treeviewdemo.adapter.SimpleTreeAdapter;
import jfsl.treeviewdemo.bean.FileBean;
/**
* 主介面
* @date 2016年9月1日 21:27:11
* @author JFSL
*
*/
public class ActivityMain extends AppCompatActivity
{
//
private ListView mListView;
//資料集
private List mDatas;
//介面卡
private SimpleTreeAdapter mAdapter;
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
try
{
mListView = (ListView)findViewById(R.id.id_listview);
//初始化模擬資料
initTestDatas();
//初始化介面卡 預設顯示層級 0
mAdapter = new SimpleTreeAdapter(this,mListView,mDatas,0);
//繫結介面卡
mListView.setAdapter(mAdapter);
} catch(IllegalAccessException e)
{
}
}
/**
* 初始化模擬資料
*/
private void initTestDatas()
{
mDatas = new ArrayList();
FileBean bean = new FileBean(1,0,"xx小學");
mDatas.add(bean);
bean = new FileBean(2,1,"一(1)班");
mDatas.add(bean);
bean = new FileBean(201,2,"Android學習");
mDatas.add(bean);
bean = new FileBean(211,2,"Java技術");
mDatas.add(bean);
bean = new FileBean(221,2,"計算機網路");
mDatas.add(bean);
bean = new FileBean(8,0,"xx中學");
mDatas.add(bean);
bean = new FileBean(11,8,"九(2)班");
mDatas.add(bean);
bean = new FileBean(12,0,"xx中學");
mDatas.add(bean);
bean = new FileBean(13,12,"高一(16)班");
mDatas.add(bean);
bean = new FileBean(14,12,"高二(13)班");
mDatas.add(bean);
bean = new FileBean(15,12,"高三(11)班");
mDatas.add(bean);
bean = new FileBean(16,0,"XXXXXX");
mDatas.add(bean);
bean = new FileBean(17,16,"大一");
mDatas.add(bean);
bean = new FileBean(18,16,"大二");
mDatas.add(bean);
bean = new FileBean(19,16,"大三");
mDatas.add(bean);
bean = new FileBean(20,19,"Android學習");
mDatas.add(bean);
bean = new FileBean(21,19,"Java技術");
mDatas.add(bean);
bean = new FileBean(22,21,"計算機網路");
mDatas.add(bean);
}
}
1.activity_main.xml
2.listview_item_treeview.xml
程式碼地址
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/4289/viewspace-2798356/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 自定義圓形進度條控制元件控制元件
- Android自定義圓形進度條Android
- Android 控制元件架構與自定義控制元件詳解Android控制元件架構
- Android自定義View--翻書控制元件(一)AndroidView控制元件
- Android自定義控制元件(神級)+MediaRecoder錄音Android控制元件
- Android自定義多宮格解鎖控制元件Android控制元件
- Android自定義控制元件 帶文字提示的SeekBarAndroid控制元件
- vue的樹形控制元件Vue控制元件
- 自定義控制元件ViewPager控制元件Viewpager
- 自定義Switch控制元件控制元件
- 4. 自定義控制元件(4) --- 自定義屬性控制元件
- 從Android到ReactNative開發(三、自定義原生控制元件支援)AndroidReact控制元件
- Android開發之自定義隨機驗證碼控制元件Android隨機控制元件
- Android自定義控制元件(高手級)--JOJO同款能力分析圖Android控制元件
- Android自定義控制元件(高手級)–JOJO同款能力分析圖Android控制元件
- iOS自定義控制元件 SlideriOS控制元件IDE
- iOS自定義控制元件 AlertViewiOS控制元件View
- iOS自定義控制元件 SegmentiOS控制元件
- winform 自定義容器控制元件ORM控制元件
- WPF Blend 自定義控制元件控制元件
- Flutter 之 自定義控制元件Flutter控制元件
- Android自定義控制元件之區域性圖片放大鏡–BiggerViewAndroid控制元件View
- Android自定義控制元件之區域性圖片放大鏡--BiggerViewAndroid控制元件View
- 從 Android 到 React Native 開發(三、自定義原生控制元件支援)AndroidReact Native控制元件
- VirtualView Android 實現詳解(三)—— 新增一個自定義控制元件ViewAndroid控制元件
- android自定義view(自定義數字鍵盤)AndroidView
- 自定義控制元件 --- 電池icon控制元件
- Flutter 自定義縮放控制元件Flutter控制元件
- Qt實現自定義控制元件QT控制元件
- AngularJS自定義表單控制元件AngularJS控制元件
- iOS自定義控制元件:簡易下拉控制元件iOS控制元件
- 利用Xfermode 自定義形狀ViewView
- Android自定義View整合AndroidView
- Android自定義遮罩層Android遮罩
- 自定義Android鍵盤Android
- Android自定義OnTouch事件Android事件
- Android 自定義UI元件AndroidUI元件
- android 自定義鍵盤Android