先看效果
难点: 数据层级的划分;理清楚层级关系, 剩下的就简单明了了;
1. 第一步 new Adapter, and setAdapter; (不需要setLayoutManager, Adapter里有set);
mAdapter = new TreeViewAdapter(getContext(),mRecyclerView);
mRecyclerView.setAdapter(mAdapter);
2. 第二步, 划分数据层级;(不好说, 自己看代码理解)
注意点: CommonMenu的构造方法中初始化层级, 是否展开;
CommonMenu 就是菜单, 菜单中也可能包含菜单, 具体是包含还是并列同级, 取决于Level_等级;
0~3都是菜单, LEVEL_ITEM 为具体对象;
Set数据过程 以及需要的实体类 如下:(具体对象的实体类就贴了一个KaKou, 其他的类似, 继承BaseMenu即可)
//初始化 Adapter需要的数据mBaseList
List<BaseMenu> mBaseList = new ArrayList<>();
for (int i = 0; i < 2; i++) {
CommonMenu lcMenu = new CommonMenu(BaseMenu.LEVEL_1, false);
lcMenu.setName("林场" + i);
List<BaseMenu> lcChildList = new ArrayList<>();
lcMenu.setChild(lcChildList);
CommonMenu yunTaiMenu = new CommonMenu(BaseMenu.LEVEL_2, false);
yunTaiMenu.setName("云台");
lcChildList.add(yunTaiMenu);
List<BaseMenu> ytChildList = new ArrayList<>();
for (int j = 0; j < 2; j++) {
YunTai item = new YunTai();
item.setName("云台Name" + j);
ytChildList.add(item);
}
yunTaiMenu.setChild(ytChildList);
CommonMenu kkMenu = new CommonMenu(BaseMenu.LEVEL_2, false);
kkMenu.setName("卡口");
lcChildList.add(kkMenu);
List<BaseMenu> kkChildList = new ArrayList<>();
for (int j = 0; j < 2; j++) {
KaKou item = new KaKou();
item.setName("卡口Name" + j);
kkChildList.add(item);
}
kkMenu.setChild(kkChildList);
CommonMenu fdMenu = new CommonMenu(BaseMenu.LEVEL_2, false);
fdMenu.setName("防盗");
lcChildList.add(fdMenu);
List<BaseMenu> fdChildList = new ArrayList<>();
for (int j = 0; j < 2; j++) {
FangDao item = new FangDao();
item.setName("防盗Name" + j);
fdChildList.add(item);
}
fdMenu.setChild(fdChildList);
CommonMenu qxzMenu = new CommonMenu(BaseMenu.LEVEL_2, false);
qxzMenu.setName("气象站");
lcChildList.add(qxzMenu);
List<BaseMenu> qxzChildList = new ArrayList<>();
for (int j = 0; j < 2; j++) {
QiXiangZhan item = new QiXiangZhan();
item.setName("气象站Name" + j);
qxzChildList.add(item);
}
qxzMenu.setChild(qxzChildList);
mBaseList.add(lcMenu);
}
mAdapter.setBaseList(mBaseList);
mAdapter.notifyDataSetChanged();
public abstract class BaseMenu {
public static final int LEVEL_0 = 0;//林局
public static final int LEVEL_1 = 1;//林场
public static final int LEVEL_2 = 2;//云台
public static final int LEVEL_3 = 3;//备用-管护站
public static final int LEVEL_ITEM = 4;//具体设备
protected String name;
protected int level = LEVEL_ITEM;
protected boolean expand = true;
public boolean isExpand() {
return expand;
}
public void setExpand(boolean expand) {
this.expand = expand;
}
public int getLevel() {
return level;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public boolean isItem(){
return level == LEVEL_ITEM;
}
public abstract List<BaseMenu> getChild();
}
public class CommonMenu extends BaseMenu {
private List<BaseMenu> child;
public List<BaseMenu> getChild() {
return child;
}
public void setChild(List<BaseMenu> child) {
this.child = child;
}
public CommonMenu(int level, boolean expand) {
this.level = level;
this.expand = expand;
}
}
public class KaKou extends BaseMenu{
private String uuid;
private String name;
private String lon;
private String lat;
public String getUuid() {
return uuid;
}
public void setUuid(String uuid) {
this.uuid = uuid;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public List<BaseMenu> getChild() {
return null;
}
public String getLon() {
return lon;
}
public void setLon(String lon) {
this.lon = lon;
}
public String getLat() {
return lat;
}
public void setLat(String lat) {
this.lat = lat;
}
}
3. 第三步 愉快的复制粘贴过程(Adapter, 以及 布局)
import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import com.sxwy.fanghuo.R;
import com.sxwy.fanghuo.data.BaseMenu;
import java.util.ArrayList;
import java.util.List;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
/**
* Created by ZhouWengong on 2023/5/17.
*/
public class TreeViewAdapter extends RecyclerView.Adapter<ViewHolder> {
public static final String TAG = TreeViewAdapter.class.getSimpleName();
private LinearLayoutManager layoutManager;
private final List<BaseMenu> mCurrentList;//当前展示的数据
private Context mContext;
private RecyclerView mRecyclerView;
public TreeViewAdapter(Context context, RecyclerView recyclerView) {
mContext = context;
mCurrentList = new ArrayList<>();
if (recyclerView != null) {
mRecyclerView = recyclerView;
layoutManager = new LinearLayoutManager(context);
mRecyclerView.setLayoutManager(layoutManager);
//展开与关闭时的动画耗时;
RecyclerView.ItemAnimator itemAnimator = recyclerView.getItemAnimator();
if (itemAnimator != null) {
int duration = 10;
itemAnimator.setAddDuration(duration);
itemAnimator.setRemoveDuration(duration);
}
}
}
public void setBaseList(List<BaseMenu> baseList) {
if (baseList != null) {
initCurrentMenu(baseList);
}
}
private void initCurrentMenu(List<BaseMenu> baseMenus) {
for (BaseMenu baseMenu : baseMenus) {
mCurrentList.add(baseMenu);
if (!baseMenu.isItem()) {
if (baseMenu.getChild() != null && baseMenu.isExpand()) {
initCurrentMenu(baseMenu.getChild());
}
}
}
}
private void onMenuExpand(BaseMenu baseMenu, int position) {
if (baseMenu.getChild() != null) {
int size = mCurrentList.size();
if (baseMenu.isExpand()) {
int lastVisibleItemPosition = layoutManager.findLastVisibleItemPosition();
insertMenuChild(baseMenu, position + 1);
int itemCount = mCurrentList.size() - size;
notifyItemRangeInserted(position + 1, itemCount);
notifyItemRangeChanged(position, mCurrentList.size());
int toPosition = position + itemCount;
if (lastVisibleItemPosition < toPosition) {
mRecyclerView.scrollToPosition(toPosition);
}
} else {
removeMenuChild(baseMenu);
notifyItemRangeRemoved(position + 1, size - mCurrentList.size());
notifyItemRangeChanged(position, mCurrentList.size());
}
} else {
notifyItemChanged(position);
}
}
private void insertMenuChild(BaseMenu menu, int position) {
if (menu.isExpand()) {
List<BaseMenu> child = menu.getChild();
if (child != null) {
// for (BaseMenu baseMenu : child) {//child逆序添加,因为position固定;
// mCurrentList.add(position , baseMenu);
// if (!baseMenu.isItem() && baseMenu.isExpand()) {
// insertMenuChild(baseMenu, position + 1);
// }
// }
for (int i = child.size() - 1; i >= 0; i--) {//逆序遍历, 正序添加
BaseMenu baseMenu = child.get(i);
mCurrentList.add(position, baseMenu);
if (!baseMenu.isItem() && baseMenu.isExpand()) {
insertMenuChild(baseMenu, position + 1);
}
}
}
}
}
private void removeMenuChild(BaseMenu menu) {
List<BaseMenu> child = menu.getChild();
if (child != null) {
mCurrentList.removeAll(child);
for (BaseMenu baseMenu : child) {
removeMenuChild(baseMenu);
}
}
}
public int getLayoutId(int viewType) {
int ret = 0;
switch (viewType) {
case BaseMenu.LEVEL_0:
break;
case BaseMenu.LEVEL_1:
ret = R.layout.item_treeview_menu_1;
break;
case BaseMenu.LEVEL_2:
ret = R.layout.item_treeview_menu_2;
break;
case BaseMenu.LEVEL_3:
break;
case BaseMenu.LEVEL_ITEM:
ret = R.layout.item_treeview_menu_item;
break;
}
return ret;
}
@Override
public int getItemViewType(int position) {
return mCurrentList.get(position).getLevel();
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
return ViewHolder.getViewHolder(mContext, parent, getLayoutId(viewType));
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
BaseMenu baseMenu = mCurrentList.get(position);
((TextView) holder.getView(R.id.text)).setText(baseMenu.getName());
if (!baseMenu.isItem()) {//我的图片资源箭头是朝右的,根据实际情况修改即可
holder.getView(R.id.indicator).setRotation(baseMenu.isExpand() ? 90 : 270);
}
View holderView = holder.getView(R.id.parent);
holderView.setTag(position);
holderView.setOnClickListener(onClickListener);
}
private final View.OnClickListener onClickListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
int position = (int) v.getTag();
BaseMenu baseMenu = mCurrentList.get(position);
if (!baseMenu.isItem()) {
baseMenu.setExpand(!baseMenu.isExpand());
onMenuExpand(baseMenu, position);
}else {
//todo item点击时触发
}
}
};
@Override
public int getItemCount() {
return mCurrentList.size();
}
}
布局只传了一个, 根据情况复制粘贴就可以,
注意: 每个Level 对应一个布局, 每个布局的paddingStart应该设置合理的间距(@dimen/treeViewMarginLevel_1)
item_treeview_menu_1.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/parent"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="60dp"
android:background="@drawable/shape_white_radius_5"
android:gravity="center_vertical"
android:paddingStart="@dimen/treeViewMarginLevel_1">
<ImageView
android:id="@+id/headPic"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_marginStart="10dp"
android:layout_marginEnd="10dp"
android:src="@mipmap/ic_cf_icon" />
<TextView
android:id="@+id/text"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="名字"
android:textColor="@color/text_black"
android:textSize="14sp"
android:textStyle="bold" />
<ImageView
android:id="@+id/indicator"
android:layout_width="15dp"
android:layout_height="15dp"
android:layout_marginStart="20dp"
android:layout_marginEnd="10dp"
android:rotation="90"
android:src="@mipmap/ic_next_page_indicator"
android:visibility="visible" />
</LinearLayout>
<View
android:layout_width="0dp"
android:layout_height="1dp" />
</LinearLayout>