# MultipleTreeView
**Repository Path**: xiaotaoge/MultipleTreeView
## Basic Information
- **Project Name**: MultipleTreeView
- **Description**: No description available
- **Primary Language**: Unknown
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2020-10-26
- **Last Updated**: 2020-12-19
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# RecycleView实现Android树形列表控件
本文主要通过RecycleView实现了任意层次的树形ListView。实现的主要原理是通过保存两份List数据,一份是完整数据,一份是当前展开的数据来实现。再通过点击事件,控制展开数据的内容。这样即是实现树形控件。
## TreeView的使用
- build.gradle
```
//增加treeview包引用
compile 'org.chen.treeview:treeview:1.0.1'
```
- activity_main.xml
```
//引入控件
```
- TestModel.java
```
//测试数据模型, 必须添加@NodeId , @NodeName 两个注解字段
public class TestModel {
//通过注解的方式,进行model转换
@NodeId //必填字段
public String id;
@NodeName //必填字段
public String name;
@NodeLabel //可选字段
public String label;
@NodePid //必填字段
public String parentId;
@NodeChild
public List child; //非叶子节点必填字段
public String other1;
public String other2;
public int ohter3;
}
```
- MainActivity.java
```
public class MainActivity extends AppCompatActivity {
private TreeRecyclerView tree_view;
private Button btn_select;
private TextView tv_result;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tree_view = (TreeRecyclerView) findViewById(R.id.tree_view);
//传入数据,以及对应的模式
tree_view.setData(generateList(), TreeRecyclerView.MODE_SINGLE_SELECT);
tv_result = (TextView) findViewById(R.id.tv_result);
btn_select = (Button) findViewById(R.id.btn_select);
btn_select.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
StringBuffer buffer = new StringBuffer();
ArrayList list = new ArrayList<>();
//获取当前已经选中的数据, 传入进去list类型,必须与存储数据类型相同
tree_view.getSelectedItems(list);
for (TestModel model : list) {
buffer.append(model.name + " ");
}
tv_result.setText(buffer.toString());
}
});
}
//手动生成的测试数据
private List generateList() {
ArrayList result = new ArrayList<>();
TestModel model1 = new TestModel();
model1.id = "1";
model1.name = "上海";
model1.label = "魔都";
model1.parentId = "";
model1.child = null;
TestModel model2 = new TestModel();
model2.id = "2";
model2.name = "北京";
model2.label = "帝都";
model2.parentId = "";
model2.child = null;
TestModel model3 = new TestModel();
model3.id = "3";
model3.name = "江苏";
model3.label = "省";
model3.parentId = "";
model3.child = null;
TestModel model4 = new TestModel();
model4.id = "4";
model4.name = "浙江";
model4.label = "省";
model4.parentId = "";
model4.child = null;
TestModel model5 = new TestModel();
model5.id = "5";
model5.name = "天津";
model5.label = "直辖市";
model5.parentId = "";
model5.child = null;
TestModel model31 = new TestModel();
model31.id = "31";
model31.name = "南京";
model31.label = "省会城市";
model31.parentId = "3";
model31.child = null;
TestModel model32 = new TestModel();
model32.id = "32";
model32.name = "苏州";
model32.label = "牛B城市";
model32.parentId = "3";
model32.child = null;
TestModel model33 = new TestModel();
model33.id = "33";
model33.name = "无锡";
model33.label = "灵山大佛";
model33.parentId = "3";
model33.child = null;
TestModel model41 = new TestModel();
model41.id = "41";
model41.name = "杭州";
model41.label = "省会城市";
model41.parentId = "4";
model41.child = null;
TestModel model42 = new TestModel();
model42.id = "42";
model42.name = "温州";
model42.label = "炒房团+皮革厂";
model42.parentId = "4";
model42.child = null;
TestModel model311 = new TestModel();
model311.id = "311";
model311.name = "玄武区";
model311.label = "玄武湖好美";
model311.parentId = "31";
model311.child = null;
TestModel model312 = new TestModel();
model312.id = "312";
model312.name = "建邺区";
model312.label = "河西新城,房价真贵";
model312.parentId = "31";
model312.child = null;
TestModel model411 = new TestModel();
model411.id = "411";
model411.name = "西湖区";
model411.label = "西湖区好美,湖边来一套别墅";
model411.parentId = "41";
model411.child = null;
TestModel model412 = new TestModel();
model412.id = "412";
model412.name = "滨江区";
model412.label = "科技新城,阿里网易";
model412.parentId = "41";
model412.child = null;
//杭州市
ArrayList list_41 = new ArrayList<>();
list_41.add(model411);
list_41.add(model412);
model41.child = list_41;
//南京市
ArrayList list_31 = new ArrayList<>();
list_31.add(model311);
list_31.add(model312);
model31.child = list_31;
//江苏省
ArrayList list_3 = new ArrayList<>();
list_3.add(model31);
list_3.add(model32);
list_3.add(model33);
model3.child = list_3;
//江苏省
ArrayList list_4 = new ArrayList<>();
list_4.add(model41);
list_4.add(model42);
model4.child = list_4;
result.add(model1);
result.add(model2);
result.add(model3);
result.add(model4);
result.add(model5);
return result;
}
}
```
## TreeView源码解析
- Node.java
```
public class Node {
public final static int TREE_LEAF = 0; //树叶节点
public final static int TREE_NODE = 1; //普通节点
/**
* 节点Id
*/
private String id;
/**
* 父节点Id
*/
private String pId;
/**
* 节点名称
*/
private String name;
/**
* 节点类型, 0为树叶节点, 1为普通节点
*/
private int type;
/**
* 节点级别, 根节点level = 0,子节点依次+1
*/
private int level;
/**
* 父Node
*/
private Node parent;
/**
* 下一级的子Node
*/
private List> children;
/**
* 是否展开
*/
private boolean isExpanded;
/**
* 是否被选中
*/
private boolean isChecked;
/**
* 节点描述
*/
private String label;
/**
* 节点信息其他内容
*/
private T content;
/**
* 节点前面的Icon
*/
private int headDrawableId;
/**
* 节点选中的Icon
*/
private int checkDrawableId;
public Node(String id, String pid, String name) {
this.id = id;
this.pId = pid;
this.name = name;
}
... 省略get set 方法
```
- TreeRecyclerView.java
```
public class TreeRecyclerView extends FrameLayout {
public final static int MODE_SINGLE_SELECT = 1; //单选模式
public final static int MODE_MULTI_SELECT = 2; //无限制,多选模式
public final static int MODE_DEPEND_SELECT = 3; //父子节点不可同选,多选模式
public final static int MODE_CLICK_SELECT = 4; //选中跳转模式
private RecyclerView mRecyclerView;
private TreeRecyclerAdapter mAdapter;
public TreeRecyclerView(Context context) {
super(context);
initView(context);
}
public TreeRecyclerView(Context context, AttributeSet attrs) {
super(context, attrs);
initView(context);
}
public TreeRecyclerView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView(context);
}
private void initView(Context context) {
mRecyclerView = new RecyclerView(context);
mRecyclerView.setLayoutManager(new LinearLayoutManager(context));
LayoutParams lp = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
addView(mRecyclerView, lp);
mAdapter = new TreeRecyclerAdapter<>(mRecyclerView.getContext());
}
/**
* 初始化树形折叠控件数据
*
*
* T 类型 示例
*
* public static class TestModel {
*
* @NodeId public String id; //必填字段
* @NodeName public String name; //必填字段
* @NodeLabel public String label;
* @NodePid public String parentId; //父节点id
* @NodeChild public List child; //child用来表示层级关系, child为空,则表示叶子节点
* ...
* others
* ...
* }
*/
public void setData(List list, int mode) {
ArrayList> nodeList = new ArrayList<>();
try {
nodeList = NodeDataConverter.convertToNodeList(list);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
setMode(mode);
mAdapter.addAllData(nodeList);
mRecyclerView.setAdapter(mAdapter);
}
/**
* 设置点击事件, MODE_CLICK_SELECT模式下需要使用
*/
public void setOnItemClickListener(OnNodeItemClickListener listener) {
mAdapter.setOnItemClickListener(listener);
}
/**
* 获取选中的内容
*
* @param
* @return
*/
public void getSelectedItems(List list) {
mAdapter.getSelectedItems(list);
}
/**
* 设置折叠控件的选择模式
*
* @param mode
*/
private void setMode(int mode) {
if (mode != MODE_SINGLE_SELECT
&& mode != MODE_MULTI_SELECT
&& mode != MODE_DEPEND_SELECT
&& mode != MODE_CLICK_SELECT) {
return;
}
mAdapter.setMode(mode);
}
}
```
- TreeRecyclerAdapter.java
```
class TreeRecyclerAdapter extends RecyclerView.Adapter {
private Context mContext;
private List> mVisibleNodes;
private List> mRootNodes;
private OnNodeItemClickListener mOnNodeItemClickListener;
private int mSelectMode = TreeRecyclerView.MODE_SINGLE_SELECT;
private OnNodeSwitchListener mOnNodeSwitchListener = new OnNodeSwitchListener() {
@Override
public void onExpand(Node node, int position) {
NodeDataConverter.expandNode(NodeDataConverter.filterNodeById(node.getId(), mRootNodes));
rearrangeVisibleNodes();
}
@Override
public void onShrink(Node node, int position) {
NodeDataConverter.shrinkNode(NodeDataConverter.filterNodeById(node.getId(), mRootNodes));
rearrangeVisibleNodes();
}
};
private OnNodeCheckListener mOnNodeCheckListener = new OnNodeCheckListener() {
@Override
public void onCheck(boolean isChecked, int position, Node node) {
if (mOnNodeItemClickListener != null) {
mOnNodeItemClickListener.onItemClick(node.getContent());
}
NodeDataConverter.checkNode(node.getId(), isChecked, mSelectMode, mVisibleNodes);
notifyDataSetChanged();
}
};
public TreeRecyclerAdapter(Context context) {
mContext = context;
mVisibleNodes = new ArrayList<>();
mRootNodes = new ArrayList<>();
}
/**
* 填充数据
*
* @param nodes
*/
public void addAllData(List> nodes) {
if (nodes != null && !nodes.isEmpty()) {
mRootNodes.clear();
mRootNodes.addAll(nodes);
}
for (Node item : nodes) {
filterVisibleNodes(item);
}
notifyDataSetChanged();
}
/**
* 设置模式
*
* @param mode
*/
public void setMode(int mode) {
mSelectMode = mode;
}
/**
* 设置点击事件
*/
public void setOnItemClickListener(OnNodeItemClickListener listener) {
mOnNodeItemClickListener = listener;
}
/**
* 返回当前选择数据
*
* @return
*/
public void getSelectedItems(List list) {
List> resultNodeList = new ArrayList<>();
for (Node item : mRootNodes) {
resultNodeList.addAll(NodeDataConverter.filterCheckedNodeList(item));
}
for (Node item : resultNodeList) {
list.add(item.getContent());
}
}
@Override
public TreeBaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view;
switch (viewType) {
case Node.TREE_NODE:
view = LayoutInflater.from(mContext).inflate(
R.layout.listitem_tree_node, parent, false);
return new TreeNodeViewHolder(view);
case Node.TREE_LEAF:
view = LayoutInflater.from(mContext).inflate(
R.layout.listitem_tree_leaf, parent, false);
return new TreeLeafViewHolder(view);
default:
return null;
}
}
@Override
public void onBindViewHolder(TreeBaseViewHolder holder, int position) {
switch (getItemViewType(position)) {
case Node.TREE_NODE:
TreeNodeViewHolder nodeViewHolder = (TreeNodeViewHolder) holder;
nodeViewHolder.bindView(mVisibleNodes.get(position),
position, mOnNodeSwitchListener, mOnNodeCheckListener);
break;
case Node.TREE_LEAF:
TreeLeafViewHolder leafViewHolder = (TreeLeafViewHolder) holder;
leafViewHolder.bindView(mVisibleNodes.get(position),
position, mOnNodeCheckListener);
break;
default:
break;
}
}
@Override
public int getItemCount() {
return mVisibleNodes.size();
}
@Override
public int getItemViewType(int position) {
return mVisibleNodes.get(position).getType();
}
/**
* 根据visible属性,重新刷新,可视节点
*/
private void rearrangeVisibleNodes() {
if (mRootNodes == null || mRootNodes.size() <= 0) {
return;
}
mVisibleNodes.clear();
for (Node node : mRootNodes) {
filterVisibleNodes(node);
}
notifyDataSetChanged();
}
/**
* 将展开节点归结到可视节点中, 递归总是从根节点开始
*
* @return
*/
private void filterVisibleNodes(Node node) {
mVisibleNodes.add(node);
if (node.isExpanded()) {
if (node.getChildren() != null) {
for (Node item : node.getChildren()) {
filterVisibleNodes(item);
}
}
}
}
}
```
- Viewholder
- TreeBaseViewHolder.java
```
public class TreeBaseViewHolder extends RecyclerView.ViewHolder {
protected int mLevelMargin;
protected TreeBaseViewHolder(View itemView) {
super(itemView);
mLevelMargin = 30;
}
protected void setChecked(View view, boolean isChecked) {
if (isChecked) {
view.setVisibility(View.VISIBLE);
} else {
view.setVisibility(View.GONE);
}
}
}
```
- TreeLeafViewHolder.java
```
public class TreeLeafViewHolder extends TreeBaseViewHolder {
private RelativeLayout rl_content;
private TextView tv_name;
private TextView tv_label;
private ImageView iv_checkbox;
public TreeLeafViewHolder(View itemView) {
super(itemView);
rl_content = (RelativeLayout) itemView.findViewById(R.id.rl_content);
tv_name = (TextView) itemView.findViewById(R.id.tv_name);
tv_label = (TextView) itemView.findViewById(R.id.tv_label);
iv_checkbox = (ImageView) itemView.findViewById(R.id.iv_checkbox);
}
public void bindView(final Node node,
final int position,
final OnNodeCheckListener onNodeCheckListener) {
//根据节点层级,进行缩进处理
RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
rl_content.getLayoutParams();
params.leftMargin = mLevelMargin * node.getLevel();
rl_content.setLayoutParams(params);
//设置节点名称
tv_name.setText(node.getName());
//设置节点描述
tv_label.setText(node.getLabel());
//设置节点选中状态
setChecked(iv_checkbox, node.isChecked());
rl_content.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (onNodeCheckListener != null) {
onNodeCheckListener.onCheck(!node.isChecked(), position, node);
}
}
});
}
}
```
- TreeNodeViewHolder.java
```
public class TreeNodeViewHolder extends TreeBaseViewHolder {
private RelativeLayout rl_content;
private ImageView iv_icon;
private TextView tv_name;
private TextView tv_label;
private ImageView iv_checkbox;
public TreeNodeViewHolder(View itemView) {
super(itemView);
rl_content = (RelativeLayout) itemView.findViewById(R.id.rl_content);
tv_name = (TextView) itemView.findViewById(R.id.tv_name);
iv_icon = (ImageView) itemView.findViewById(R.id.iv_icon);
tv_label = (TextView) itemView.findViewById(R.id.tv_label);
iv_checkbox = (ImageView) itemView.findViewById(R.id.iv_checkbox);
}
public void bindView(final Node node,
final int position,
final OnNodeSwitchListener onNodeSwitchListener,
final OnNodeCheckListener onNodeCheckListener) {
//根据节点层级,进行缩进处理
RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
rl_content.getLayoutParams();
params.leftMargin = mLevelMargin * node.getLevel();
rl_content.setLayoutParams(params);
//设置节点名称
tv_name.setText(node.getName());
//设置节点描述
tv_label.setText(node.getLabel());
//设置节点选中状态
setChecked(iv_checkbox, node.isChecked());
//设置节点展开状态
setExpanded(iv_icon, node.isExpanded());
iv_icon.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (node.isExpanded()) {
onNodeSwitchListener.onShrink(node, position);
rotationExpandIcon(iv_icon, 0, -90);
} else {
onNodeSwitchListener.onExpand(node, position);
rotationExpandIcon(iv_icon, -90, 0);
}
}
});
rl_content.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (onNodeCheckListener != null) {
onNodeCheckListener.onCheck(!node.isChecked(), position, node);
}
}
});
}
/**
* 设置展开图标效果
*
* @param view
* @param isExpanded
*/
private void setExpanded(View view, boolean isExpanded) {
if (isExpanded) {
view.setRotation(0);
} else {
view.setRotation(-90);
}
}
/**
* 根据角度选择图标
*
* @param view
* @param from
* @param to
*/
private void rotationExpandIcon(final View view, final float from, final float to) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
ValueAnimator valueAnimator = ValueAnimator.ofFloat(from, to);
valueAnimator.setDuration(150);
valueAnimator.setInterpolator(new DecelerateInterpolator());
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
view.setRotation((Float) valueAnimator.getAnimatedValue());
}
});
valueAnimator.start();
}
}
}
```