最近碰到一个需求,如何创建类似于windows文件管理器那样的文件目录,服务器返回的数据没有顺序和规律,只返回文件目录的每一个节点,节点中包括id和parentId,其他的属性都没有标志作用。写这篇文章的最大的目的是为了防止以后自己碰到类似的问题一头雾水,所以做个记录以便自己以后查看。
我做出来的效果图如下:(图一是当打开一个文件夹时自动关闭其他展开文件夹,图二是不自动关闭其他文件夹)
这个效果实现的思路不是很难:主要是以下几点:首先这个实现效果使用的是listview,每一个节点的层级效果通过convertView.setPadding(bean.getLevel() * 30, 30, 10, 30)这句设置条目内间距来实现,然后图标则是根据每一个节点的isExpand属性来控制。第二点也是最重要的一个方面对来自于服务器数据的排序,这个过程不是很难。第三点是最麻烦的一个点,就是当你打开或者关闭一个节点的时候,应该要向操作的节点集合中删除哪些节点或者是添加哪些节点,打开就是添加节点,关闭就是删除节点,然后notifyDataSetChanged()。
以下是代码实现:
节点对象类:
import java.util.ArrayList;
import java.util.List;
/**
* 文件描述:
* 作者:徐干稳
* 创建时间:2018/9/29
* 更改时间:2018/9/29
* 版本号:1.0
*/
public class TreeNodeBean {
private Integer id;
private Integer parentId;
private Integer level;
private TreeNodeBean parent;
private List<TreeNodeBean> children=new ArrayList<>();
private String name;
private boolean isExpand;
private boolean isVisible;
private int drawableResId;
private List<Integer> allParentIds=new ArrayList<>();
public TreeNodeBean(Integer id, Integer parentId,String name) {
this.id = id;
this.parentId = parentId;
this.name=name;
}
public int getDrawable() {
if(isExpand){
return R.drawable.bottom;
}else {
return R.drawable.right;
}
}
public List<Integer> getAllParentIds() {
return allParentIds;
}
public void setAllParentIds(List<Integer> allParentIds) {
this.allParentIds = allParentIds;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public List<TreeNodeBean> getChildren() {
return children;
}
public void setChildren(List<TreeNodeBean> children) {
this.children = children;
}
public Integer getParentId() {
return parentId;
}
public void setParentId(Integer parentId) {
this.parentId = parentId;
}
public Integer getLevel() {
return null==parent?0:parent.getLevel()+1;
}
public TreeNodeBean getParent() {
return parent;
}
public void setParent(TreeNodeBean parent) {
this.parent = parent;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public boolean isExpand() {
return isExpand;
}
public void setExpand(boolean expand) {
isExpand = expand;
if(!expand) {
if (children.size() != 0) {
for (TreeNodeBean treeNodeBean : children) {
treeNodeBean.setExpand(expand);
}
}
}
}
public boolean isVisible() {
return 0==parentId?true:parent.isExpand()?true:false;
}
public void setVisible(boolean visible) {
isVisible = visible;
}
/**
* 如果Id相同,即表示是同一个节点
* */
@Override
public boolean equals(Object obj) {
return id==((TreeNodeBean)obj).getId();
}
}
Activity界面类:
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ListView;
import java.util.ArrayList;
import java.util.List;
/**
* 文件描述:
* 作者:徐干稳
* 创建时间:2018/10/8
* 更改时间:2018/10/8
* 版本号:1.0
*/
public class SecondActivity extends Activity {
private ListView listview;
private List<TreeNodeBean> mDatas=new ArrayList<>();
private List<TreeNodeBean> adapterDatas=new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
listview=(ListView)findViewById(R.id.listview);
mDatas.add(new TreeNodeBean(1,0,"学科"));
mDatas.add(new TreeNodeBean(2,1,"语文"));
mDatas.add(new TreeNodeBean(3,1,"数学"));
mDatas.add(new TreeNodeBean(4,3,"数学1"));
mDatas.add(new TreeNodeBean(5,4,"数学2"));
mDatas.add(new TreeNodeBean(6,2,"语文2"));
mDatas.add(new TreeNodeBean(7,2,"语文1"));
mDatas.add(new TreeNodeBean(8,7,"文章1"));
mDatas.add(new TreeNodeBean(9,3,"算法1"));
mDatas.add(new TreeNodeBean(10,8,"文字分析1"));
mDatas.add(new TreeNodeBean(11,8,"文字分析2"));
mDatas.add(new TreeNodeBean(12,10,"文字分析3"));
mDatas.add(new TreeNodeBean(13,11,"文字分析4"));
mDatas.add(new TreeNodeBean(14,9,"算法分析1"));
mDatas.add(new TreeNodeBean(15,9,"算法分析2"));
mDatas.add(new TreeNodeBean(16,14,"算法分析3"));
mDatas.add(new TreeNodeBean(17,15,"算法分析4"));
mDatas=TreeHelper.initBeans(mDatas);
adapterDatas.add(mDatas.get(0)); //必须添加根节点
final MySecondAdapter adapter=new MySecondAdapter(this,adapterDatas);
listview.setAdapter(adapter);
listview.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
if(!adapterDatas.get(position).isExpand()){
adapterDatas=TreeHelper.addChilds(adapterDatas.get(position).getId(),position,mDatas,adapterDatas,false);
}else {
adapterDatas=TreeHelper.removeChilds(adapterDatas.get(position).getId(),position,mDatas,adapterDatas);
}
adapter.notifyDataSetChanged();
}
});
}
}
listview的adapter实现类:
扫描二维码关注公众号,回复:
5919156 查看本文章
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.List;
/**
* 文件描述:
* 作者:徐干稳
* 创建时间:2018/10/8
* 更改时间:2018/10/8
* 版本号:1.0
*/
public class MySecondAdapter extends MyTreeNodeAdapter {
private List<TreeNodeBean> mSortBeans = new ArrayList<>();
public MySecondAdapter(Context context, List<TreeNodeBean> mDatas) {
super(context, mDatas);
mSortBeans = mDatas;
}
@Override
public View getConvertView(int position,View convertView, ViewGroup parent) {
Holder holder = null;
if (null == convertView) {
holder = new Holder();
convertView = LayoutInflater.from(context).inflate(R.layout.file_item, null);
holder.name = convertView.findViewById(R.id.tv_name);
holder.image=(ImageView) convertView.findViewById(R.id.image);
convertView.setTag(holder);
} else {
holder = (Holder) convertView.getTag();
}
final TreeNodeBean bean = mSortBeans.get(position);
convertView.setPadding(bean.getLevel() * 30, 30, 10, 30);
holder.name.setText(bean.getName());
holder.image.setVisibility(View.INVISIBLE);
if(bean.getChildren().size()!=0){
holder.image.setVisibility(View.VISIBLE);
holder.image.setImageResource(bean.getDrawable());
}else {
holder.image.setVisibility(View.INVISIBLE);
}
return convertView;
}
public class Holder {
public ImageView image;
public TextView name;
}
}
排序&删除&添加节点帮助类(显示数据源获取帮助类)TreeHelper:
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
/**
* 文件描述:
* 作者:徐干稳
* 创建时间:2018/10/8
* 更改时间:2018/10/8
* 版本号:1.0
*/
public class TreeHelper {
/**
* 这种方式是一开始就加载所有数据,界面上显示有缺陷(listview展示时没有条目也显示分隔线),只作参考
*/
public static List<TreeNodeBean> getSortNodes(List<TreeNodeBean> beans) {
int solidSize = beans.size();
/**
* 设置父节点和子节点
* */
for (int i = 0; i < beans.size(); i++) {
TreeNodeBean child = beans.get(i);
for (int j = 0; j < beans.size(); j++) {
if (child.getParentId() == beans.get(j).getId()) {
child.setParent(beans.get(j));
beans.get(j).getChildren().add(child);
}
}
}
/**
* 根据父节点排序
* */
Collections.sort(beans, new Comparator<TreeNodeBean>() {
@Override
public int compare(TreeNodeBean o1, TreeNodeBean o2) {
return o1.getParentId() - o2.getParentId();
}
});
for (int i = 1; i < beans.size(); i++) {
beans.addAll(i + 1, beans.get(i).getChildren());
}
for (int i = 0; i < beans.size(); i++) {
for (int j = beans.size() - 1; j > i; j--) {
if (beans.get(i).getId() == beans.get(j).getId()) {
beans.remove(j);
}
}
}
return beans;
}
/**
* 初始化集合数据,互相添加子、父节点
*
*/
public static List<TreeNodeBean> initBeans(List<TreeNodeBean> beans) {
List<TreeNodeBean> newNodes = new ArrayList<>();
newNodes.add(beans.get(0));
for (int j = 0; j < newNodes.size(); j++) {
for (int i = 0; i < beans.size(); i++) {
if (beans.get(i).getParentId() == newNodes.get(j).getId() && !newNodes.contains(beans.get(i))) {
newNodes.add(j + 1, beans.get(i));
}
}
}
beans = newNodes;
for (int i = 0; i < beans.size(); i++) {
TreeNodeBean child = beans.get(i);
for (int j = 0; j < beans.size(); j++) {
if (child.getParentId() == beans.get(j).getId()) {
child.setParent(beans.get(j));
//添加父节点集合,后面做删除节点方法时需要用到,添加所有的父节点的id到parentsIds
child.getAllParentIds().addAll(beans.get(j).getAllParentIds());
child.getAllParentIds().add(beans.get(j).getId());
beans.get(j).getChildren().add(child);
}
}
}
return beans;
}
/**
* 展开时做添加处理,并作删除其他展开项处理
* @param autoClose 用于设置是否关闭其他已打开的同级节点
*/
public static List<TreeNodeBean> addChilds(int id, int position, List<TreeNodeBean> beans, List<TreeNodeBean> targetBeans, boolean autoClose) {
if (autoClose) {
TreeNodeBean clickedBean = targetBeans.get(position);
for (int i = 0; i < targetBeans.size(); i++) {
if (targetBeans.get(i).getAllParentIds().size() == clickedBean.getAllParentIds().size()
&& targetBeans.get(i).getId() != id) {
if (targetBeans.get(i).isExpand()) {
// targetBeans.removeAll(targetBeans.get(i).getChildren());
for (int j = 0; j < beans.size(); j++) {
if (beans.get(j).getAllParentIds().contains(targetBeans.get(i).getId())) {
targetBeans.remove(beans.get(j));
}
}
targetBeans.get(i).setExpand(false);
break;
}
}
}
for (int i = 0; i < targetBeans.size(); i++) {
if (targetBeans.get(i).getId() == id) {
position = i;
break;
}
}
}
for (int i = 0; i < beans.size(); i++) {
if (beans.get(i).getParentId() == id) {
targetBeans.add(position + 1, beans.get(i));
}
}
targetBeans.get(position).setExpand(true);
return targetBeans;
}
/**
* 折叠时做删除处理
*/
public static List<TreeNodeBean> removeChilds(int id, int position, List<TreeNodeBean> beans, List<TreeNodeBean> targetBeans) {
for (int i = 0; i < beans.size(); i++) {
if (beans.get(i).getAllParentIds().contains(id)) {
if (targetBeans.contains(beans.get(i)))
targetBeans.remove(beans.get(i));
}
}
targetBeans.get(position).setExpand(false);
return targetBeans;
}
}
以上就是所有的代码,还有一个比较关键的地方就是在拿到服务器数据之后,应该把根节点找到然后放到第一位置,然后再去做排序和增删查改操作,我在代码中有故意写到。