recyclerview中树状结构的实现,加载本地中文件夹信息

引文:
在项目实现中,对于树状图结构的分析一直无法实现正确的效果,结果查看别人的项目都不要适合我的应用场景,但是查看其实原理是差不多的,但是我没有看明白,所以一直在看这方面的东西。查阅并修改他人的代码实现文件夹效果,看着还是有点繁琐,先记录,后续再进行数据的修改。声明一点,我的代码是在别人的代码的基础上进行修改。

  • 效果呈现
    recyclerview中树状结构的实现,加载本地中文件夹信息_第1张图片

  • 系统讲解

    • 需要的控件:recyclerview(其实使用Listview也可以,就是adapter中结构不一样)
    • 实现步骤
      (1)数据初始化
      (2)点击监听,数据更新
      (3)数据处理,构建树状结构
  • 具体的代码实现如下所示:

1、在activity或者在fragment中进行数据的初始化和数据更新

private void getLocalData() {
		//获取本地路径
		List<FileNodeBean> fileList = new ArrayList<>();
		//获取本地文件目录
		File path = Environment.getExternalStorageDirectory();
		Log.d(TAG, "onClick: " + path.getName());
		//获取文件子项
		File[] mfile = path.listFiles();
		List<File> mAllLocalData = new ArrayList<>();

		//目录初始化
		for (int i = 0 ; i < mfile.length; i++) {
		//本地文件中存在隐藏的文件,不需要显示
			if (!mfile[i].isHidden()) {
				if (MineTypeUtil.isShowFile(mfile[i])){
					FileNodeBean fileNode = new FileNodeBean("local" + i, 0, mfile[i].getName());
					fileNode.setParent(null);
					fileNode.setLevel(0);
					fileNode.setId(mfile[i].getPath());
					fileNode.setPath(mfile[i].getPath());
					fileNode.setVidelFile(MineTypeUtil.isVideoFile(mfile[i].getPath()));
					fileList.add(fileNode);

					mAllLocalData.add(mfile[i]);
				}
			}
		}
		//适配adapter,初始化数据
			mFileAdapter = new FileAdapter(mFileRecyclerView, mContext);
			mFileAdapter.updateData(fileList, 0);
			mFileRecyclerView.setAdapter(mFileAdapter);
			//点击图标的监听事件
			mFileAdapter.setNodeDataListener(new FileAdapter.onNodeDataListener() {
				@Override
				public void onNodeDataListener(FileNodeBean node) {
					Log.d(TAG, "onNodeDataListener: + getName " + node.getName());
					List<FileNodeBean> childList = new ArrayList<>();

					for (int i = 0 ; i < mAllLocalData.size(); i++) {
						//通过路径判断获取点击的item
						if (mAllLocalData.get(i).getPath().equals(node.getPath())) {
							Log.d(TAG, "onNodeDataListener:node.path " + node.getPath());
							if (mAllLocalData.get(i).listFiles() != null){
							//获取是否存在子项
								Log.d(TAG, "onNodeDataListener:listfile " + mAllLocalData.get(i).listFiles());
								int m = 0;
								//遍历点击item的子项
								for (int j = 0; j < mAllLocalData.get(i).listFiles().length; j++) {

									//子项为目录或者音频文件时
									if (MineTypeUtil.isShowFile(mAllLocalData.get(i).listFiles()[j])){
										m++;
										FileNodeBean fileNodeBean = new FileNodeBean();
										fileNodeBean.setId(mAllLocalData.get(i).listFiles()[j].getPath());
										fileNodeBean.setName(mAllLocalData.get(i).listFiles()[j].getName());
										fileNodeBean.setExpand(false);
										fileNodeBean.setParent(node);
										fileNodeBean.setPath(mAllLocalData.get(i).listFiles()[j].getPath());
										fileNodeBean.setVidelFile(MineTypeUtil.isVideoFile(mAllLocalData.get(i).listFiles()[j].getPath()));
										fileNodeBean.setLevel(node.getLevel() + 1);
										childList.add(fileNodeBean);
										//更新列表
										if (node.isExpand()){
											mAllLocalData.remove(mAllLocalData.get(i).listFiles()[j]);
										}else {
											mAllLocalData.add(i + m, mAllLocalData.get(i).listFiles()[j]);
										}
									}
								}
							//更新fieList的数据
							for(int k = 0; k < fileList.size(); k++){
								if (fileList.get(k).getPath().equals(node.getPath())) {
									fileList.get(k).setChildren(childList);
									break;
								}
							}
							//对于数据展开与闭合的处理

							if (node.isExpand()) {
								childList.clear();
								node.setChildren(childList);
								node.setExpand(!node.isExpand());
								mFileAdapter.updateData(fileList, node.getLevel() - 1);
							} else {
								node.setChildren(childList);
								node.setExpand(!node.isExpand());
								mFileAdapter.updateData(fileList, node.getLevel() + 1);
							}
						}else {
								Log.d(TAG, "onNodeDataListener: " + mAllLocalData.get(i).listFiles());
								Toast.makeText(mContext, "子项为空", Toast.LENGTH_SHORT).show();
							}
							break;
						}

					}}
			});

	}
  • recyclerview中adapter处理,继承于TreeListViewAdapter ,将一些通用的数据进行处理
