在本篇文章中主要是使用ListView实现像电脑版的文件树的形式,以目录“/”作为根目录,依次显示次级目录,当单击ListView中的某一项的时候,如果是文件夹就显示其下包含的文件信息,如果是文件或者文档就显示提示信息。效果形式这里就不为大家显示了。如果感兴趣可以参考一下。
下面从代码的角度为读客介绍程序的编写思路:
package com.example.fileoperation; //这个是自己的工程建立的包,可以根据自己的工程进行更改
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;
/*
类 Node,实现的Comparable<Node>接口,主要是问了通过比较和排序的方法,将文件夹和文档进行分类显示,实现该接口必须实现抽象函数:comparedTo(Node node),而在本例程中没有用该方法进行排序,而是使用的Collections.sort();
类Node的作用主要是:作为文件树的节点,包含很多信息——文件名、文件路径、父节点信息、子节点列表、是否有子节点、是否为文件夹、是否为文档、是否展开、本节点的级数、id号等信息。还有一些关于成员变量的一些方法。
*/
public class Node implements Comparable<Node> {
private String str_Name; // 表示本节点名;
private String str_Path; //表示本节点的路径;
private String str_ParentName; //表示父节点名;
private String str_ParentPath; //表示父节点路径;
boolean bool_haveSub;//表示是否拥有子节点;
List<String> subNodeAbsolutePath;
public boolean isFile; //表示是否问文件
public boolean isDoc; //表示是否问文档;
public boolean isOpen=false;//表示是否被展开;
public int level; //表示本节点所处的级数,根节点(“/”)的级数level=0;
public int id;
public int parentID=0;
private List<Node> subNodeList; //表示本节点的子节点的列表,主要是在该节点被点击之后使用,用于向显示的ListView中添加需要显示的子节点的信息。
/*
* @param Path:当表示文件xx的时候,输入格式为:/xx/xxx/xxx/xxx
* */
public Node (String Path)
{
str_Path = Path;
subNodeList = new LinkedList<Node>(); //初始化子节点信息列表
subNodeAbsolutePath = new LinkedList<>(); //初始化子节点的文件名列表;
extractInfo(); //用于根据传入的参数:Path提取需要信息。包括一些初始化的信息;
}
//该方法用于返回子节点的个数
public int getSubNodeSum()
{
if(subNodeList!=null) {
return subNodeList.size();
}
else
{
return 0;
}
}
//该方法用于设置子节点:主要是根据子节点中的属性将子节点中的文件和文档进行归类排序;
public void setSubNodeList(List<Node> subNodeList)
{
/*
该方法就是前面给大家说的归类排序。通过比较节点之间的isFile属性,将文档归一类,将文件夹归一类;
*/
Collections.sort(subNodeList, new Comparator<Node>() {
@Override
public int compare(Node node, Node node2) {
int nodeInt_1 = node.isFile?1:0;
int nodeInt_2 = node2.isFile?1:0;
if(nodeInt_1>nodeInt_2)
return 1;
if (nodeInt_1==nodeInt_2)
return 0;
if (nodeInt_1<nodeInt_2)
return -1;
return 0;
}
});
this.subNodeList = subNodeList;
}
/*
该方法用于获取子节点列表
*/
public List<Node> getSubNodeList()
{
return this.subNodeList;
}
/*
该方法用于根据输入的参数:Path,提取出相应的信息,包括节点名、父节点、节点的子节点信息等。
*/
private void extractInfo()
{
//如果传入的参数Path不为空,则进行相应的操作。
if(!str_Path.isEmpty())
{
String[] info = str_Path.split("/"); //表示以“/”分开路径字符串。
if(info.length>1) { //如果分开的信息的数组中的长度>1,则表示该路径不是第一节点,至少为第二节点。
level = info.length;
str_Name = info[info.length - 1];
str_ParentName = info[info.length - 2];
for (int i = 0; i < info.length - 1; i++) {
str_ParentPath = str_ParentPath + "/" + info[i]; //提取出父节点的路径信息。
}
}
else //表示该Path表示的第一节点的信息或者根节点"/"。
{
level = 1;
str_Name = info[0];
str_ParentName = "/";
str_ParentPath = "/";
}
//判断是文档还是文件夹;
File file = new File(str_Path);
if(file.isDirectory()&&file.listFiles()!=null)//判断是否为文件夹
{
int listFilesLength = file.listFiles().length;
if(listFilesLength>0) { //如果子节点的长度大于0,表示含有子节点,反之,表示没有子节点。
bool_haveSub = true; //标志有子节点
isFile = true; //标志是文件夹
isDoc = false; //标志不是文档
for (int i = 0;i<listFilesLength;i++)
{
//System.out.println("输出subNodeAbsolutePath中元素的个数:"+listFilesLength);
subNodeAbsolutePath.add(file.listFiles()[i].getAbsolutePath());//返回子节点列表,向子节点列表中添加子节点信息
}
}
else //表示为文档
{
bool_haveSub = false;
isFile = true;
isDoc = false;
subNodeAbsolutePath = null;
}
}
else
{
bool_haveSub = false;
isFile = false;
isDoc = true;
}
}
}
//获得str_Name;
public String getStr_Name()
{
return str_Name;
}
//获得str_Parent;
public String getStr_ParentName()
{
return str_ParentName;
}
//表示是否拥有子节点
public boolean getbool_haveSub()
{
return bool_haveSub;
}
//获取节点的路径
public String getStr_Path()
{
return this.str_Path;
}
//获取父节点路径
public String getStr_ParentPath()
{
return str_ParentPath;
}
@Override
public int compareTo(Node node) {
int i = (String.valueOf(isFile)).compareTo(String.valueOf(node.isFile));
return i;
}
}
上面介绍的是节点类的申明,其中已经备注和解释了相关信息。
下面是适配器,主要是用于向ListView控件中添加指定的信息。该类继承自BaseAdapter适配器。
package com.example.fileoperation;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import java.io.File;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
/*
该类为适配器类,继承自BaseAdapter。用于将Node类对象的信息添加到ListView中去。
*/
public class PathListAdapter extends BaseAdapter {
private Context context;
private List<Node> displayTree;
public static final int indentValue = 60;
ListView listView;
//构造函数。
/*
@context:表示调用的Activity;
@displayTree:表示需要显示在ListView控件中的节点;
@listView:表示使用到的控件:ListView;
*/
public PathListAdapter(final Context context, final List<Node> displayTree, ListView listView)
{
this.context = context;
this.displayTree = displayTree;
this.listView = listView;
/*
设置ListView控件的事件,点击控件中的指定项item触发。用于向显示的Node列表中——displayTree添加Node信息。
*/
this.listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
List<Node> childrenNodeList = new LinkedList<>();
Node treenode = displayTree.get(i);
if(!treenode.isOpen) //当节点没有被点击的时候,即未被展开显示的时候。
{
if (treenode.isDoc) { //如果是文件夹,则可以用于展开
Toast.makeText(context, "该选项是文档,无法再点击。", Toast.LENGTH_SHORT).show();
treenode.isOpen = false;
} else {
if (treenode.bool_haveSub) {
treenode.isOpen = true;
for (String each : treenode.subNodeAbsolutePath) {
Node node = new Node(each);
childrenNodeList.add(node);
}
treenode.setSubNodeList(childrenNodeList);
displayTree.addAll(i + 1, childrenNodeList);
} else {
treenode.isOpen = false;
Toast.makeText(context, "该选项是文件,但是无子文件,无法再往下点击了。", Toast.LENGTH_SHORT).show();
}
}
}
else
{
collapse(treenode,i);//用于迭代关闭其下的子节点和后辈节点,即可以关闭多级节点的子节点。
}
notifyDataSetChanged();//系统自带函数,用于更新适配器当数据源改变的时候。
}
});
}
public void collapse(Node treenode,int position)
{
if(treenode.isOpen)
{
treenode.isOpen = false;
List<Node> childrenNode = treenode.getSubNodeList();
for(int i=0;i<treenode.getSubNodeSum();i++)
{
if(childrenNode.get(i).isOpen)
{
collapse(childrenNode.get(i),position+1);//迭代关闭子节点
}
displayTree.remove(position+1);
}
}
}
@Override
public int getCount() {
return displayTree.size();//返回节点列表中节点的个数
}
@Override
public Object getItem(int i) {//返回节点列表中的某一项
return displayTree.get(i);
}
@Override
public long getItemId(int i) {//返回节点所属的列表中的索引
displayTree.get(i).id=i;
return i;
}
@Override
public View getView(int i, View view, ViewGroup viewGroup) {//显示的节点对应的ListView中的item的控件的设置。
ViewHolder viewHolder = null;//新建类,用于包含控件view中指定的控件。
Node node = displayTree.get(i);
if(view == null)
{
view = LayoutInflater.from(context).inflate(R.layout.fileitem_layout,viewGroup,false);//用于自定义ListView中的item显示的格式或者样式。
viewHolder = new ViewHolder(view);
view.setTag(viewHolder);//用于将ViewHolder中的控件与view关联起来。
}
else
{
viewHolder = (ViewHolder)view.getTag();
}
viewHolder.tv_showPathName.setText(node.getStr_Name());
if(node.isDoc)
{
viewHolder.iv_showFileOrDoc.setBackgroundResource(R.mipmap.doc);
}
if(node.isFile)
{
viewHolder.iv_showFileOrDoc.setBackgroundResource(R.mipmap.file);
}
if(node.isOpen)
{
viewHolder.iv_showOpenOrClose.setBackgroundResource(R.mipmap.downofopen);
}
else
{
viewHolder.iv_showOpenOrClose.setBackgroundResource(R.mipmap.top);
}
view.setPadding(indentValue*(node.level-2),0,0,0);
return view;
}
}
class ViewHolder
{
TextView tv_showPathName;
ImageView iv_showOpenOrClose;
ImageView iv_showFileOrDoc;
public ViewHolder(View view)
{
tv_showPathName = (TextView)view.findViewById(R.id.tv_showPathName);
iv_showFileOrDoc = (ImageView)view.findViewById(R.id.iv_showFileOrDoc);
iv_showOpenOrClose = (ImageView)view.findViewById(R.id.iv_showOpenOrClose);
}
}
下面就是主页面的MainActivity中的代码部分。
package com.example.fileoperation;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ListView listView = (ListView)findViewById(R.id.lv_list);
List<Node> displayTreeNode = new LinkedList<>();
String rootPath = "/";
File file = new File(rootPath);
for(File each:file.listFiles())
{
displayTreeNode.add(new Node(each.getAbsolutePath()));
}
//用于对显示的列表中项进行归类。这是对第一级节点信息进行处理,在Node类中使用该方法主要是对于节点的子节点的信息进行归类处理。两者不一样,但是功能一样。
Collections.sort(displayTreeNode, new Comparator<Node>() {
@Override
public int compare(Node node, Node node2) {
int nodeInt_1 = node.isFile?1:0;
int nodeInt_2 = node2.isFile?1:0;
if(nodeInt_1>nodeInt_2)
return 1;
if (nodeInt_1==nodeInt_2)
return 0;
if (nodeInt_1<nodeInt_2)
return -1;
return 0;
}
});
PathListAdapter pathListAdapter = new PathListAdapter(getApplicationContext(),displayTreeNode,listView);
listView.setAdapter(pathListAdapter);
}
}
如下是两个XML文件的信息,其中第一个是主界面的Layout;第二个是适配器中采用的对于item项的Layout。
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<EditText
android:layout_width="300dp"
android:layout_height="60dp"
android:id="@+id/ev_search"
android:layout_centerHorizontal="true"
android:textColor="#00000000"
android:textSize="40sp"
android:text="333333"/>
<ListView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/lv_list"
android:layout_below="@+id/ev_search"
android:footerDividersEnabled="false" />
</RelativeLayout>
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="80dp">
<ImageView
android:layout_width="60dp"
android:layout_height="match_parent"
android:id="@+id/iv_showOpenOrClose"
android:layout_marginStart="10dp"
android:layout_centerVertical="true"/>
<ImageView
android:layout_height="match_parent"
android:layout_width="60dp"
android:id="@+id/iv_showFileOrDoc"
android:layout_toEndOf="@+id/iv_showOpenOrClose"
android:layout_marginStart="20dp"
android:layout_centerVertical="true"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/tv_showPathName"
android:layout_toEndOf="@+id/iv_showFileOrDoc"
android:layout_centerVertical="true"
android:textColor="#00BCD4"
android:textSize="20sp"/>
</RelativeLayout>
以上就是全部源码信息。