溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

怎么在Android應(yīng)用中添加一個文件管理功能

發(fā)布時間:2020-12-02 15:47:27 來源:億速云 閱讀:172 作者:Leah 欄目:移動開發(fā)

這篇文章給大家介紹怎么在Android應(yīng)用中添加一個文件管理功能,內(nèi)容非常詳細,感興趣的小伙伴們可以參考借鑒,希望對大家能有所幫助。

思路:

獲取存儲器接口
遍歷當(dāng)前目錄
利用ListView顯示文件文件夾

先是布局

<&#63;xml version="1.0" encoding="utf-8"&#63;>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:orientation="vertical">

 <HorizontalScrollView
 android:layout_width="match_parent"
 android:layout_height="wrap_content"
 android:scrollbars="none">

 <LinearLayout
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:orientation="horizontal"
 android:id="@+id/lyPath">

 <TextView
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:textAppearance="&#63;android:textAppearance"
 android:text="@string/txt_path_now"/>

 <TextView
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:textAppearance="&#63;android:textAppearance"
 android:text="mnt/sdcard"
 android:id="@+id/txtPath"/>

 </LinearLayout>
 </HorizontalScrollView>

 <View
 android:layout_width="match_parent"
 android:layout_height="1dp"
 android:background="@android:color/darker_gray"/>

 <LinearLayout
 android:layout_width="match_parent"
 android:layout_height="0dp"
 android:layout_weight="1">

 <ListView
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:id="@+id/folderList"/>

 </LinearLayout>
</LinearLayout>

用于加載文件的Item布局

list_file_style.xml

<&#63;xml version="1.0" encoding="utf-8"&#63;>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 xmlns:tools="http://schemas.android.com/tools"
 android:layout_width="match_parent"
 android:layout_height="wrap_content"
 android:orientation="vertical">

 <LinearLayout
 android:layout_width="match_parent"
 android:layout_height="wrap_content"
 android:orientation="horizontal"
 android:scaleY="0.9">

 <CheckBox
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:focusable = "false"
 android:focusableInTouchMode="false"
 android:id="@+id/cbSelect"/>

 <ImageView
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:id="@+id/img"
 android:src="@mipmap/other"
 tools:ignore="ContentDescription" />

 <LinearLayout
 android:layout_width="match_parent"
 android:layout_height="wrap_content"
 android:orientation="vertical">

 <TextView
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:textAppearance="&#63;android:textAppearance"
 android:id="@+id/name"
 android:text="setting"/>

 <LinearLayout
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:orientation="horizontal">

 <TextView
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:id="@+id/time"
  android:text="2017-3-30 21:40:02"/>

 <View
  android:layout_width="20dp"
  android:layout_height="match_parent"/>

 <TextView
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:id="@+id/size"
  android:text="1.2Mb"/>

 </LinearLayout>

 </LinearLayout>

 </LinearLayout>

 <View
 android:layout_width="match_parent"
 android:layout_height="1dp"
 android:background="@android:color/darker_gray"/>

</LinearLayout>

自定義類

為了更好的將數(shù)據(jù)綁定到ListView上我選擇自定義BaseAdapter類

package czhy.grey.sun.exam.bin.adapter_;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.CheckBox;

import java.io.File;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.HashMap;

import czhy.grey.sun.exam.R;
import czhy.grey.sun.exam.bin.holder_.FileHolder;

public class FileAdapter extends BaseAdapter {
 private ArrayList<File> list;
 private LayoutInflater inflater;
 private boolean isRoot;
 // 用來控制CheckBox的選中狀況
 private HashMap<Integer, Boolean> isSelected;
 private int selectNum;

 public FileAdapter(Context context, ArrayList<File> list,boolean isRoot) {
 this.list = list;
 this.isRoot = isRoot;
 inflater = LayoutInflater.from(context);
 isSelected = new HashMap<>();
 // 初始化數(shù)據(jù)
 initDate();
 }

 @Override
 public int getCount() {
 return list.size();
 }

 @Override
 public File getItem(int position) {
 return list.get(position);
 }

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

