不知怎么突然有了想写一个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://schemas.android.com/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 android.app.*; 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() { android.app.AlertDialog.Builder builder = new android.app.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(R.id.booklist); mListView.setChoiceMode(3); // Multi mListView.setMultiChoiceModeListener(mCallback); mListView.setAdapter(adapter); } else { failAddingDialog(); return; } } /** * 添加书籍失败对话框 */ private void failAddingDialog() { android.app.AlertDialog.Builder builder = new android.app.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 R.id.action_addingbooks: BookAddingDialog(); break; default: break; } return true; } private class MultiModeCallback implements MultiChoiceModeListener { public boolean onActionItemClicked(ActionMode actionmode, MenuItem menuitem) { switch (menuitem.getItemId()) { case R.id.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.http://blog.csdn.net/xyz_lmn/article/details/12754785
2.http://blog.csdn.net/ghost_programmer/article/details/46827933 (强推)
然后就是点击通过路径添加之后通过startForResult打开路径添加的Activity
嚄……贴一下actionmode的menu,里面只有一个删除的按钮
<?xml version="1.0" encoding="utf-8"?> <menu xmlns:android="http://schemas.android.com/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(R.id.book_name); mViewHolder.mCheckBox = (CheckBox) view.findViewById(R.id.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; } }
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; } }
然后0.0就到了选择路径添加的部分……首先来看一下布局文件
一个用来显示当前目录路径的TextView,一个ListView列出可供选择的文件,线性布局的最下方是确定和取消两个按钮。这里还应用了Selector来改变点击ListView的item时的背景颜色,为了某种程度上的美观……?通过android:listSelector = "@drawable/mfilelist_view"设置
<?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: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://schemas.android.com/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(R.id.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; } }
package com.ldgforever.jianreader; import java.io.File; import java.util.ArrayList; import java.util.List; import android.app.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(R.id.tv_path); Button buttonConfirm = (Button) findViewById(R.id.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(R.id.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); } } } }