主要实现的是获取本地文件夹,读取txt和csv文件
apk: http://fir.im/h1qa
我是站在巨人的肩膀上,有前辈已经把获取手机文件的写成一个类,直接调用即可,在此表示感谢
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 java.util.HashMap;
import java.util.List;
import java.util.Map;
import android.os.Environment;
/**
* 用于获取手机的文件夹及文件的工具类,如果权限允许,可以获取手机上任意路径的文件列表
* GetFilesUtils使用的是懒汉式单例模式,线程安全
*@since 2016/1/19.
*/
public class GetFilesUtils {
public static final String FILE_TYPE_FOLDER="wFl2d";
public static final String FILE_INFO_NAME="fName";
public static final String FILE_INFO_ISFOLDER="fIsDir";
public static final String FILE_INFO_TYPE="fFileType";
public static final String FILE_INFO_NUM_SONDIRS="fSonDirs";
public static final String FILE_INFO_NUM_SONFILES="fSonFiles";
public static final String FILE_INFO_PATH="fPath";
private static GetFilesUtils gfu;
private GetFilesUtils(){
}
/**
* 获取GetFilesUtils实例
* @return GetFilesUtils
**/
public static synchronized GetFilesUtils getInstance(){
if(gfu==null){
gfu=new GetFilesUtils();
}
return gfu;
}
/**
* 获取文件path文件夹下的文件列表
* @see #getSonNode(String)
* @param path 手机上的文件夹
* @return path文件夹下的文件列表的信息,信息存储在Map中,Map的key的列表如下:
* FILE_INFO_NAME : String 文件名称
* FILE_INFO_ISFOLDER: boolean 是否为文件夹
* FILE_INFO_TYPE: string 文件的后缀
* FILE_INFO_NUM_SONDIRS : int 子文件夹个数
* FILE_INFO_NUM_SONFILES: int 子文件个数
* FILE_INFO_PATH : String 文件的绝对路径
**/
public List
我是在此基础上,使用Fragment ,文件读取分两部分,一是文件夹获取,这是一个界面,而是文件内容的读取,这需要换一个界面,完全可以使用两个activity,在这俩之间传要读取的文件路径即可,我嫌要多一个activity,就使用
Fragment 来切换界面,然后在activity与Fragment 间传数据,使用的是回调的方式
由于功能简单,回调的方法就写了一个(就是java里的接口)
public interface FragmentCallBack {
void showContent( StringBuffer sb);
}
主函数
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.support.v7.app.AppCompatActivity;
import android.view.KeyEvent;
import android.view.View;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.SimpleAdapter;
import android.widget.TextView;
import android.widget.Toast;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;
public class MainActivity extends AppCompatActivity implements FragmentCallBack{
private ListView folderLv;
//当前文件路径
private TextView foldernowTv;
private static List> aList;
private String baseFile;
private SimpleAdapter sAdapter;
private static MessageFragment messageFragment;
private static FloderFragment floderFragment;
//当前frame退出时判断使用
private static Fragment currentFragment;
private FragmentManager fragmentManager;
private static FragmentTransaction fragmentTransaction;
private static boolean isExit = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.content_main);
/*FragmentManager*/
fragmentManager = getSupportFragmentManager();
messageFragment = new MessageFragment();
floderFragment =new FloderFragment();
fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.setCustomAnimations(android.R.anim.fade_in, android.R.anim.fade_out);
//由于当前的是activity,替换后,activity里的数据还是存在的,显示很乱,不是不可以把当前类换成frame
fragmentTransaction.replace(R.id.relativelayout,floderFragment);//将fragment设置到布局上
currentFragment = floderFragment;
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commitAllowingStateLoss();
}
/*回调使用 */
public void showContent( StringBuffer sb){
Bundle data = new Bundle();
data.putString("TEXT", sb.toString());
messageFragment.setArguments(data);//通过Bundle向Activity中传递值
//由于当前的是activity,替换后,activity里的数据还是存在的,显示很乱,不是不可以把当前类换成frame
/*这里要新建一个fragmentTransaction,要不会回调出错*/
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.setCustomAnimations(android.R.anim.fade_in, android.R.anim.fade_out);
fragmentTransaction.replace(R.id.relativelayout, messageFragment);//将fragment设置到布局上
currentFragment = messageFragment;
// 加入回退栈就可以实现按返回键退回到上一个fragment界面
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commitAllowingStateLoss();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
private void exit() {
if(!isExit) {
isExit = true;
Toast.makeText(this, "再按一次退出程序", Toast.LENGTH_SHORT).show();
new Timer().schedule(new TimerTask() {
@Override
public void run() {
isExit = false;
}
}, 2000);
} else {
finish();
}
}
//监听返回键,2s内按两次退出
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK
&& event.getRepeatCount() == 0) {
//当前是文件读取界面
if(currentFragment == messageFragment){
currentFragment = floderFragment;
////退回 文件夹 界面,不过界面是初始化时的样子,下面这两种方式一样的效果
getSupportFragmentManager().popBackStack();
// return super.onKeyDown(keyCode, event);
}else {
exit();
}
}
return false;
}
}
这里继承了上面的回调方法,就是接口的实现类嘛,方法里实现的是更新当前布局为文件读取界面,把返回键重写了,按一次,如果当前界面是文件内容读取界面,就回到文件夹获取界面,如果是文件夹获取页面,2S 内按两次返回键退出
private void exit() {
if(!isExit) {
isExit = true;
Toast.makeText(this, "再按一次退出程序", Toast.LENGTH_SHORT).show();
new Timer().schedule(new TimerTask() {
@Override
public void run() {
isExit = false;
}
}, 2000);
} else {
finish();
}
}
mainactivity布局
读取文件夹的布局文件
文件夹是使用list显示的,每一条记录显示的格式的布局
有图片,有文件名,有文件的信息,反正这些数据上面的文件工具类都已经提供好了
读取文件的布局,数据显示在TextView里
由于文件数据量不是一个页面就可以全部显示完的,所以TextView加了垂直滚动,看看文件读取messageFragment
public class MessageFragment extends Fragment {
private TextView textView;
public MessageFragment() {
// Required empty public constructor
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.content_message, container, false);
textView = (TextView)view.findViewById(R.id.message);
/*TextView自滚动(垂直)*/
textView.setMovementMethod(ScrollingMovementMethod.getInstance()) ;
//获取数据
Bundle data = getArguments();//获得从activity中传递过来的值
textView.setText(data.getString("TEXT"));
return view ;
}
}
读取文件夹的
import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.SimpleAdapter;
import android.widget.TextView;
import android.widget.Toast;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Created by Administrator on 2016/1/20.
*/
public class FloderFragment extends Fragment {
private ListView folderLv;
//当前文件路径
private TextView foldernowTv;
private static List> aList;
private String baseFile;
private SimpleAdapter sAdapter;
private static FragmentCallBack fragmentCallBack = null;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.content_floder, container, false);
/*ListView*/
folderLv = (ListView) view.findViewById(R.id.listview);
aList=new ArrayList>();
sAdapter=new SimpleAdapter(getContext(), aList,R.layout.listitem_folder, new String[]{"fImg","fName","fInfo"},
new int[]{R.id.folder_img,R.id.folder_name,R.id.folder_info});
folderLv.setAdapter(sAdapter);
folderLv.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView> parent, View view, int position, long id) {
if(aList.get(position).get("fIsDir").equals(true)){
try {
loadFolderList(aList.get(position).get("fPath").toString());
} catch (IOException e) {
e.printStackTrace();
}
}else{//打开文件之前判断一下
if(aList.get(position).get("fImg").equals(R.drawable.filetype_unknow)){
final int positi = position;
new AlertDialog.Builder(getContext())
.setTitle("前方高能预警,强烈建议停止读取,确定要读取?")
.setPositiveButton("确定", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
readTxtFile(aList.get(positi).get("fPath").toString());
return;
}
})
.setNegativeButton("取消", null)
.show();
}else {
readTxtFile(aList.get(position).get("fPath").toString());
}
}
}
});
baseFile=GetFilesUtils.getInstance().getBasePath();
foldernowTv=(TextView)view.findViewById(R.id.folder_now);
foldernowTv.setText(baseFile);
foldernowTv.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
String folder = GetFilesUtils.getInstance().getParentPath(foldernowTv.getText().toString());
if (folder == null) {
Toast.makeText(getContext(), "无父目录", Toast.LENGTH_SHORT).show();
} else {
loadFolderList(folder);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
return view ;
}
private void loadFolderList(String file) throws IOException{
List> list=GetFilesUtils.getInstance().getSonNode(file);
if(list!=null){
Collections.sort(list, GetFilesUtils.getInstance().defaultOrder());
aList.clear();
for(Map map:list){
String fileType=(String) map.get(GetFilesUtils.FILE_INFO_TYPE);
Map gMap=new HashMap();
if(map.get(GetFilesUtils.FILE_INFO_ISFOLDER).equals(true)){
gMap.put("fIsDir", true);
gMap.put("fImg",R.drawable.filetype_folder );
gMap.put("fInfo", map.get(GetFilesUtils.FILE_INFO_NUM_SONDIRS)+"个文件夹和"+
map.get(GetFilesUtils.FILE_INFO_NUM_SONFILES)+"个文件");
}else{
gMap.put("fIsDir", false);
if(fileType.equals("txt")||fileType.equals("text")||fileType.equals("csv")){
gMap.put("fImg", R.drawable.filetype_text);
}else{
gMap.put("fImg", R.drawable.filetype_unknow);
}
gMap.put("fInfo","文件大小:"+GetFilesUtils.getInstance().getFileSize(map.get(GetFilesUtils.FILE_INFO_PATH).toString()));
}
gMap.put("fName", map.get(GetFilesUtils.FILE_INFO_NAME));
gMap.put("fPath", map.get(GetFilesUtils.FILE_INFO_PATH));
aList.add(gMap);
}
}else{
aList.clear();
}
sAdapter.notifyDataSetChanged();
foldernowTv.setText(file);
}
/**
* 功能:Java读取txt文件的内容
* 步骤:1:先获得文件句柄
* 2:获得文件句柄当做是输入一个字节码流,需要对这个输入流进行读取
* 3:读取到输入流后,需要读取生成字节流
* 4:一行一行的输出。readline()。
* 备注:需要考虑的是异常情况
* @param filePath
*/
public static void readTxtFile(String filePath) {
try {
String encoding = "GBK";
StringBuffer sb = new StringBuffer();
File file = new File(filePath);
if (file.isFile() && file.exists()) { //判断文件是否存在
InputStreamReader read = new InputStreamReader(
new FileInputStream(file), encoding);//考虑到编码格式
BufferedReader bufferedReader = new BufferedReader(read);
String lineTxt = null;
while ((lineTxt = bufferedReader.readLine()) != null) {
sb.append(lineTxt);
// System.out.println(lineTxt);
}
//传数据到messagefragment
//1 直接调用Activity中的方法 ,找不到,不知为何,可能不支持了吧
//2 回调
fragmentCallBack.showContent(sb);
read.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**2 回调需要重写*/
@Override
public void onAttach(Activity activity) {
// TODO Auto-generated method stub
super.onAttach(activity);
fragmentCallBack = (MainActivity)activity;
}
}
我把读取文件夹和读取文件都放在一个类里面实现,传送的数据是文件的内容,这样可能会有一些文件,就是当文件稍微有些大时,数据肯定会内存溢出,改进方案就是只传一个文件路径,读取文件的事放在MessageFragment 里去做
代码的流程是:当要读取文件内容时,使用回调函数,去显示文件信息
源码:http://download.csdn.net/detail/i_do_can/9415981