 @Override
 public View getView(int position, View convertView, ViewGroup parent) {
 FileHolder holder;
 File file = getItem(position);

 if (convertView == null) {
 convertView = inflater.inflate(R.layout.list_file_style, parent, false);
 holder = new FileHolder(convertView);
 convertView.setTag(holder);
 } else {
 holder = (FileHolder) convertView.getTag();
 }
 // TODO: 2017/4/1 根目錄UI優(yōu)化
 if (!isRoot && position == 0) {
 holder.setName("返回上一層", file.isDirectory(),isSelectedFor(position));
 holder.setId(position,isSelectedFor(position),new View.OnClickListener() {
 @Override
 public void onClick(View v) {
  int position = (int) v.getTag();
  boolean b = !isSelected.get(position);

  isSelected.put(position, b);
  ((CheckBox) v).setChecked(b);
  //全選或全取消操作
  for(int i=0;i< getCount();i++){
  setChecked(i,b);
  }
  selectNum = b&#63;getCount():0;

  notifyDataSetChanged();
 }
 });
 holder.setTime("全選");
 holder.setSize("已選擇"+selectNum+"項");
 } else {
 holder.setName(file.getName(), file.isDirectory(),isSelectedFor(position));
 holder.setId(position,isSelectedFor(position),new View.OnClickListener() {
 @Override
 public void onClick(View v) {
  int position = (int) v.getTag();
  boolean b = !isSelectedFor(position);
  isSelected.put(position, b);
  ((CheckBox) v).setChecked(b);
  //是否已經(jīng)全選
  if(isSelectedAll()) {
  isSelected.put(0, true);
  selectNum = getCount();
  }else {
  isSelected.put(0, false);
  selectNum = b&#63;selectNum+1:selectNum-1;
  }

  notifyDataSetChanged();
 }
 });
 holder.setTime(new SimpleDateFormat("yyyy/mm/hh/dd hh:mm:ss").format(file.lastModified()));

 if (file.isFile())
 holder.setSize(fileLength(file.length()));
 else {
 holder.setSize("");
 }
 }

 return convertView;
 }

 public boolean isSelectedFor(int id) {
 return isSelected.get(id);
 }

 public int getSelectNum() {
 return selectNum;
 }

 //私有函數(shù)
 /** 初始化isSelected的數(shù)據(jù)
 */
 private void initDate() {
 selectNum = 0;
 for (int i = 0; i < list.size(); i++) {
 isSelected.put(i, false);
 }
 }

 private void setChecked(int id,boolean isChecked) {
 isSelected.put(id,isChecked);
 }

 private String fileLength(long length) {
 String size;

 if (length > 1024 * 1024)
 size = new DecimalFormat("#.00").format(length / (1024.0 * 1024.0)) + "MB";
 else if (length > 1024)
 size = new DecimalFormat("#.00").format(length / 1024.0) + "KB";
 else
 size = length + "B";

 return size;
 }

 private boolean isSelectedAll(){
 for(int i=1;i< getCount();i++){
 if(!isSelectedFor(i))
 return false;
 }

 return true;
 }

}

以及用于布局導(dǎo)入的Holder

package czhy.grey.sun.exam.bin.holder_;


import android.view.View;
import android.widget.CheckBox;
import android.widget.ImageView;
import android.widget.TextView;

import czhy.grey.sun.exam.R;

public class FileHolder{

 private CheckBox cbSelect;
 private TextView name;
 private TextView time;
 private TextView size;
 private ImageView img;

 public FileHolder(View convertView) {
 cbSelect = (CheckBox)convertView.findViewById(R.id.cbSelect);
 name = (TextView)convertView.findViewById(R.id.name);
 time = (TextView)convertView.findViewById(R.id.time);
 img = (ImageView)convertView.findViewById(R.id.img);
 size = (TextView)convertView.findViewById(R.id.size);
 }

 public void setTime(String time) {
 this.time.setText(time);
 }

 public void setId(int id,boolean isSelected, View.OnClickListener listener) {
 cbSelect.setTag(id);
 cbSelect.setChecked(isSelected);
 cbSelect.setOnClickListener(listener);
 }

 public void setName(String name,boolean isDirectory,boolean isChecked) {
 this.name.setText(name);
 cbSelect.setChecked(isChecked);
 if (isDirectory) {
 if(name.equalsIgnoreCase("返回上一層")){
 img.setImageResource(R.mipmap.back);
 }else
 img.setImageResource(R.mipmap.folder);
 }else {
 String type = name.substring(name.lastIndexOf(".")+1,name.length());

 if ((type.equalsIgnoreCase("doc") || type.equalsIgnoreCase("docx") || type.equalsIgnoreCase("txt"))) {
 img.setImageResource(R.mipmap.doc_text);
 } else {
 img.setImageResource(R.mipmap.other);
 }
 }
 }

 public void setSize(String size) {
 this.size.setText(size);
 }
}

控制文件

全局變量

 private ListView folderList;
 private TextView txtPath;
 private FileAdapter fileAdapter;
 private ArrayList<File> rootFileList;
 private boolean isRootNow;

首先是獲取存儲器列表

 private void getRootFile(){
 rootFileList = new ArrayList<>();
 StorageManager storageManager = (StorageManager) this.getSystemService(STORAGE_SERVICE);
 try {
 //內(nèi)部存儲器
 File inside = null;
 //可移除存儲器集合
 ArrayList<File> outside = new ArrayList<>();

 //獲取存儲器接口 API-24以下不支持StorageVolume接口
 //API-24開始可直接 List<StorageVolume> svList = storageManager.getStorageVolumes();
 Method getVolumeList = StorageManager.class.getMethod("getVolumeList");
 getVolumeList.setAccessible(true);
 //獲取存儲器列表
 Object[] invokes = (Object[]) getVolumeList.invoke(storageManager);
 if (invokes != null) {
 for (Object obj:invokes) {
  //獲取存儲器地址接口
  Method getPath = obj.getClass().getMethod("getPath");
  //獲取存儲器地址
  String path = (String) getPath.invoke(obj);
  File file = new File(path);
  if (file.canWrite()) {
  //獲取存儲器是否可移除接口
  Method isRemovable = obj.getClass().getMethod("isRemovable");
  //存儲器是否可移除
  if((isRemovable.invoke(obj)).equals(true)){
  outside.add(file);
  }else {
  inside = file;
  }
  }
 }
 //按0-內(nèi)部存儲器 >0外部存儲器 順序 添加到根目錄列表
 rootFileList.add(inside);
 rootFileList.addAll(outside);
 }
 } catch (NoSuchMethodException |
 IllegalArgumentException |
 IllegalAccessException |
 InvocationTargetException e1) {
 e1.printStackTrace();
 }
 }

