最快的Android TreeView出现了!
源码地址:https://github.com/niugao/RecyclerListTreeView
- 基于RecyclerView实现。
- 存储数据的结构并不是Tree,而是一个ArrayList。与所有已知的网上的实现都不一样,大家似乎都跳不出固定思维。 可以比较一下代码量,此实现比其它的少一半都不止。
- 核心是一个表示Tree的类,但它的本质是一个List。对RecyclerView没有任何改动,对Adapter只有少量封装, 使用者不会产生任何陌生感。也就是说你对RecyclerView能做的,现在依然能做。
- 以List的形式表式树,带来很多好处:
-- 没有了递归。该用递归的地方全部变成了循环(Tree不论有多少层都没有栈溢出)。
-- 其次是有序,插入节点时,可以指定它是它爸爸的第几个儿子。
-- 极其适合在RecyclerView中使用。
-- 跟List无异,无论根节点还是子节点都对应RecyclerView中的一行。
-- 不需对RecyclerView做任何改动。
有诗为证
远看像棵树
近看不是树
似树而非树
是为牛逼树
示例
Adapter:
public class ExampleListTreeAdapter extends
ListTreeAdapter<ExampleListTreeAdapter.BaseViewHolder> {
//行上弹出菜单的侦听器
private PopupMenu.OnMenuItemClickListener itemMenuClickListener;
//记录弹出菜单是在哪个行上出现的
private ListTree.TreeNode currentNode;
//保存子行信息的类
public static class ContactInfo{
//头像,用于设置给ImageView。
private Bitmap bitmap;
//标题
private String title;
//描述
private String detail;
public ContactInfo(Bitmap bitmap, String title, String detail) {
this.bitmap = bitmap;
this.title = title;
this.detail = detail;
}
public Bitmap getBitmap() {
return bitmap;
}
public String getTitle() {
return title;
}
public String getDetail() {
return detail;
}
}
//构造方法
public ExampleListTreeAdapter(ListTree tree,
PopupMenu.OnMenuItemClickListener listener){
super(tree);
this.itemMenuClickListener=listener;
}
public ListTree.TreeNode getCurrentNode() {
return currentNode;
}
@Override
protected BaseViewHolder onCreateNodeView(ViewGroup parent, int viewType){
View view = null;
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
BaseViewHolder vh;
//创建不同的行View
if(viewType==R.layout.contacts_group_item){
//最后一个参数必须传true
view = inflater.inflate(viewType,parent,true);
vh=new GroupViewHolder(view);
}else if(viewType == R.layout.contacts_contact_item){
view = inflater.inflate(viewType,parent,true);
vh=new ContactViewHolder(view);
}else{
return null;
}
return vh;
}
@Override
protected void onBindNodeViewHolder(BaseViewHolder holder, int position) {
View view = holder.itemView;
//get node at the position
ListTree.TreeNode node = tree.getNodeByPlaneIndex(position);
if(node.getLayoutResId() == R.layout.contacts_group_item){
//group node
String title = (String)node.getData();
GroupViewHolder gvh= (GroupViewHolder) holder;
gvh.textViewTitle.setText(title);
gvh.textViewCount.setText("0/"+node.getChildrenCount());
gvh.aSwitch.setChecked(node.isChecked());
gvh.aSwitch.setTag(node);
gvh.textViewMenu.setTag(node);
}else if(node.getLayoutResId() == R.layout.contacts_contact_item){
//child node
ContactInfo info = (ContactInfo) node.getData();
ContactViewHolder cvh= (ContactViewHolder) holder;
cvh.imageViewHead.setImageBitmap(info.getBitmap());
cvh.textViewTitle.setText(info.getTitle());
cvh.textViewDetail.setText(info.getDetail());
cvh.aSwitch.setChecked(node.isChecked());
cvh.aSwitch.setTag(node);
}
}
//组行和联系人行的Holder基类
class BaseViewHolder extends ListTreeViewHolder{
public BaseViewHolder(View itemView) {
super(itemView);
}
}
//将ViewHolder声明为Adapter的内部类,反正外面也用不到
class GroupViewHolder extends BaseViewHolder {
TextView textViewTitle;
TextView textViewCount;
Switch aSwitch;
TextView textViewMenu;
public GroupViewHolder(View itemView) {
super(itemView);
textViewTitle = itemView.findViewById(R.id.textViewTitle);
textViewCount = itemView.findViewById(R.id.textViewCount);
aSwitch = itemView.findViewById(R.id.switchChecked);
textViewMenu = itemView.findViewById(R.id.textViewMenu);
//应响应点击事件而不是CheckedChange事件,因为那样会引起事件的递归触发
aSwitch.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
ListTree.TreeNode node = (ListTree.TreeNode) view.getTag();
node.setChecked(!node.isChecked());
int planeIndex = tree.getNodePlaneIndex(node);
//改变所有的子孙们的状态
int count =tree.setDescendantChecked(planeIndex,node.isChecked());
notifyItemRangeChanged(planeIndex,count+1);
}
});
//点了PopMenu控件,弹出PopMenu
textViewMenu.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ListTree.TreeNode node = (ListTree.TreeNode) v.getTag();
currentNode=node;
PopupMenu popup = new PopupMenu(v.getContext(), v);
popup.setOnMenuItemClickListener(itemMenuClickListener);
MenuInflater inflater = popup.getMenuInflater();
inflater.inflate(R.menu.menu_item, popup.getMenu());
popup.show();
}
});
}
}
class ContactViewHolder extends BaseViewHolder{
ImageView imageViewHead;
TextView textViewTitle;
TextView textViewDetail;
Switch aSwitch;
public ContactViewHolder(View itemView) {
super(itemView);
imageViewHead = itemView.findViewById(R.id.imageViewHead);
textViewTitle = itemView.findViewById(R.id.textViewTitle);
textViewDetail = itemView.findViewById(R.id.textViewDetail);
aSwitch = itemView.findViewById(R.id.switchChecked);
//应响应点击事件而不是CheckedChange事件,因为那样会引起事件的递归触发
aSwitch.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
ListTree.TreeNode node = (ListTree.TreeNode) view.getTag();
node.setChecked(!node.isChecked());
int planeIndex = tree.getNodePlaneIndex(node);
//改变所有的子孙们的状态
int count =tree.setDescendantChecked(planeIndex,node.isChecked());
notifyItemRangeChanged(planeIndex,count+1);
}
});
}
}
}
使用Adapter
public class MainActivity extends AppCompatActivity
implements PopupMenu.OnMenuItemClickListener {
//保存数据的集合
private ListTree tree=new ListTree();
//从ListTreeAdapter派生的Adapter
ExampleListTreeAdapter adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
//使用Android原生的RecyclerView即可
RecyclerView listView = findViewById(R.id.listview);
//创建后台数据:一棵树
//创建组们,是root node,所有parent为null
ListTree.TreeNode groupNode1=tree.addNode(null,"特别关心", R.layout.contacts_group_item);
ListTree.TreeNode groupNode2=tree.addNode(null,"我的好友", R.layout.contacts_group_item);
ListTree.TreeNode groupNode3=tree.addNode(null,"朋友", R.layout.contacts_group_item);
ListTree.TreeNode groupNode4=tree.addNode(null,"家人", R.layout.contacts_group_item);
ListTree.TreeNode groupNode5=tree.addNode(null,"同学", R.layout.contacts_group_item);
//第二层
ExampleListTreeAdapter.ContactInfo contact;
Bitmap bitmap= BitmapFactory.decodeResource(getResources(), R.drawable.contacts_normal);
contact = new ExampleListTreeAdapter.ContactInfo(bitmap,"王二","[在线]我是王二");
ListTree.TreeNode contactNode1=tree.addNode(groupNode2,contact,R.layout.contacts_contact_item);
ListTree.TreeNode contactNode2=tree.addNode(groupNode5,contact,R.layout.contacts_contact_item);
//再添加一个
bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.contacts_normal);
contact=new ExampleListTreeAdapter.ContactInfo(bitmap,"王三","[离线]我没有状态");
tree.addNode(groupNode2,contact,R.layout.contacts_contact_item);
tree.addNode(groupNode5,contact,R.layout.contacts_contact_item);
//第三层
bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.contacts_normal);
contact=new ExampleListTreeAdapter.ContactInfo(bitmap,"东邪","[离线]出来还价");
ListTree.TreeNode n=tree.addNode(contactNode1,contact,R.layout.contacts_contact_item);
n.setShowExpandIcon(false);
//再添加一个
bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.contacts_normal);
contact=new ExampleListTreeAdapter.ContactInfo(bitmap,"李圆圆","[离线]昨天出门没出去");
n=tree.addNode(contactNode1,contact,R.layout.contacts_contact_item);
n.setShowExpandIcon(false);
adapter=new ExampleListTreeAdapter(tree,this);
listView.setLayoutManager(new LinearLayoutManager(this));
listView.setAdapter(adapter);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}else if(id == R.id.action_del_selected){
//删除选中的Nodes,删一个Node时会将其子孙一起删掉
tree.removeCheckedNodes();
adapter.notifyDataSetChanged();
return true;
}
return super.onOptionsItemSelected(item);
}
@Override
public boolean onMenuItemClick(MenuItem item){
switch (item.getItemId()) {
case R.id.action_add_item:
//向当前行增加一个儿子
ListTree.TreeNode node = adapter.getCurrentNode();
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.contacts_normal);
ExampleListTreeAdapter.ContactInfo contact=new ExampleListTreeAdapter.ContactInfo(
bitmap,"New contact","[离线]我没有状态");
ListTree.TreeNode childNode = tree.addNode(node,contact,R.layout.contacts_contact_item);
adapter.notifyTreeItemInserted(node,childNode);
return true;
default:
return false;
}
}
}