public class FileAdapter extends TreeListViewAdapter {

	private static final String TAG = " TreeListViewAdapter ";
	private onNodeDataListener onNodeDataListener;
	private FileAdapter.onItemDataListener onItemDataListener ;
	
	public FileAdapter(RecyclerView mTree, Context context) {
		super(mTree, context);
	}

	private List<FileNodeBean> mDatas= new ArrayList<>();
	//数据更新,每一次点击之后,数据展开关闭产生的数据变化
	@Override
	public void updateData(List<FileNodeBean> datas, int defaultExpandLevel) {

		super.updateData(datas, defaultExpandLevel);
	}

	@Override
	protected void getBindViewHolder(FileNodeBean node, RecyclerView.ViewHolder holder, int position) {
		ViewHolder mHolder = (ViewHolder)holder;

		if (node.isVidelFile()){
			mHolder.typeImage.setImageResource(R.mipmap.icon_file_manager_video);
			mHolder.mExpandImage.setVisibility(View.GONE);
		}else {
			mHolder.typeImage.setImageResource(R.mipmap.icon_file_manager_folder);
			mHolder.mExpandImage.setVisibility(View.VISIBLE);
		}

		mHolder.mExpandImage.setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View v) {

				expandOrCollapse(position);
			//	setChecked(node, node.isExpand());

				onNodeDataListener.onNodeDataListener(node);
				notifyDataSetChanged();
			}
		});
		holder.itemView.setSelected(node.isSelelct());

		if (node.isExpand()){
			mHolder.mExpandImage.setBackgroundResource(R.mipmap.icon_file_folder_expand);
		}else {
			mHolder.mExpandImage.setBackgroundResource(R.mipmap.icon_file_folder_unexpand);
		}
		mHolder.tvName.setText(node.getName());

		//增加一个item的点击事件,修改item的状态
		holder.itemView.setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View v) {
				for (int i = 0 ; i < mNodes.size(); i++){
					if (node.getId().equals(mNodes.get(i).getId())){
					
					}else {

						mNodes.get(i).setSelelct(false);
						Log.d("lucky", "onClick: " + mNodes.get(i).isSelelct());
					}
				}
				Log.d("lucky", "onClick: " + node.isSelelct());
				onItemDataListener.onItemDataListener(node);

				notifyDataSetChanged();
			}
		});
	}

	@NonNull
	@Override
	public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
		View view = mInflater.inflate(R.layout.file_move_folder_path_item, parent, false);
		RecyclerView.ViewHolder holder = new  ViewHolder(view);
		view.setTag(holder);
		return holder;
	}



	class ViewHolder extends RecyclerView.ViewHolder{
		TextView tvName;
		ImageView mExpandImage;

		ImageView typeImage;

		public ViewHolder(@NonNull View itemView) {
			super(itemView);
			tvName = itemView.findViewById(R.id.folder_name);
			mExpandImage = itemView.findViewById(R.id.expand_change_image);
			typeImage = itemView.findViewById(R.id.folder_image);
		}
	}

	public void setNodeDataListener(onNodeDataListener setNodeDataListener) {
		this.onNodeDataListener = setNodeDataListener;
	}

	public void setSelelctDataListener(onItemDataListener itemDataListener) {
		this.onItemDataListener = itemDataListener;
	}


	public interface onNodeDataListener {
		void onNodeDataListener(FileNodeBean node);
	}

	public interface onItemDataListener {
		void onItemDataListener(FileNodeBean node);
	}
}

2、TreeListViewAdapter 表示通用结构中的实现:

public abstract class TreeListViewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {


	private static final String TAG =  " TreeListViewAdapter ";
	protected Context mContext;
	/**
	 * 存储所有可见的Node
	 */
	protected List<FileNodeBean> mNodes = new ArrayList<>();
	protected LayoutInflater mInflater;

