【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自定義控制元件——自定義屬性Android控制元件
- Android自定義控制元件之自定義組合控制元件Android控制元件
- Android自定義控制元件之自定義屬性Android控制元件
- 自定義圓形進度條控制元件控制元件
- android:建立自定義控制元件Android控制元件
- (Android自定義控制元件)Android自定義狀態提示圖表Android控制元件
- Android 自定義圓形頭像Android
- Android自定義圓形頭像Android
- Android自定義組合控制元件之自定義屬性Android控制元件
- Android自定義控制元件系列之圓形進度條的實現Android控制元件
- android 自定義控制元件 自定義屬性詳細介紹Android控制元件
- Android自定義圓形進度條Android
- Android自定義控制元件之自定義ViewGroup實現標籤雲Android控制元件View
- Android 自定義輪播圖片控制元件Android控制元件
- Android自定義下拉重新整理控制元件Android控制元件
- android自定義開關控制元件-SlideSwitchAndroid控制元件IDE
- vue的樹形控制元件Vue控制元件
- Android 控制元件架構與自定義控制元件詳解Android控制元件架構
- 自定義ImageView完成圓形頭像自定義View
- Android 自定義控制元件一 帶圓形進度的按鈕 ControlButton2Android控制元件
- Android自定義控制元件 帶文字提示的SeekBarAndroid控制元件
- Android自定義多宮格解鎖控制元件Android控制元件
- Android自定義View--翻書控制元件(一)AndroidView控制元件
- Android自定義控制元件之基本原理Android控制元件
- Android自定義控制元件系列之基礎篇Android控制元件
- Android自定義控制元件模仿iOS滑塊SwitchButtonAndroid控制元件iOS
- 樹形控制元件比較 (轉)控制元件
- 自定義Switch控制元件控制元件
- 自定義控制元件ViewPager控制元件Viewpager
- 控制元件自定義位置控制元件
- 如何自定義控制元件控制元件
- Android自定義圓形進度條原始碼解析Android原始碼
- Android自定義控制元件實現一個帶文字與數字的圓形進度條Android控制元件
- 4. 自定義控制元件(4) --- 自定義屬性控制元件
- Android 自定義控制元件 ViewPager頭部指示器控制元件 ViewPagerBelowIndicatorAndroid控制元件ViewpagerIndicator
- 【組合控制元件】android自定義控制元件之帶文字的ImageView控制元件AndroidView