1.前言
我们平时希望从存储获取一个文件,总是要调用系统的文件管理器,但是系统的管理器并不是所有手机都有的,如果没有会提示用户安装,这种体验非常不好,并且系统选择器很难看,于是自己写了一个漂亮简洁的文件选择器,嵌入式的既方便又不复杂。
- FolderChooserActivity.class
- FolderChooserAdapter.class
- FolderChooserInfo.class
2.功能
支持文件和文件夹选择,在调用到选择器时只需要传入一个boolean值即可区分。
Intent intent = new Intent(MainActivity.this, FolderChooserActivity.class);
intent.putExtra("isFolderChooser", true);
startActivityForResult(intent, REQUEST_FOLDER);
支持自定义文件类型的图标,我自己写了几种常用的类型,你可以在else下面加入你需要的类型
private int fileType(File file){
int image = R.mipmap.type_file;
if(file.isDirectory()){
image = R.mipmap.type_folder;
}else{
try {
// 指定文件类型的图标
String[] token = file.getName().split("\\.");
String suffix = token[token.length - 1];
if (suffix.equalsIgnoreCase("txt")) {
image = R.mipmap.type_txt;
} else if (suffix.equalsIgnoreCase("png") || suffix.equalsIgnoreCase("jpg") || suffix.equalsIgnoreCase("jpeg") || suffix.equalsIgnoreCase("gif")) {
image = R.mipmap.type_image;
} else if (suffix.equalsIgnoreCase("mp3")) {
image = R.mipmap.type_mp3;
} else if (suffix.equalsIgnoreCase("mp4") || suffix.equalsIgnoreCase("rmvb") || suffix.equalsIgnoreCase("avi")) {
image = R.mipmap.type_video;
} else if (suffix.equalsIgnoreCase("apk")) {
image = R.mipmap.type_apk;
}
}catch (Exception e){
e.printStackTrace();
}
}
return image;
}
如果是文件选择器的话,支持自由选择文件类型,只需要传入一个imietype,默认所有类型*/*
Intent intent = new Intent(MainActivity.this, FolderChooserActivity.class);
intent.putExtra("isFolderChooser", false);
intent.putExtra("mimeType", "image/*");
startActivityForResult(intent, REQUEST_FILE);
3.使用方法
选择文件夹的话,进入要选择的文件夹点击右上角保存图标即可选择,在文件选择器里除了可以直接点击文件选择外,同样可以点击右上角保存按钮选择文件夹。
本选择器使用线程池处理耗时查找文件的内容,查找时会转圈,不过一般SD卡文件也就那么些,文件多了也就1到2秒。
头部标题栏可以自定义,可以直接使用我写的,也可以用ActionBar,注意保存的按钮处理一下即可。
跳转到选择器时可传入三种intent extra,一个不传也没事,都有默认值
intent.putExtra("isFolderChooser", true);//是否是文件夹,默认false
//选择器初始路径,默认SD卡根目录
intent.putExtra("file_path", Environment.getExternalStorageDirectory().getAbsolutePath() + "/DCIM");
intent.putExtra("mimeType", "image/*");//文件选择器类型,默认所有文件
最后在你的activity接收返回的文件地址
<span style="font-size:18px;">@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode == RESULT_OK) {
switch (requestCode){
case REQUEST_FOLDER:
File folder = (File) data.getSerializableExtra("file_path");
Toast.makeText(MainActivity.this, "已选择:" + folder.getAbsolutePath(), Toast.LENGTH_LONG).show();
break;
case REQUEST_FILE:
File file = (File) data.getSerializableExtra("file_path");
Toast.makeText(MainActivity.this, "已选择:" + file.getAbsolutePath(), Toast.LENGTH_LONG).show();
break;
}
}
super.onActivityResult(requestCode, resultCode, data);
}</span>
列表分割线使用了一个库com.yqritc:recyclerview-flexibledivider:1.2.8,你也可以不用它,自己在RecyclerView的item中定制你的分割线。
4.权限
别忘了加入权限:<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
5.全部源码
package com.example.folderchooser;
import android.content.Intent;
import android.net.Uri;
import android.os.Environment;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
import com.example.folderchooser.folderchooser.FolderChooserActivity;
import java.io.File;
public class MainActivity extends AppCompatActivity {
private Button choose_folder;
private Button choose_file;
private static final int REQUEST_FOLDER = 1;
private static final int REQUEST_FILE = 2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
}
private void initView() {
choose_folder = (Button) findViewById(R.id.choose_folder);
choose_file = (Button) findViewById(R.id.choose_file);
choose_folder.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent(MainActivity.this, FolderChooserActivity.class);
intent.putExtra("isFolderChooser", true);
intent.putExtra("file_path", Environment.getExternalStorageDirectory().getAbsolutePath() + "/DCIM");
startActivityForResult(intent, REQUEST_FOLDER);
}
});
choose_file.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent(MainActivity.this, FolderChooserActivity.class);
intent.putExtra("isFolderChooser", false);
intent.putExtra("mimeType", "image/*");
startActivityForResult(intent, REQUEST_FILE);
}
});
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode == RESULT_OK) {
switch (requestCode){
case REQUEST_FOLDER:
File folder = (File) data.getSerializableExtra("file_path");
Toast.makeText(MainActivity.this, "已选择:" + folder.getAbsolutePath(), Toast.LENGTH_LONG).show();
break;
case REQUEST_FILE:
File file = (File) data.getSerializableExtra("file_path");
Toast.makeText(MainActivity.this, "已选择:" + file.getAbsolutePath(), Toast.LENGTH_LONG).show();
break;
}
}
super.onActivityResult(requestCode, resultCode, data);
}
}
package com.example.folderchooser.folderchooser;
import android.annotation.SuppressLint;
import android.content.Intent;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.webkit.MimeTypeMap;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.example.folderchooser.R;
import com.yqritc.recyclerviewflexibledivider.HorizontalDividerItemDecoration;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 文件和文件夹选择器
* 作者:yedongyang
* 我的博客:http://blog.csdn.net/sinat_25689603
* created by ydy on 2016/7/11 11:47
*/
public class FolderChooserActivity extends AppCompatActivity {
ImageView titleLeft; //标题栏左边按钮
TextView titleText; // 标题栏title
ImageView titleRight; //标题栏右边按钮
TextView savePath;
RecyclerView recyclerView;
LinearLayout loading_view;
//是否为文件夹选择器。true文件夹,false文件
private boolean isFolderChooser = false;
private String mimeType = "*/*";
private String mInitialPath = Environment.getExternalStorageDirectory().getAbsolutePath();
private FolderChooserAdapter mAdapter;
private List
mData;
private File parentFolder;
private List
parentContents;
private boolean canGoUp = true;
private ExecutorService singleThreadExecutor;
@SuppressLint("HandlerLeak")
private Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what){
case 0:
savePath.setText(parentFolder.getAbsolutePath());
mData.clear();
mData.addAll(getContentsArray());
mAdapter.notifyDataSetChanged();
loading_view.setVisibility(View.GONE);
recyclerView.setVisibility(View.VISIBLE);
break;
}
}
};
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_folder_chooser);
isFolderChooser = getIntent().getBooleanExtra("isFolderChooser", false);
String mime_Type = getIntent().getStringExtra("mimeType");
String file_path = getIntent().getStringExtra("file_path");
mimeType = mime_Type == null ? mimeType : mime_Type;
singleThreadExecutor = Executors.newSingleThreadExecutor();
mInitialPath = file_path == null ? mInitialPath : file_path;
parentFolder = new File(mInitialPath);
initView();
setData();
}
private void setData(){
singleThreadExecutor.execute(new Runnable() {
@Override
public void run() {
parentContents = isFolderChooser ? listFiles() : listFiles(mimeType);
handler.sendEmptyMessage(0);
}
});
}
private void initView() {
findViewById();
setTitleView();
setRecyclerView();
savePath.setText(parentFolder.getAbsolutePath());
}
//设置标题栏
private void setTitleView() {
titleText.setText("请选择目录");
titleLeft.setVisibility(View.VISIBLE);
titleRight.setVisibility(View.VISIBLE);
titleLeft.setImageResource(R.mipmap.ic_arrow_back_white_24dp);
titleRight.setImageResource(R.mipmap.ic_save_white_24dp);
titleLeft.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
FolderChooserActivity.this.finish();
}
});
titleRight.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ChooserEnd();
}
});
}
private void setRecyclerView() {
mData = new ArrayList<>();
//设置布局管理器
LinearLayoutManager manager = new LinearLayoutManager(this);
manager.setOrientation(LinearLayoutManager.VERTICAL);
recyclerView.setLayoutManager(manager);
//分割线
recyclerView.addItemDecoration(new HorizontalDividerItemDecoration.Builder(this)
.color(0xFFE9E7E8)
.size(1)
.build());
//设置适配器
mAdapter = new FolderChooserAdapter(this, mData, new ItemClickCallback() {
@Override
public void onClick(View view, int position, FolderChooserInfo info) {
onSelection(view, position, info);
}
});
recyclerView.setAdapter(mAdapter);
}
private List
getContentsArray() {
List
results = new ArrayList<>();
if (parentContents == null) {
if (canGoUp){
FolderChooserInfo info = new FolderChooserInfo();
info.setName("...");
info.setFile(null);
info.setImage(R.mipmap.back);
results.add(info);
}
return results;
}
if (canGoUp){
FolderChooserInfo info = new FolderChooserInfo();
info.setName("...");
info.setFile(null);
info.setImage(R.mipmap.back);
results.add(info);
}
results.addAll(parentContents);
return results;
}
public void onSelection( View view, int position, FolderChooserInfo info) {
if (canGoUp && position == 0) {
if (parentFolder.isFile()) {
parentFolder = parentFolder.getParentFile();
}
parentFolder = parentFolder.getParentFile();
if (parentFolder.getAbsolutePath().equals("/storage/emulated"))
parentFolder = parentFolder.getParentFile();
canGoUp = parentFolder.getParent() != null;
} else {
parentFolder = info.getFile();
canGoUp = true;
if (parentFolder.getAbsolutePath().equals("/storage/emulated"))
parentFolder = Environment.getExternalStorageDirectory();
}
if (parentFolder.isFile()) {
ChooserEnd();
}else{
loading_view.setVisibility(View.VISIBLE);
recyclerView.setVisibility(View.GONE);
setData();
}
}
private List
listFiles() { File[] contents = parentFolder.listFiles(); List
results = new ArrayList<>(); if (contents != null) { for (File fi : contents) { if (fi.isDirectory()){ FolderChooserInfo info = new FolderChooserInfo(); info.setName(fi.getName()); info.setFile(fi); info.setImage(fileType(fi)); results.add(info); } } Collections.sort(results, new FolderSorter()); return results; } return null; } private List
listFiles(String mimeType) { File[] contents = parentFolder.listFiles(); List
results = new ArrayList<>(); if (contents != null) { MimeTypeMap mimeTypeMap = MimeTypeMap.getSingleton(); for (File fi : contents) { if (fi.isDirectory()) { FolderChooserInfo info = new FolderChooserInfo(); info.setName(fi.getName()); info.setFile(fi); info.setImage(fileType(fi)); results.add(info); } else { if (fileIsMimeType(fi, mimeType, mimeTypeMap)) { FolderChooserInfo info = new FolderChooserInfo(); info.setName(fi.getName()); info.setFile(fi); info.setImage(fileType(fi)); results.add(info); } } } Collections.sort(results, new FileSorter()); return results; } return null; } boolean fileIsMimeType(File file, String mimeType, MimeTypeMap mimeTypeMap) { if (mimeType == null || mimeType.equals("*/*")) { return true; } else { // get the file mime type String filename = file.toURI().toString(); int dotPos = filename.lastIndexOf('.'); if (dotPos == -1) { return false; } String fileExtension = filename.substring(dotPos + 1); String fileType = mimeTypeMap.getMimeTypeFromExtension(fileExtension); if (fileType == null) { return false; } // check the 'type/subtype' pattern if (fileType.equals(mimeType)) { return true; } // check the 'type/*' pattern int mimeTypeDelimiter = mimeType.lastIndexOf('/'); if (mimeTypeDelimiter == -1) { return false; } String mimeTypeMainType = mimeType.substring(0, mimeTypeDelimiter); String mimeTypeSubtype = mimeType.substring(mimeTypeDelimiter + 1); if (!mimeTypeSubtype.equals("*")) { return false; } int fileTypeDelimiter = fileType.lastIndexOf('/'); if (fileTypeDelimiter == -1) { return false; } String fileTypeMainType = fileType.substring(0, fileTypeDelimiter); if (fileTypeMainType.equals(mimeTypeMainType)) { return true; } } return false; } private static class FileSorter implements Comparator
{ @Override public int compare(FolderChooserInfo lhs, FolderChooserInfo rhs) { if (lhs.getFile().isDirectory() && !rhs.getFile().isDirectory()) { return -1; } else if (!lhs.getFile().isDirectory() && rhs.getFile().isDirectory()) { return 1; } else { return lhs.getName().compareTo(rhs.getName()); } } } private static class FolderSorter implements Comparator
{ @Override public int compare(FolderChooserInfo lhs, FolderChooserInfo rhs) { return lhs.getName().compareTo(rhs.getName()); } } public interface ItemClickCallback{ void onClick(View view, int position, FolderChooserInfo info); } private void findViewById(){ titleLeft = (ImageView) findViewById(R.id.title_left); titleRight = (ImageView) findViewById(R.id.title_right); titleText = (TextView) findViewById(R.id.title_text); savePath = (TextView) findViewById(R.id.save_path); recyclerView = (RecyclerView) findViewById(R.id.recyclerView); loading_view = (LinearLayout) findViewById(R.id.loading_view); } private void ChooserEnd(){ File result = parentFolder; Intent intent = new Intent(); intent.putExtra("file_path", result); setResult(RESULT_OK, intent); finish(); } private int fileType(File file){ int image = R.mipmap.type_file; if(file.isDirectory()){ image = R.mipmap.type_folder; }else{ try { // 指定文件类型的图标 String[] token = file.getName().split("\\."); String suffix = token[token.length - 1]; if (suffix.equalsIgnoreCase("txt")) { image = R.mipmap.type_txt; } else if (suffix.equalsIgnoreCase("png") || suffix.equalsIgnoreCase("jpg") || suffix.equalsIgnoreCase("jpeg") || suffix.equalsIgnoreCase("gif")) { image = R.mipmap.type_image; } else if (suffix.equalsIgnoreCase("mp3")) { image = R.mipmap.type_mp3; } else if (suffix.equalsIgnoreCase("mp4") || suffix.equalsIgnoreCase("rmvb") || suffix.equalsIgnoreCase("avi")) { image = R.mipmap.type_video; } else if (suffix.equalsIgnoreCase("apk")) { image = R.mipmap.type_apk; } }catch (Exception e){ e.printStackTrace(); } } return image; } } package com.example.folderchooser.folderchooser; import android.content.Context; import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; import com.example.folderchooser.R; import java.util.List; public class FolderChooserAdapter extends RecyclerView.Adapter
{ private Context mContext; private LayoutInflater mInflater; private List
mData; private FolderChooserActivity.ItemClickCallback callback; public FolderChooserAdapter(Context mContext, List
mData, FolderChooserActivity.ItemClickCallback callback) { this.mContext = mContext; this.mData = mData; this.callback = callback; mInflater = LayoutInflater.from(mContext); } @Override public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View view = mInflater.inflate(R.layout.folder_chooser_item, parent, false); ViewHolder holder = new ViewHolder(view); return holder; } @Override public void onBindViewHolder(ViewHolder holder, final int position) { final FolderChooserInfo info = mData.get(position); holder.name.setText(info.getName() == null ? "" : info.getName()); holder.v.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { callback.onClick(v, position, info); } }); holder.image.setImageResource(info.getImage()); } @Override public int getItemCount() { return mData.size(); } static class ViewHolder extends RecyclerView.ViewHolder{ ImageView image; TextView name; LinearLayout v; ViewHolder(View view) { super(view); image = (ImageView) view.findViewById(R.id.image); name = (TextView) view.findViewById(R.id.name); v = (LinearLayout) view.findViewById(R.id.view); } } } package com.example.folderchooser.folderchooser; import java.io.File; public class FolderChooserInfo { private String name; private File file; private int image; public String getName() { return name; } public void setName(String name) { this.name = name; } public File getFile() { return file; } public void setFile(File file) { this.file = file; } public int getImage() { return image; } public void setImage(int image) { this.image = image; } }
参考:https://github.com/afollestad/material-dialogs#selectors