	/**
	 * 存储所有的Node
	 */
	protected List<FileNodeBean> mAllNodes = new ArrayList<>();

	public TreeListViewAdapter(RecyclerView mTree, Context context){
		mContext = context;
		mInflater = LayoutInflater.from(context);

	}

	//上一个状态的所有node
	private List<FileNodeBean> beforeDatas = new ArrayList<>();
	public void updateData(List<FileNodeBean> datas, int defaultExpandLevel) {


		/**
		 * 对所有的Node进行排序
		 */
		mAllNodes = TreeHelper.getSortedNodes(datas, defaultExpandLevel);
		/**
		 * 过滤出可见的Node
		 */
		mNodes = TreeHelper.filterVisibleNode(mAllNodes);

		Log.d("TAG", "updateData: " + mNodes.size() + "  " + mAllNodes.size());


	}

	/**
	 * 相应ListView的点击事件 展开或关闭某节点
	 */
	public void expandOrCollapse(int position) {
		FileNodeBean n = mNodes.get(position);

		if (n != null) {// 排除传入参数错误异常
			//item不为叶子节点
			if (!n.isLeaf()) {
				//n.setExpand(!n.isExpand());
				mNodes = TreeHelper.filterVisibleNode(mAllNodes);
				notifyDataSetChanged();// 刷新视图
			}
		}

	}



	@Override
	public int getItemCount() {
		return mNodes.size();
	}



	@Override
	public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {

		FileNodeBean nodeBean = mNodes.get(position);
		holder.itemView.setPadding((nodeBean.getLevel()+1) * 40, 3, 3, 3);
		getBindViewHolder(nodeBean, holder, position);


	}

	protected abstract void getBindViewHolder(FileNodeBean node, RecyclerView.ViewHolder holder, int position);

	@Override
	public long getItemId(int position) {
		return position;
	}


}

3、MineTypeUtil 用于判断文件的类型

public class MineTypeUtil {
	private static final char SEPARATOR = '/';
	private static final char EXTENSION_SEPARATOR = '.';
	public static final String GENERIC_MIME_TYPE = "application/octet-stream";
	private static final Map<String, String> sExtensionToMimeTypeMap =
			MapBuilder.<String, String>newHashMap()
					// Compatibility (starting from L), in the order they appear in Android source.
					.put("mp3", "application/mp3")
					.put("wav", "application/wav")
					.put("gsm", "application/gsm")
					.put("text", "application/text")
					.buildUnmodifiable();
	private static final String TAG = "MineTypeUtil";

	public static String getMimeType(@NonNull String path) {
		String extension = getExtension(path);
		extension = extension.toLowerCase(Locale.US);
		String mimeType = sExtensionToMimeTypeMap.get(extension);
		if (!TextUtils.isEmpty(mimeType)) {
			return mimeType;
		}
		mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
		if (!TextUtils.isEmpty(mimeType)) {
			return mimeType;
		}
		return GENERIC_MIME_TYPE;
	}

	public static String getExtension(@NonNull String path) {
		int index = indexOfExtensionSeparator(path);
		if (path == null){
			index = -1;
		}
		return index != -1 ? path.substring(index + 1) : "";
	}

	public static int indexOfExtensionSeparator(@NonNull String path) {
		int lastSeparatorIndex = indexOfLastSeparator(path);
		if (path == null){
			return -1;
		}
		int lastExtensionSeparatorIndex = path.lastIndexOf(EXTENSION_SEPARATOR);
		return lastSeparatorIndex > lastExtensionSeparatorIndex ? -1 : lastExtensionSeparatorIndex;
	}

	public static int indexOfLastSeparator(@NonNull String path) {
		if (path == null){
			return -1;
		}
		return path.lastIndexOf(SEPARATOR);
	}

	public static boolean isShowFile(File file) {
		String path = file.getPath();
		boolean isVideo = false;
		isVideo = isVideoFile(path);

		if (!file.isHidden() && (isVideo || file.isDirectory())) {

			return true;
		}
		return false;
	}
	public static boolean isVideoFile(String path){
		if (MineTypeUtil.getMimeType(path).equals("application/mp3") || MineTypeUtil.getMimeType(path).equals("application/wav")
				|| MineTypeUtil.getMimeType(path).equals("application/gsm")) {
			return true;
		}
		return false;
	}
}

用于辅助处理MineTypeUtil 的文件信息

public class MapBuilder<K, V, M extends Map<K, V>> {

	@NonNull
	private M mMap;

	private MapBuilder(@NonNull M map) {
		mMap = map;
	}

	@NonNull
	public static <K, V> MapBuilder<K, V, HashMap<K, V>> newHashMap() {
		return new MapBuilder<>(new HashMap<>());
	}

	@NonNull
	public static <K, V, M extends Map<K, V>> MapBuilder<K, V, M> buildUpon(@NonNull M map) {
		return new MapBuilder<>(map);
	}

	@NonNull
	public M build() {
		M map = mMap;
		mMap = null;
		return map;
	}

	@NonNull
	public Map<K, V> buildUnmodifiable() {
		Map<K, V> map = Collections.unmodifiableMap(mMap);
		mMap = null;
		return map;
	}


	@NonNull
	public MapBuilder<K, V, M> put(@Nullable K key, @Nullable V value) {
		mMap.put(key, value);
		return this;
	}

	@NonNull
	public MapBuilder<K, V, M> remove(@Nullable K key) {
		mMap.remove(key);
		return this;
	}

	@NonNull
	public MapBuilder<K, V, M> putAll(@NonNull Map<? extends K, ? extends V> m) {
		mMap.putAll(m);
		return this;
	}

	@NonNull
	public MapBuilder<K, V, M> clear() {
		mMap.clear();
		return this;
	}

	@NonNull
	@RequiresApi(Build.VERSION_CODES.N)
	public MapBuilder<K, V, M> replaceAll(
			@NonNull BiFunction<? super K, ? super V, ? extends V> function) {
		mMap.replaceAll(function);
		return this;
	}

	@NonNull
	@RequiresApi(Build.VERSION_CODES.N)
	public MapBuilder<K, V, M> putIfAbsent(@Nullable K key, @Nullable V value) {
		mMap.putIfAbsent(key, value);
		return this;
	}

	@NonNull
	@RequiresApi(Build.VERSION_CODES.N)
	public MapBuilder<K, V, M> remove(@Nullable K key, @Nullable V value) {
		mMap.remove(key, value);
		return this;
	}

	@NonNull
	@RequiresApi(Build.VERSION_CODES.N)
	public MapBuilder<K, V, M> replace(@Nullable K key, @Nullable V oldValue,
									   @Nullable V newValue) {
		mMap.replace(key, oldValue, newValue);
		return this;
	}

	@NonNull
	@RequiresApi(Build.VERSION_CODES.N)
	public MapBuilder<K, V, M> replace(@Nullable K key, @Nullable V value) {
		mMap.replace(key, value);
		return this;
	}

	@NonNull
	@RequiresApi(Build.VERSION_CODES.N)
	public MapBuilder<K, V, M> merge(
			@Nullable K key, @NonNull V value,
			@NonNull BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
		mMap.merge(key, value, remappingFunction);
		return this;
	}
}

4、构建树状图的关键处理

public class TreeHelper {
	/**
	 * 传入node  返回排序后的Node
	 */
	public static List<FileNodeBean> getSortedNodes(List<FileNodeBean> datas,
											int defaultExpandLevel) {
		List<FileNodeBean> result = new ArrayList<FileNodeBean>();
		// 设置Node间父子关系
		List<FileNodeBean> nodes = convetData2Node(datas);
		// 拿到根节点
		List<FileNodeBean> rootNodes = getRootNodes(nodes);
		// 排序以及设置Node间关系
		for (FileNodeBean node : rootNodes) {
			addNode(result, node, defaultExpandLevel);
		}
		return result;
	}

	/**
	 * 过滤出所有可见的Node
	 */
	public static List<FileNodeBean> filterVisibleNode(List<FileNodeBean> nodes) {
		List<FileNodeBean> result = new ArrayList<FileNodeBean>();

		for (FileNodeBean node : nodes) {
			// 如果为根节点,或者上层目录为展开状态
			if (node.isRoot() || node.isParentExpand()) {
				result.add(node);
			}
		}
		return result;
	}

	/**
	 * 设置Node间,父子关系;让每两个节点都比较一次,即可设置其中的关系
	 */
	private static List<FileNodeBean> convetData2Node(List<FileNodeBean> nodes) {

		for (int i = 0; i < nodes.size(); i++) {
			FileNodeBean n = nodes.get(i);
			for (int j = i + 1; j < nodes.size(); j++) {
				FileNodeBean m = nodes.get(j);
				//id的类型为string类型
				if (m.getpId() instanceof String) {
					if (m.getpId().equals(n.getId())) {
						n.getChildren().add(m);
						m.setParent(n);
					} else if (m.getId().equals(n.getpId())) {
						m.getChildren().add(n);
						n.setParent(m);
					}
				} else {
					//id类型为int型
					if (m.getpId() == n.getId()) {
						n.getChildren().add(m);
						m.setParent(n);
					} else if (m.getId() == n.getpId()) {
						m.getChildren().add(n);
						n.setParent(m);
					}
				}
			}
		}
		return nodes;
	}