當(dāng)我們點擊一個文件夾時,打開文件夾以及更新UI

 //獲取目錄數(shù)據(jù)
 private void refurbish(File folder) {
 txtPath.setText(folder.getPath());
 ArrayList<File> files = new ArrayList<>();

 files.add(folder.getParentFile());

 File[] folderFile = folder.listFiles();
 if (null != folderFile && folderFile.length > 0) {
 for(File file:folderFile)
 files.add(file);
 }

 //新建集合用做打開文件夾
 ArrayList<File> openedFolder = new ArrayList<>();
 //獲取第一個文件夾 上一級文件夾
 openedFolder.add(files.get(0));
 //移除 上一級文件夾 剩下為當(dāng)前文件夾內(nèi)容
 files.remove(0);
 //排序 文件夾在前,然后按文件名排序
 Collections.sort(files, new Comparator<File>() {
 @Override
 public int compare(File f1, File f2) {
 if (f1.isDirectory()) {
  if (f2.isDirectory()) {
  return f1.getName().compareToIgnoreCase(f2.getName());
  } else {
  return -1;
  }
 } else if (f2.isDirectory()) {
  return 1;
 } else {
  return f1.getName().compareToIgnoreCase(f2.getName());
 }
 }
 });
 //將排序完畢的內(nèi)容添加到目標(biāo)集合 目的:解決第一個文件夾不是上一層地址問題
 openedFolder.addAll(files);
 fileAdapter = new FileAdapter(this, openedFolder,folder.getParent() == null);
 folderList.setAdapter(fileAdapter);
 isRootNow = false;
 } 

程序剛運行時需要加載存儲器列表,而不是某一文件夾,所有需要額外定義一個函數(shù)

 //獲取根目錄數(shù)據(jù)
 private void rootFile() {
 txtPath.setText("/");
 fileAdapter = new FileAdapter(this, rootFileList,true);
 folderList.setAdapter(fileAdapter);
 isRootNow = true;
 }

因為存儲器掛載點的問題,返回上一層時不會返回到存儲器列表也就是/storage目錄
加上有些文件夾是空或者為系統(tǒng)文件的安全性考慮需要對其隱藏,在獲取存儲器列表是已經(jīng)完成了
但,如果直接讓其返回上一層會出現(xiàn)進入不安全的目錄,所以需要對其進行判斷是否是返回根目錄

 public boolean isRootFile(File file) {
 //經(jīng)過兩部不同的手機測試,這兩個目錄是可能的目錄
 //如果不能正確返回可以自行測試
 //測試方法:輸出父目錄,然后在這里添加或修改即可
 return file.getParent().equalsIgnoreCase("/") || file.getParent().equalsIgnoreCase("/storage");
 }

有了以上這些,在控制文件創(chuàng)建是直接調(diào)用相應(yīng)的函數(shù)即可

@Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_import);

 txtPath = (TextView) findViewById(R.id.txtPath);
 folderList = (ListView) findViewById(R.id.folderList);
 folderList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
 @Override
 public void onItemClick(AdapterView<&#63;> parent, View view, int position, long id) {
 File file = fileAdapter.getItem(position);
 //因為為的程序中不需要對文件進行其他操作,所有不做處理
 //有需求的在這里添加else即可
 if (file.isDirectory()) {
  if (isRootNow)
  refurbish(file);
  else if (isRootFile(file))
  rootFile();
  else
  refurbish(file);
 }
 }
 });
 getRootFile();
 rootFile();
 }

弄了這么多基本算是完成了,為了用戶友好,我對返回鍵進行了重載

 /**
 * 監(jiān)聽物理按鍵
 * 需要注意的是這個函數(shù)是有返回值的
 * 返回值可以理解為
 * true表示做了處理,就不提交給處理系統(tǒng)的back按鍵事件
 * false則是提交給系統(tǒng)處理
 */
 @Override
 public boolean onKeyDown(int keyCode, KeyEvent event) {
 if ((keyCode == KeyEvent.KEYCODE_BACK && event.getRepeatCount() == 0)) {

 if (!isRootNow) {
 File file = fileAdapter.getItem(0);
 if (isRootFile(file))
  rootFile();
 else
  refurbish(file);

 return true;
 } else {
 finish();
 }
 }

 return super.onKeyDown(keyCode, event);
 }

關(guān)于怎么在Android應(yīng)用中添加一個文件管理功能就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,可以學(xué)到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。

向AI問一下細節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI