不知怎么突然有了想写一个txt阅读器的想法 ……目前只实现了一小部分功能,并且参考了网上很多大神的代码,受益匪浅!!~
目前实现的功能:
1.(1)首次打开阅读器时,会弹出选择对话框,可以选择扫描sd卡方式,扫描出sd卡上的所有txt文件并进行简单的筛选(>50KB)之后,得到的文件将被显示在ListView中;
(2)若选择了通过路径添加,则会弹出路径选择的Activity,点击确定后选择的txt文件将会出现在ListView的最后;
(3)若选择了稍后手动添加,则稍候可点击界面右上角加号弹出该选择对话框。
2.长按ListView中的item,则会进入多选删除模式。
嗯哼……开始贴代码了……
主activity的布局文件就不贴了 。。一个ListView。。。一个listitem。。。贴一下menu:book_list.xml
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http:///apk/res/android" >
<item
android:id="@+id/action_addingbooks"
android:icon="@android:drawable/ic_menu_add"
android:orderInCategory="500"
android:showAsAction="ifRoom"
android:title="添加书籍"/>
</menu>
实现右上角的加号,点击弹出添加书籍对话框功能。
然后是BookListActivity.class
package com.ldgforever.jianreader;
import .*;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.*;
import android.util.Log;
import android.view.*;
import android.widget.ListView;
import android.widget.Toast;
import android.widget.AbsListView.MultiChoiceModeListener;
import com.ldgforever.jianreader.R;
import com.ldgforever.savedata.savedataListMap;
import java.io.File;
import java.util.*;
public class BookListActivity extends Activity {
private static List<String> file_name;
private static List<String> file_txt_path;
private MyBookAdapter adapter;
private File file;
private List<Map<String, String>> listItems;
private MultiModeCallback mCallback;
private String mExternalStoragePath;
private Handler mHandler;
private ListView mListView;
private ProgressDialog mProgressDialog;
/**
* 接收返回的路径
*/
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
Log.d("com.ldgforever.jianreader", "receivingPath");
if (data != null) {
Log.d("com.ldgforever.jianreader", "onActivityResult");
String mPath = data.getStringExtra("file");
File pathFile = new File(mPath);
Map<String, String> pathMap = new HashMap<String, String>();
if (pathFile.exists()) {
if (pathFile.getName().endsWith(".txt")) {
pathMap.put("Name", pathFile.getName());
pathMap.put("Path", pathFile.getPath());
listItems.add(pathMap);
savedataListMap.saveInfo(BookListActivity.this, "ListMap", listItems);
ShowTxtFilesInList(listItems);
} else {
Toast.makeText(BookListActivity.this, "请选择一个txt文件!", 0).show();
;
}
}
}
}
@Override
protected void onCreate(Bundle bundle) {
super.onCreate(bundle);
setContentView(R.layout.activity_book_list);
mProgressDialog = new ProgressDialog(BookListActivity.this);
mProgressDialog.setCancelable(false);
mProgressDialog.setMessage("正在搜索书籍,请稍候 ……");
mExternalStoragePath = Environment.getExternalStorageDirectory().toString();
file = new File(mExternalStoragePath);
file_name = new ArrayList<String>();
file_txt_path = new ArrayList<String>();
listItems = new ArrayList<Map<String, String>>();
listItems = savedataListMap.getInfo(BookListActivity.this, "ListMap");
if (listItems.isEmpty()) {
BookAddingDialog();
} else {
ShowTxtFilesInList(listItems);
}
mHandler = new Handler() {
public void handleMessage(Message message) {
switch (message.what) {
case 11:
ShowTxtFilesInList(listItems);
savedataListMap.saveInfo(BookListActivity.this, "ListMap", listItems);
if (mProgressDialog.isShowing()) {
mProgressDialog.dismiss();
return;
}
break;
case 12:
ShowTxtFilesInList(listItems);
savedataListMap.saveInfo(BookListActivity.this, "ListMap", listItems);
if (mProgressDialog.isShowing()) {
mProgressDialog.dismiss();
return;
}
break;
default:
break;
}
return;
}
};
}
/**
* 书籍添加对话框
*/
private void BookAddingDialog() {
.AlertDialog.Builder builder = new .AlertDialog.Builder(this);
builder.setTitle("请选择添加书籍的方式");
builder.setPositiveButton("扫描SDCard添加", new android.content.DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialoginterface, int i) {
listItems = new ArrayList<Map<String, String>>();
mProgressDialog.show();
new Thread() {
public void run() {
listFileTxt(file);
mHandler.sendEmptyMessage(12);
}
}.start();
}
});
builder.setNegativeButton("选择路径添加", new android.content.DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialoginterface, int i) {
Intent intent = new Intent(BookListActivity.this, MyFileManager.class);
startActivityForResult(intent, 2);
}
});
builder.setNeutralButton("稍后手动添加", new android.content.DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialoginterface, int i) {
dialoginterface.dismiss();
}
});
builder.create().show();
}
/**
* 将保存在List<Map<String,String>>中的书籍信息显示到ListView中
* @param listItems
*/
private void ShowTxtFilesInList(List<Map<String, String>> listItems) {
if (file_name != null) {
for (int i = 0; i < file_name.size(); i++) {
HashMap<String, String> hashmap = new HashMap<String, String>();
hashmap.put("Name", (String) file_name.get(i));
hashmap.put("Path", (String) file_txt_path.get(i));
listItems.add(hashmap);
}
adapter = new MyBookAdapter(this, listItems);
mCallback = new MultiModeCallback();
mListView = (ListView) findViewById(.booklist);
mListView.setChoiceMode(3); // Multi
mListView.setMultiChoiceModeListener(mCallback);
mListView.setAdapter(adapter);
} else {
failAddingDialog();
return;
}
}
/**
* 添加书籍失败对话框
*/
private void failAddingDialog() {
.AlertDialog.Builder builder = new .AlertDialog.Builder(this);
builder.setTitle("添加书籍失败");
builder.setPositiveButton("确定", new android.content.DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialoginterface, int i) {
dialoginterface.dismiss();
}
});
}
/**
* 递归查找SD卡上所有书籍
* @param file
*/
public static void listFileTxt(File file) {
File[] files = file.listFiles();
try {
for (File f : files) {
if (!f.isDirectory()) {
if (f.getName().endsWith(".txt")) {
long size = f.length();
if (size > 50 * 1024) {
file_name.add(f.getName());
file_txt_path.add(f.getAbsolutePath());
}
}
} else if (f.isDirectory()) {
listFileTxt(f);
}
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.book_list, menu);
return super.onCreateOptionsMenu(menu);
}
public boolean onOptionsItemSelected(MenuItem menuitem) {
switch (menuitem.getItemId()) {
case .action_addingbooks:
BookAddingDialog();
break;
default:
break;
}
return true;
}
private class MultiModeCallback implements MultiChoiceModeListener {
public boolean onActionItemClicked(ActionMode actionmode, MenuItem menuitem) {
switch (menuitem.getItemId()) {
case .menu_delete:
adapter.deleteSeletcedItems();
adapter.notifyDataSetChanged();
actionmode.finish();
return true;
default:
return false;
}
}
public boolean onCreateActionMode(ActionMode actionmode, Menu menu) {
actionmode.getMenuInflater().inflate(R.menu.book_actionmenu, menu);
adapter.setItemMultiCheckable(true);
adapter.notifyDataSetChanged();
return true;
}
public void onDestroyActionMode(ActionMode actionmode) {
savedataListMap.saveInfo(BookListActivity.this, "ListMap", listItems);
adapter.setItemMultiCheckable(false);
adapter.clearSeletedItems();
adapter.notifyDataSetChanged();
}
public void onItemCheckedStateChanged(ActionMode actionmode, int i, long l, boolean flag) {
if (flag)
adapter.addSelectedItem(i);
else
adapter.cancelSelectedItem(i);
adapter.notifyDataSetChanged();
actionmode.invalidate();
}
public boolean onPrepareActionMode(ActionMode actionmode, Menu menu) {
return false;
}
}
}
程序里有一定的注释0-0,直接看比较清晰。查找txt文件用的是递归查找文件名末尾为".txt"的文件,如果满足条件就添加到Map<String,String>里。这里还自定义了一个Adapter用以将txt文件更新到ListView上并且应用了ActionMode实现了ListView的多选和删除功能。关于ActionMode的使用参考了以下两篇文章,感谢!
1.
2. (强推)
然后就是点击通过路径添加之后通过startForResult打开路径添加的Activity
嚄……贴一下actionmode的menu,里面只有一个删除的按钮
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http:///apk/res/android" >
<item
android:id="@+id/menu_delete"
android:icon="@android:drawable/ic_menu_delete"
android:showAsAction="ifRoom"
android:title="删除"/>
</menu>
贴一下自定义的BookAdapter,删除添加item的方法也写在了里面
package com.ldgforever.jianreader;
import android.content.Context;
import android.view.*;
import android.widget.*;
import java.util.*;
import com.ldgforever.jianreader.R;
public class MyBookAdapter extends BaseAdapter {
private List<Map<String, String>> listItems;
private static List<Map<String, String>> isSelected;
private boolean itemMultiCheckable;
private Context mContext;
private ViewHolder mViewHolder;
static class ViewHolder {
public TextView mBookName;
public CheckBox mCheckBox;
}
public MyBookAdapter(Context context, List<Map<String, String>> listItems) {
this.mContext = context;
this.listItems = listItems;
isSelected = new ArrayList<Map<String, String>>();
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public int getCount() {
return listItems.size();
}
@Override
public Object getItem(int i) {
return listItems.get(i);
}
@Override
public View getView(int i, View view, ViewGroup viewgroup) {
mViewHolder = new ViewHolder();
if (view == null) {
view = LayoutInflater.from(mContext).inflate(R.layout.booklist_item, null);
mViewHolder.mBookName = (TextView) view.findViewById(.book_name);
mViewHolder.mCheckBox = (CheckBox) view.findViewById(.mCheckBox);
view.setTag(mViewHolder);
} else {
mViewHolder = (ViewHolder) view.getTag();
}
if (itemMultiCheckable) {
mViewHolder.mCheckBox.setVisibility(View.VISIBLE);
if (isSelected.contains(listItems.get(i))) {
mViewHolder.mCheckBox.setChecked(true);
} else {
mViewHolder.mCheckBox.setChecked(false);
}
} else {
mViewHolder.mCheckBox.setVisibility(View.GONE);
}
mViewHolder.mBookName.setText(listItems.get(i).get("Name"));
return view;
}
public void addSelectedItem(int i) {
isSelected.add(listItems.get(i));
}
public void cancelSelectedItem(int i) {
isSelected.remove(listItems.get(i));
}
public void clearSeletedItems() {
isSelected = new ArrayList<Map<String, String>>();
}
public void deleteSeletcedItems() {
for (Map<String, String> map : isSelected) {
listItems.remove(map);
}
}
public void setItemMultiCheckable(boolean flag) {
itemMultiCheckable = flag;
}
}
为了每次打开应用不重新扫描txt……需要存储一下存有txt文件信息的List<Map<String,String>>,这里我主要查到了两种方法,1.序列化 2.存为JSON数组形式 然后保存在SharePreference中
1.序列化
package com.ldgforever.jianreader;
import java.io.Serializable;
import java.util.List;
import java.util.Map;
public class SeriallizableList implements Serializable {
private List<Map<String, String>> listItems;
public SeriallizableList() {
}
public List<Map<String, String>> getListItems() {
return listItems;
}
public void setListItems(List<Map<String, String>> list) {
listItems = list;
}
}
2.保存为JSON数组形式
这里就不贴了0.0 贴出参考的内容相同的博(虽然是在别的网站看见的但是贴站内好像比较友好……?),感觉没有什么改的必要。
然后0.0就到了选择路径添加的部分……首先来看一下布局文件
一个用来显示当前目录路径的TextView,一个ListView列出可供选择的文件,线性布局的最下方是确定和取消两个按钮。这里还应用了Selector来改变点击ListView的item时的背景颜色,为了某种程度上的美观……?通过android:listSelector = "@drawable/mfilelist_view"设置
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http:///apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#FFFFFF"
android:orientation="vertical" >
<TextView
android:id="@+id/tv_path"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="5px"
android:textColor="#436EEE"
android:textSize="20sp" >
</TextView>
<ListView
android:id="@android:id/list"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="9"
android:listSelector="@drawable/mfilelist_view">
</ListView>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:background="#FFFFFF"
android:orientation="horizontal" >
<Button
android:id="@+id/btn_yes"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="6dp"
android:layout_weight="1"
android:background="#436EEE"
android:text="确定"
android:textColor="#FFFFFF" />
<Button
android:id="@+id/btn_cancel"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="6dp"
android:layout_weight="1"
android:background="#436EEE"
android:text="取消"
android:textColor="#FFFFFF" />
</LinearLayout>
</LinearLayout>
mfilelist_view.xml如下
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http:///apk/res/android" >
<item android:state_selected="true"
android:drawable="@color/lightyellow" />
<item android:state_focused="true"
android:drawable="@color/lightyellow" />
<item android:state_pressed="true"
android:drawable="@color/lightyellow" />
</selector>
及其应用的color.xml(values文件夹下)
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="white">#ffffff</color>
<color name="black">#000000</color>
<color name="lightyellow">#FFEBCD</color>
</resources>
list_item懒得贴了,没有什么图标啥的就是一个光秃秃的TextView。
自定义的Adapter如下
package com.ldgforever.jianreader;
import java.io.File;
import java.util.List;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;
public class MyFileAdapter extends BaseAdapter {
private LayoutInflater mInflater;
private List<String> items;
private List<String> paths;
public MyFileAdapter(Context context, List<String> items, List<String> paths) {
mInflater = LayoutInflater.from(context);
this.items = items;
this.paths = paths;
}
public int getCount() {
return items.size();
}
public Object getItem(int position) {
return items.get(position);
}
public long getItemId(int position) {
return position;
}
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
convertView = mInflater.inflate(R.layout.filelist_item, null);
holder = new ViewHolder();
holder.mFileTextView = (TextView) convertView.findViewById(.list_text);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
File f = new File(paths.get(position).toString());
if (items.get(position).toString().equals("b1")) {
holder.mFileTextView.setText("返回根目录..");
} else if (items.get(position).toString().equals("b2")) {
holder.mFileTextView.setText("返回上一层..");
} else {
if (f.isDirectory()) {
holder.mFileTextView.setText("+ "+f.getName());
} else {
holder.mFileTextView.setText(f.getName());
}
}
return convertView;
}
private class ViewHolder {
TextView mFileTextView;
}
}
最后就路径添加的Activity了,若点击的为目录,则用ListView显示当前目录下的所有文件;反之就出现选中,背景颜色改变。点击确定后,将获取的选定的文件路径传回BookListActivity。若点击取消则直接finish掉当前Activity。
package com.ldgforever.jianreader;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import .ListActivity;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
public class MyFileManager extends ListActivity {
private List<String> items = null;
private List<String> paths = null;
private String rootPath = "/";
private String curPath = "/";
private TextView mPath;
private MyFileAdapter adapter;
private boolean mItemFlag = false;
@Override
protected void onCreate(Bundle bundle) {
super.onCreate(bundle);
setContentView(R.layout.activity_fileselect_list);
mPath = (TextView) findViewById(.tv_path);
Button buttonConfirm = (Button) findViewById(.btn_yes);
buttonConfirm.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
if (mItemFlag) {
Intent data = new Intent(MyFileManager.this, BookListActivity.class);
data.putExtra("file", curPath);
Log.d("com.ldgforever.jianreader", curPath);
setResult(2, data);
finish();
} else {
Toast.makeText(MyFileManager.this, "请选择一个txt文件!", 0).show();
}
}
});
Button buttonCancle = (Button) findViewById(.btn_cancel);
buttonCancle.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
finish();
}
});
getFileDir(rootPath);
}
/**
* 如果文件是目录,则将目录下的文件显示在listview中
*
* @param filePath
*/
private void getFileDir(String filePath) {
mPath.setText(filePath);
items = new ArrayList<String>();
paths = new ArrayList<String>();
File f = new File(filePath);
adapter = new MyFileAdapter(this, items, paths);
if (f.isDirectory()) {
File[] files = f.listFiles();
if (!filePath.equals(rootPath)) {
items.add("b1");
paths.add(rootPath);
items.add("b2");
paths.add(f.getParent());
}
if (files != null) {
for (int i = 0; i < files.length; i++) {
File file = files[i];
items.add(file.getName());
paths.add(file.getPath());
}
}
}
setListAdapter(adapter);
}
@Override
protected void onListItemClick(ListView l, View v, int position, long id) {
if (!mItemFlag) {
v.setBackgroundColor(getResources().getColor(R.color.lightyellow));
mItemFlag = true;
} else {
v.setBackgroundColor(getResources().getColor(R.color.white));
mItemFlag = false;
}
File file = new File(paths.get(position));
if (file != null) {
if (file.isDirectory()) {
curPath = paths.get(position);
getFileDir(paths.get(position));
} else {
curPath = paths.get(position);
}
}
}
}