最近在做一個將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 int folder_level;
首先是獲取存儲器列表
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,boolean isBack) {
if (isBack)
folder_level--;
else
folder_level++;
txtPath.setText(folder.getPath());
ArrayList<File> files = new ArrayList<>();
File[] folderFile = folder.listFiles();
if (null != folderFile && folderFile.length > 0) {
Collections.addAll(files, folderFile);
}
//排序 文件夾在前,然後按文件名排序
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());
}
}
});
//新建集合用做打開文件夾
ArrayList<File> openedFolder = new ArrayList<>();
//將上層目錄保存到第一項
openedFolder.add(folder.getParentFile());
//將排序完畢的內容添加到目標集合 目的:解決第一個文件夾不是上一層地址問題
openedFolder.addAll(files);
fileAdapter = new FileAdapter(this, openedFolder, false);
folderList.setAdapter(fileAdapter);
}
程序剛運行時需要加載存儲器列表,而不是某一文件夾,所有需要額外定義一個函數
//獲取根目錄數據
private void rootFile() {
txtPath.setText("/");
fileAdapter = new FileAdapter(this, rootFileList,true);
folderList.setAdapter(fileAdapter);
folder_level= 0;
}
有了以上這些,在控制文件創建是直接調用相應的函數即可
@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(folder_level == 0)//判斷是否在根目錄
refurbish(file,false);
else if (folder_level == 1 && position == 0) //判斷當前位置是否在一級目錄 並且 是否點擊第一項
rootFile();
else
refurbish(file,position == 0);
}
}
});
getRootFile();
rootFile();
setSubject();
importManger = new ImportManger();
}
弄了這麼多基本算是完成了,爲了用戶友好,我對返回鍵進行了重載
/**
* 監聽物理按鍵
* 需要注意的是這個函數是有返回值的
* 返回值可以理解爲
* true表示做了處理,就不提交給處理系統的back按鍵事件
* false則是提交給系統處理
*/
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if ((keyCode == KeyEvent.KEYCODE_BACK && event.getRepeatCount() == 0)) {
if (folder_level != 0) {
if (folder_level != 1)
refurbish(fileAdapter.getItem(0),true);
else
rootFile();
return true;
} else {
finish();
}
}
return super.onKeyDown(keyCode, event);
}
參考文獻:
http://www.cnblogs.com/shang53880/archive/2012/07/03/2574806.html
http://blog.sina.com.cn/s/blog_5da93c8f0102vcam.html
http://vjson.com/wordpress/%E8%8E%B7%E5%8F%96android%E8%AE%BE%E5%A4%87%E6%8C%82%E8%BD%BD%E7%9A%84%E6%89%80%E6%9C%89%E5%AD%98%E5%82%A8%E5%99%A8.html