Android编写简易文件管理模块

最近在做一个将word文档导入到SQLite的程序。对于文件选择问题,经过再三考虑决定写一个简易的文件管理模块,用来选择需要导入的文件文件

先看下效果图:

思路:

获取存储器接口
遍历当前目录
利用ListView显示文件文件夹

先是布局

<?xml version="1.0" encoding="utf-8"?>
<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="?android:textAppearance"
 android:text="@string/txt_path_now"/>

 <TextView
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:textAppearance="?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

<?xml version="1.0" encoding="utf-8"?>
<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="?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>

自定义类

为了更好的将数据绑定到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<>();
 // 初始化数据
 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优化
 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?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);
  //是否已经全选
  if(isSelectedAll()) {
  isSelected.put(0, true);
  selectNum = getCount();
  }else {
  isSelected.put(0, false);
  selectNum = b?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;
 }

 //私有函数
 /** 初始化isSelected的数据
 */
 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;
 }

}

以及用于布局导入的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 {
 //内部存储器
 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-内部存储器 >0外部存储器 顺序 添加到根目录列表
 rootFileList.add(inside);
 rootFileList.addAll(outside);
 }
 } catch (NoSuchMethodException |
 IllegalArgumentException |
 IllegalAccessException |
 InvocationTargetException e1) {
 e1.printStackTrace();
 }
 }

当我们点击一个文件夹时,打开文件夹以及更新UI

 //获取目录数据
 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));
 //移除 上一级文件夹 剩下为当前文件夹内容
 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());
 }
 }
 });
 //将排序完毕的内容添加到目标集合 目的:解决第一个文件夹不是上一层地址问题
 openedFolder.addAll(files);
 fileAdapter = new FileAdapter(this, openedFolder,folder.getParent() == null);
 folderList.setAdapter(fileAdapter);
 isRootNow = false;
 }

程序刚运行时需要加载存储器列表,而不是某一文件夹,所有需要额外定义一个函数

 //获取根目录数据
 private void rootFile() {
 txtPath.setText("/");
 fileAdapter = new FileAdapter(this, rootFileList,true);
 folderList.setAdapter(fileAdapter);
 isRootNow = true;
 }

因为存储器挂载点的问题,返回上一层时不会返回到存储器列表也就是/storage目录
加上有些文件夹是空或者为系统文件的安全性考虑需要对其隐藏,在获取存储器列表是已经完成了
但,如果直接让其返回上一层会出现进入不安全的目录,所以需要对其进行判断是否是返回根目录

 public boolean isRootFile(File file) {
 //经过两部不同的手机测试,这两个目录是可能的目录
 //如果不能正确返回可以自行测试
 //测试方法:输出父目录,然后在这里添加或修改即可
 return file.getParent().equalsIgnoreCase("/") || file.getParent().equalsIgnoreCase("/storage");
 }

有了以上这些,在控制文件创建是直接调用相应的函数即可

@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<?> 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();
 }

弄了这么多基本算是完成了,为了用户友好,我对返回键进行了重载

 /**
 * 监听物理按键
 * 需要注意的是这个函数是有返回值的
 * 返回值可以理解为
 * true表示做了处理,就不提交给处理系统的back按键事件
 * false则是提交给系统处理
 */
 @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);
 }

参考文献:

Android获取内外置存储卡的方法

android文件管理器用法详解

