The fastest Android TreeView appears!
Source address: https://github.com/niugao/RecyclerListTreeView
- Based on RecyclerView.
- The structure of storing data is not a Tree, but an ArrayList. Unlike all the known implementations on the Internet, it seems that people can't jump out of fixed thinking. You can compare the amount of code. This implementation is less than half of the others.
- The core is a class representing Tree, but its essence is a List. There is no change to RecyclerView, only a small amount of encapsulation to Adapter, and users will not feel strange. That is to say, what you can do with RecyclerView is still possible.
-
Table tree in the form of List brings many benefits:
- no recursion. All the places where recursion should be used become loops (Tree has no stack overflow no matter how many layers there are).
- next is order. When inserting a node, you can specify that it is the father's son.
– perfect for RecyclerView.
– no different from List, both the root node and the child node correspond to a row in RecyclerView.
– no changes to RecyclerView are required.
Poetry as evidence
It looks like a tree in the distance
It's not a tree
Like a tree, not a tree
It's for cattle
Example
Adapter:
public abstract class ListTreeAdapter<VH extends ListTreeViewHolder>
extends RecyclerView.Adapter<VH> {
protected ListTree tree;
//Drawable resource id for expand and collapse icons
private Bitmap expandIcon=null;
private Bitmap collapseIcon=null;
//Construction method
public ListTreeAdapter(ListTree tree){
this.tree=tree;
}
public ListTreeAdapter(ListTree tree,Bitmap expandIcon,Bitmap collapseIcon){
this.tree=tree;
this.expandIcon=expandIcon;
this.collapseIcon=collapseIcon;
}
@Override
final public VH onCreateViewHolder(ViewGroup parent, int viewType) {
if(expandIcon==null){
expandIcon=BitmapFactory.decodeResource(
parent.getContext().getResources(), R.drawable.expand);
}
if(collapseIcon==null){
collapseIcon=BitmapFactory.decodeResource(
parent.getContext().getResources(),R.drawable.collapse);
}
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
ViewGroup container = (ViewGroup) inflater.inflate(
R.layout.row_container_layout,parent,false);
//To shrink or expand in response to a click event on Arrow
ImageView arrowIcon = container.findViewById(R.id.listtree_arrowIcon);
//Follow the width of the list control to calculate an appropriate size for it
int w= parent.getMeasuredWidth();
arrowIcon.getLayoutParams().width=w/15;
arrowIcon.getLayoutParams().height=w/15;
arrowIcon.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ListTree.TreeNode node = (ListTree.TreeNode) v.getTag();
if(node.isShowExpandIcon()) {
int nodePlaneIndex = tree.getNodePlaneIndex(node);
if (node.isExpand()) {
//Retract
int count = tree.collapseNode(nodePlaneIndex);
notifyItemChanged(nodePlaneIndex);
//Notify view to delete related lines
notifyItemRangeRemoved(nodePlaneIndex + 1, count);
} else {
//Open
int count = tree.expandNode(nodePlaneIndex);
notifyItemChanged(nodePlaneIndex);
//Notify view to insert related lines
notifyItemRangeInserted(nodePlaneIndex + 1, count);
}
}
}
});
//Subclass to create their own row view
VH vh = onCreateNodeView(container,viewType);
if(vh==null){
return null;
}
vh.containerView = container;
vh.arrowIcon=arrowIcon;
vh.headSpace=container.findViewById(R.id.listtree_head_space);
//container.addView(vh.itemView);
return vh;
}
protected abstract VH onCreateNodeView(ViewGroup parent, int viewType);
protected abstract void onBindNodeViewHolder(VH viewHoler,int position);
@Override
final public int getItemViewType(int position) {
int count=0;
ListTree.TreeNode node = tree.getNodeByPlaneIndex(position);
return node.getLayoutResId();
}
@Override
final public void onBindViewHolder(VH holder, int position) {
//get node at the position
ListTree.TreeNode node = tree.getNodeByPlaneIndex(position);
if(node.isShowExpandIcon()) {
if (node.isExpand()) {
holder.arrowIcon.setImageBitmap(collapseIcon);
} else {
holder.arrowIcon.setImageBitmap(expandIcon);
}
}else{
//No icon needed
holder.arrowIcon.setImageBitmap(null);
}
holder.arrowIcon.setTag(node);
//Change the indent distance according to the layer depth of node, starting from 0
int layer = tree.getNodeLayerLevel(node);
holder.headSpace.getLayoutParams().width=layer*20;
//Give subclasses the opportunity to bind row data
onBindNodeViewHolder(holder,position);
}
@Override
final public int getItemCount() {
return tree.size();
}
}
Using Adapter
public class MainActivity extends AppCompatActivity {
private ListTree tree=new ListTree();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
.setAction("Action", null).show();
}
});
RecyclerView listView = findViewById(R.id.listview);
//Create background data: a tree
//Create groups. It's root node. All parent s are null
ListTree.TreeNode groupNode1=tree.addNode(null,"Special care", R.layout.contacts_group_item);
ListTree.TreeNode groupNode2=tree.addNode(null,"My friends", R.layout.contacts_group_item);
ListTree.TreeNode groupNode3=tree.addNode(null,"Friend", R.layout.contacts_group_item);
ListTree.TreeNode groupNode4=tree.addNode(null,"Family", R.layout.contacts_group_item);
ListTree.TreeNode groupNode5=tree.addNode(null,"Classmate", R.layout.contacts_group_item);
//The second floor
ExampleListTreeAdapter.ContactInfo contact;
Bitmap bitmap= BitmapFactory.decodeResource(getResources(), R.drawable.contacts_normal);
contact = new ExampleListTreeAdapter.ContactInfo(
bitmap,"WangTwo","[On-line]I'm Wang Er");
ListTree.TreeNode contactNode1=tree.addNode(groupNode2,contact,R.layout.contacts_contact_item);
ListTree.TreeNode contactNode2=tree.addNode(groupNode5,contact,R.layout.contacts_contact_item);
//Add another
bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.contacts_normal);
contact=new ExampleListTreeAdapter.ContactInfo(bitmap,"Wang San","[Off-line]I'm not in shape");
tree.addNode(groupNode2,contact,R.layout.contacts_contact_item);
tree.addNode(groupNode5,contact,R.layout.contacts_contact_item);
//Third floor
bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.contacts_normal);
contact=new ExampleListTreeAdapter.ContactInfo(bitmap,"East evil","[Off-line]Come out and make a counter-offer");
ListTree.TreeNode n=tree.addNode(contactNode1,contact,R.layout.contacts_contact_item);
n.setShowExpandIcon(false);
//Add another
bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.contacts_normal);
contact=new ExampleListTreeAdapter.ContactInfo(bitmap,"Li Yuanyuan","[Off-line]I didn't go out yesterday");
n=tree.addNode(contactNode1,contact,R.layout.contacts_contact_item);
n.setShowExpandIcon(false);
ExampleListTreeAdapter adapter=new ExampleListTreeAdapter(tree);
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) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
}