	// 拿到根节点
	private static List<FileNodeBean> getRootNodes(List<FileNodeBean> nodes) {
		List<FileNodeBean> root = new ArrayList<FileNodeBean>();
		for (FileNodeBean node : nodes) {
			if (node.getLevel() == 0 || node.isRoot()){
				root.add(node);
			}
//			if (node.isRoot())
//				root.add(node);
		}
		return root;
	}

	/**
	 * 递归,把一个节点上的所有的内容都挂上去
	 * @param nodes node为挂载后的数据
	 * @param node 需要增加的子项
	 * @param currentLevel
	 * @param 
	 * @param 
	 */
	private static <T, B> void addNode(List<FileNodeBean> nodes, FileNodeBean<T, B> node,
									    int currentLevel) {
		//将所有node加入到字符中


		node.setLevel(currentLevel);
		nodes.add(node);

		if (node.isLeaf()){
			return;
		}
		for (int i = 0 ; i < node.getChildren().size(); i++){
			addNode(nodes, node.getChildren().get(i), currentLevel + 1);
		}
		Log.d("Treehelper", "addNode: " + nodes.size());
	}
}

辅助处理事件

public class FileNodeBean<T, B> {


		/**
		 * ****************** 分级树状等字段
		 */
		//传入的实体对象
		public B bean;
		//父级id子级pid
		private T id;
		//根节点pId为0
		private T pId;
		//节点名称
		private String name;
		//当前的级别
		private int level;
		//是否展开
		private boolean isExpand = false;
		//子节点数据 ,套用当前node
		private List<FileNodeBean> children = new ArrayList<>();
		//父节点
		private FileNodeBean parent;
		//是否被checked选中
		private boolean isChecked;

		private boolean isSelelct;

		private String path;

		private boolean isVidelFile;

		public FileNodeBean() {
		}

		public FileNodeBean(T id, T pId, String name) {
			super();
			this.id = id;
			this.pId = pId;
			this.name = name;
		}

		public FileNodeBean(T id, T pId, String name, B bean) {
			super();
			this.id = id;
			this.pId = pId;
			this.name = name;
			this.bean = bean;
		}

	public boolean isVidelFile() {
		return isVidelFile;
	}

	public void setVidelFile(boolean videlFile) {
		isVidelFile = videlFile;
	}

	public String getPath() {
		return path;
	}

	public void setPath(String path) {
		this.path = path;
	}

	public boolean isSelelct() {
		return isSelelct;
	}

	public void setSelelct(boolean selelct) {
		isSelelct = selelct;
	}

	public boolean isChecked() {
			return isChecked;
		}

		public void setChecked(boolean isChecked) {
			this.isChecked = isChecked;
		}

		public T getId() {
			return id;
		}

		public void setId(T id) {
			this.id = id;
		}

		public T getpId() {
			return pId;
		}

		public void setpId(T pId) {
			this.pId = pId;
		}

		public String getName() {
			return name;
		}

		public void setName(String name) {
			this.name = name;
		}

		public void setLevel(int level) {
			this.level = level;
		}

		public boolean isExpand() {
			return isExpand;
		}

		public List<FileNodeBean> getChildren() {
			return children;
		}

		public void setChildren(List<FileNodeBean> children) {
			this.children = children;
		}

		public FileNodeBean getParent() {
			return parent;
		}

		public void setParent(FileNodeBean parent) {
			this.parent = parent;
			if (parent != null){
				this.pId = (T) parent.getId();
			}

		}

		/**
		 * 是否为跟节点
		 */
		public boolean isRoot() {
			return parent == null;
		}

		/**
		 * 判断父节点是否展开
		 */
		public boolean isParentExpand() {
			if (parent == null)
				return false;
			return parent.isExpand();
		}

		/**
		 * 是否是叶子界点
		 */
		public boolean isLeaf() {
			return children.size() == 0;
		}

		/**
		 * 获取当前所在层级,
		 * 设置层级间隔
		 */
		public int getLevel() {
			return parent == null ? 0 : parent.getLevel() + 1;
		}

		/**
		 * 设置展开
		 */
		public void setExpand(boolean isExpand) {
			this.isExpand = isExpand;
			if (!isExpand) {
				for (FileNodeBean node : children) {
					node.setExpand(isExpand);
				}
			}
		}

}

你可能感兴趣的:(java,android,开发语言)