如何获取Android设备挂载的所有存储器

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • android简易文件管理器实例(列表式文件目录)

    核心代码: File fatherFile = new File(path);File[] files = fatherFile.listFiles(); 效果图: 实现这种列表式的目录,用直接读取目录下文件方法会比较简单,但是如果要根据文件类型从所有文件中分类,那就用ContentProvider去查询数据库方式会更有效率: 实现代码: FileListActivity.java package com.example.d_readandwritetextfile; import java.i

  • android文件管理器用法详解

    很久没有写东西了,鉴于某某同学文件管理器不会,这里简单介绍一下,同时写一个demon,参考了网上别人写的代码,自己也学习学习,研究研究. 首先所谓文件管理器,看起来就是一个列表,列表里面是文件夹或者文件,首先把布局写出来,我想在最上方的左边显示文件的路径,右边显示该路径下的文件个数,其实还是一个遍历文件,然后用列表显示出来的问题.下面是ListView,用来显示文件列表.下面是运行的效果图: 主界面的布局文件如下: <?xml version="1.0" encoding=&qu

  • Android获取手机文件夹及文件列表的方法

    先看看效果图: package wuwang.tools.utils; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.text.DecimalFormat; import java.util.ArrayList; import java.util.Comparator; import

  • Android编写简易文件管理模块

    最近在做一个将word文档导入到SQLite的程序.对于文件选择问题,经过再三考虑决定写一个简易的文件管理模块,用来选择需要导入的文件文件 先看下效果图: 思路: 获取存储器接口 遍历当前目录 利用ListView显示文件文件夹 先是布局 <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/re

  • Android实现简易计算器小程序

    本文实例为大家分享了Android实现简易计算器小程序的具体代码,供大家参考,具体内容如下 目标效果: 通过编写代码,可以实现整数和小数的加减乘除运算,以及删除和清空的功能. 1.页面中Button使用的是线性布局,最外边一个是父布局,第一行C,DEL,/,*为第一个子布局,第二行7,8,9,-为第二个子布局,第三行4,5,6,+为第三个子布局,第四五行为第四个子布局,第四个子布局中还有两个相当于是孙布局的级别,1,2,3为第一个孙布局,0和.为第二个孙布局,=在两个孙布局之外第四个子布局以内.

  • Android实现简易的音乐播放器

    本文实例为大家分享了Android实现简易的音乐播放器,供大家参考,具体内容如下 功能介绍 本次实验实现的是使用Andriod Studio开发一个简易的音乐播放器,所包含的功能有音乐的播放.暂停.上一曲.下一曲.音乐播放的进度以及手动拖动来控制音乐的播放进度. 实现过程 导入项目所需的音乐文件.图标.背景等 1.创建一个raw文件夹,将音乐文件导入到这个文件夹中,方便我们在项目中使用 2.在drawable中导入所需的图片.图标 设计UI界面 1.设计5个button控件,分别对应上一曲,下一

  • 基于Python编写简易的成语接龙游戏

    目录 前言 1.游戏规则 2.正式敲代码 2.1模块导入 2.2读取txt数据 2.3界面设置 2.4电脑接龙 2.5重新开始新游戏 2.6成语是否合法 2.7读取成语的数据 2.8附完整的项目源码 3.效果展示 前言 "胸藏文墨怀如谷,腹有诗书气自华".      ——<和董传留别> 成语接龙是中华民族传统的文字游戏. 它历史悠久,是传统文字.文化.文明的一个缩影,也是老少皆宜的民间文化娱乐活动. 成语接龙:"龙腾虎跃,该你了!"          

  • 利用Matlab编写简易版连连看小游戏

    这是一款MATLAB连连看小游戏,基本实现了连连看的功能,不过目前没有写出死局判定的模块,日后可能会对其进行改进. 游戏截图 游戏组装说明 我们的变量path输入的是图像包的相对路径或绝对路径 这里我们由于文件夹名称就叫“图片”所以直接path='图片’是没啥问题的. 文件夹里就是图片素材啦,其名称没有任何要求,形状最好是方的,不过不是的话问题也不大,程序会自动将其拉伸成方的........ 我们这里要求的是读入jpg格式,想要png,jpg都读的话,.....最简单的就是直接读两次 完整代码

  • 利用Python编写简易版德州扑克小游戏

    目录 德州扑克简要介绍 什么是德州扑克 游戏规则简要介绍 德州扑克游戏的python实现过程 游戏初始化 评选赢家 游戏主题函数 游戏体验与展示 模块不足与后续改进 德州扑克简要介绍 什么是德州扑克 德州扑克不知道大家是否玩过,它是起源于美国的得克萨斯州的一种博弈类卡牌游戏,英文名叫做Texas Hold’em Poker.玩法上又分为常规桌(Cash, 现金局),单桌赛(SNG)和多桌锦标赛(MTT).虽然扑克种类繁多,但基本的扑克规则通常保持一致.它是一种考验心态与谋略的游戏. 游戏规则简要

  • 利用Python编写简易的录制屏幕小工具

    目录 1.准备 2.代码 由于最近测试需要录制系统界面的操作过程,因为都是全屏的操作,所以用python做一个简单的录屏小工具. 实现过程也是比较简单,就是通过对屏幕操作进行不断的截图,最后将截图合成一个操作视频的过程.由于我们只是做简单的截屏功能,并没有加入音频效果. 1.准备 开始之前我们还是按照以往的方式介绍一下使用到的第三方的python模块. from PIL import ImageGrab import numpy as np import cv2 import datetime

  • Java语言实现简单FTP软件 FTP本地文件管理模块实现(9)

    本文为大家分享了FTP本地文件管理模块的实现方法,供大家参考,具体内容如下 首先看一下界面: 1.本地文件列表的显示功能 将本地的当前目录下所有文件显示出来,并显示文件的属性包括文件名.大小.日期.通过javax.swing.JTable()来显示具体的数据.更改当前文件目录会调用com.oyp.ftp.panel.local.LocalPanel类的listLocalFiles()方法,其主要代码如下 /** * 读取本地文件到表格的方法 */ private void listLocalFi

  • Android编写简单的网络爬虫

    一.网络爬虫的基本知识 网络爬虫通过遍历互联网络,把网络中的相关网页全部抓取过来,这体现了爬的概念.爬虫如何遍历网络呢,互联网可以看做是一张大图,每个页面看做其中的一个节点,页面的连接看做是有向边.图的遍历方式分为宽度遍历和深度遍历,但是深度遍历可能会在深度上过深的遍历或者陷入黑洞.所以,大多数爬虫不采用这种形式.另一方面,爬虫在按照宽度优先遍历的方式时候,会给待遍历的网页赋予一定优先级,这种叫做带偏好的遍历. 实际的爬虫是从一系列的种子链接开始.种子链接是起始节点,种子页面的超链接指向的页面是

随机推荐