查阅了很多关于层级数列的,笔者技术薄弱,因为用到了注解和反射什么的,感觉看起来比较复杂,所以想是否可以有别的方式实现,尝试了会儿之后实现了如图的效果
方块为占位的列项,具体图片自行更换
- 首先每列都为ImageView + TextView 所以xml文件是这样的list_item.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="40dp" >
<ImageView
android:id="@+id/id_item_icon"
android:layout_width="15dp"
android:layout_height="15dp"
android:layout_centerVertical="true"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
android:src="@drawable/tree_close" />
<TextView
android:layout_centerVertical="true"
android:id="@+id/id_item_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="@id/id_item_icon"
android:text="@string/hello_world"
android:textSize="18dp" />
</RelativeLayout>
- 因为涉及到层级,感觉用递归的方式去实现会比较简单,所以在设计Javabean时,考虑了树的数据结构的方式
TreeItem.java
public class TreeItem {
//储存的数据
private String mString;
//层级,方便缩进
private int level;
//是否展开
private boolean open;
//子项
private List<TreeItem> mSons;
public TreeItem(String string) {
mString = string;
//每个item默认层级为1,被添加到父类中时根据父类更改层级
this.level = 1;
this.open = false;
}
public String getString() {
return mString;
}
//是否有子项,若有则返回子项集合,若没有则为空
public List<TreeItem> getSons() {
if(mSons == null || mSons.size() == 0)
return null;
return mSons;
}
public TreeItem addSons(TreeItem sons) {
//在初次添加时候初始化
if(mSons == null)
mSons = new ArrayList<>();
//根据自己层级为子项设定层级
sons.setSonLevel(sons,this.level);
//把子项加入到自己的列表
mSons.add(sons);
return this;
}
public void setString(String string) {
mString = string;
}
public int getLevel() {
return level;
}
public void setLevel(int level) {
this.level = level;
}
public boolean isOpen() {
return open;
}
public void setOpen(boolean open) {
this.open = open;
}
private void setSonLevel(TreeItem treeItem,int level){
//子项的层级比父类大1
treeItem.setLevel(level+1);
//若有子项,则递归调用,设置其层级
if (treeItem.getSons() != null) {
for (TreeItem item : treeItem.getSons()) {
setSonLevel(item, item.getLevel());
}
}
}
}
这样的设计使得添加数据比较方便灵活,结构清晰,可以直接看出显示的效果
mTreeItems.add(new TreeItem("Game")
.addSons(new TreeItem("Steam")
.addSons(new TreeItem("CHi"))
.addSons(new TreeItem("Sha")
.addSons(new TreeItem("bbbb"))
.addSons(new TreeItem("cccc"))))
.addSons(new TreeItem("LOL"))
.addSons(new TreeItem("Car")));
- TreeListAdapter.java 在适配器中,分为两个列表,一个储存所有的子项,一个用于储存需要展示的数据,在每一次刷新列表时候都重新填充列表
public class TreeListAdapter extends RecyclerView.Adapter<TreeListAdapter.TreeViewHolder> {
private Context mContext;
//传入的列表数据
private List<TreeItem> mTreeItems;
//用于判断是否为第一次加载,方便为展示列表重新添加数据
private boolean begin;
//展示的列表数据
private List<TreeItem> mItemList;
public TreeListAdapter(Context context, List<TreeItem> treeItems) {
mContext = context;
mTreeItems = treeItems;
mItemList = new ArrayList<>();
begin = true;
}
@NonNull
@Override
public TreeViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(mContext).inflate(R.layout.list_item, parent, false);
TreeViewHolder holder = new TreeViewHolder(view);
return holder;
}
@Override
public void onBindViewHolder(@NonNull TreeViewHolder holder, int position) {
//在getList方法中为mItemList添加了所有需要展示出来的数据,所以根据顺序依次展示即可
TreeItem treeItem = mItemList.get(position);
//设置缩进
holder.itemView.setPadding(30 * treeItem.getLevel(), 0, 0, 0);
//如果有子类
if (treeItem.getSons() != null) {
//如果子项展开了
if (treeItem.isOpen()) {
holder.mImageView.setBackgroundResource(R.drawable.tree_open);
} else {
holder.mImageView.setBackgroundResource(R.drawable.tree_close);
}
//给有子项的列表图片添加点击事件,更改是否展开
holder.mImageView.setOnClickListener((v) -> {
treeItem.setOpen(!treeItem.isOpen());
//刷新列表,重新添加数据
begin = true;
notifyDataSetChanged();
});
} else {
//根节点设置标志
holder.mImageView.setBackgroundResource(R.drawable.ic_launcher_background);
}
//添加文本
holder.mTextView.setText(treeItem.getString());
}
//因为在每次刷新列表首先调用的是这个方法,所以在这个方法中填充mItemList列表
@Override
public int getItemCount() {
if (begin) {
mItemList.clear();
getList(mTreeItems);
//每次刷新只添加一次
begin = false;
}
return mItemList.size();
}
//传入TreeItem数列
private void getList(List<TreeItem> treeItems) {
//依次取出数列中的每一项
for (TreeItem treeItem : treeItems) {
//首先加入到需要展示的列表中
mItemList.add(treeItem);
//如果此项又有子项,且是展开的
if (treeItem.getSons() != null && treeItem.isOpen()) {
//则把此项的子项列表传入
getList(treeItem.getSons());
}
}
}
class TreeViewHolder extends RecyclerView.ViewHolder {
ImageView mImageView;
TextView mTextView;
public TreeViewHolder(@NonNull View itemView) {
super(itemView);
mImageView = itemView.findViewById(R.id.id_item_icon);
mTextView = itemView.findViewById(R.id.id_item_text);
}
}
}
- 主界面xml文件 activity_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/id_listview"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
</RelativeLayout>
- 主界面activity :MainActivity.java
public class MainActivity extends AppCompatActivity {
private RecyclerView mRecyclerView;
private List<TreeItem> mTreeItems = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
addlist();
TreeListAdapter adapter = new TreeListAdapter(this,mTreeItems);
mRecyclerView = findViewById(R.id.id_listview);
mRecyclerView.setAdapter(adapter);
mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
}
private void addlist(){
mTreeItems.add(new TreeItem("Game")
.addSons(new TreeItem("Steam")
.addSons(new TreeItem("CHi"))
.addSons(new TreeItem("Sha")
.addSons(new TreeItem("bbbb"))
.addSons(new TreeItem("cccc"))))
.addSons(new TreeItem("LOL"))
.addSons(new TreeItem("Car")));
}
}
github地址:https://github.com/jiangkerLove/RecyclerViewList