失眠网,内容丰富有趣,生活中的好帮手!
失眠网 > Android文本阅读器 SD卡文本阅读

Android文本阅读器 SD卡文本阅读

时间:2021-11-06 10:40:29

相关推荐

Android文本阅读器 SD卡文本阅读

文本阅读器,区别于小说阅读器,不能读取大容量的文件。文件容量过大,就会导致读取的速度变得很慢。

读取一个文本文件需要使用BufferedReader + FileReader来逐行读取内容。

还需要准备一个滚动面板配合TextView来显示内容。

如果文件比较大,读取时间会比较长,就需要使用ProgressDialog来建立进度条,提示用户当前正在加载数据。

注:这里读取文件的必须要转码;因为Android的底层是Linux开发,而Linux默认编码是“UTF-8”,而windows建立的文本文件默认是“简体中文(GBK)”,所以Android中打开的文本文件通常需要转码。 InputStreamReader(new FileInputStream(file),"GBK");

首先,我们看下整体的实现效果:

注:这里设置只显示了SD卡中的文件夹以及文本文件,这是通过扩展名的判断实现的

实行步骤:第一步:

一、创建activity_main布局:

<LinearLayout xmlns:android="/apk/res/android"xmlns:tools="/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical" ><TextViewandroid:id="@+id/title_text"android:layout_width="match_parent"android:layout_height="0dp"android:layout_weight="1"android:gravity="center_vertical"android:text="当前位置: /mnt/sdcard"android:textSize="14sp" /><ListViewandroid:id="@+id/list"android:layout_width="match_parent"android:layout_height="0dp"android:layout_weight="8"android:cacheColorHint="#00000000" ></ListView></LinearLayout>

二、创建给列表添加数据的file_line.xml布局:两个TextView,一个是显示文件夹图片的,一个事显示文件夹名字的

<LinearLayout xmlns:android="/apk/res/android"xmlns:tools="/tools"android:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="horizontal" ><TextViewandroid:id="@+id/file_img"android:layout_width="0dp"android:layout_height="wrap_content"android:layout_weight="1" /><TextViewandroid:id="@+id/file_name"android:layout_width="0dp"android:layout_height="wrap_content"android:layout_weight="4"android:textSize="14sp" /></LinearLayout>

三、创建显示文本的activity

<ScrollView xmlns:android="/apk/res/android"xmlns:tools="/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:background="#ffffff" ><TextViewandroid:id="@+id/detail"android:layout_width="match_parent"android:layout_height="wrap_content"android:textColor="#000000"android:textSize="14sp" /></ScrollView>

四、布局写完后,下面是老样子,先编写一个Globals的公共类。

用来动态计算手机的高度和宽度,等比放大缩小。还有初始化所有扩展名和图片的对应关系

import java.util.HashMap;import java.util.Map;import org.liky.txt.R;import android.app.Activity;public class Globals {public static int SCREEN_WIDTH;public static int SCREEN_HEIGHT;// 建立一个Map集合, 里面封装了所有扩展名对应的图标图片, 以便进行文件图标的显示public static Map<String, Integer> allIconImgs = new HashMap<String, Integer>();public static void init(Activity a) {SCREEN_WIDTH = a.getWindowManager().getDefaultDisplay().getWidth();SCREEN_HEIGHT = a.getWindowManager().getDefaultDisplay().getHeight();// 初始化所有扩展名和图片的对应关系allIconImgs.put("txt", R.drawable.txt_file);allIconImgs.put("mp3", R.drawable.mp3_file);allIconImgs.put("mp4", R.drawable.mp4_file);allIconImgs.put("bmp", R.drawable.image_file);allIconImgs.put("gif", R.drawable.image_file);allIconImgs.put("png", R.drawable.image_file);allIconImgs.put("jpg", R.drawable.image_file);allIconImgs.put("dir_open", R.drawable.open_dir);allIconImgs.put("dir_close", R.drawable.close_dir);}}

五、创建一个自定义FileAdapter,用于读取,显示界面的列表中的数据,包括通过扩展名取得的图片和文件名:

import java.util.ArrayList;import java.util.List;import java.util.Map;import org.liky.txt.R;import org.liky.txt.util.Globals;import android.content.Context;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.AbsListView.LayoutParams;import android.widget.BaseAdapter;import android.widget.TextView;public class FileAdapter extends BaseAdapter {private Context ctx;private List<Map<String, Object>> allValues = new ArrayList<Map<String, Object>>();public FileAdapter(Context ctx, List<Map<String, Object>> allValues) {this.ctx = ctx;this.allValues = allValues;}@Overridepublic int getCount() {return allValues.size();}@Overridepublic Object getItem(int arg0) {return allValues.get(arg0);}@Overridepublic long getItemId(int position) {return position;}@Overridepublic View getView(int position, View convertView, ViewGroup parent) {if (convertView == null) {convertView = LayoutInflater.from(ctx).inflate(R.layout.file_line,null);// 设置高度convertView.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, Globals.SCREEN_HEIGHT / 9));}// 取得组件TextView fileImg = (TextView) convertView.findViewById(R.id.file_img);fileImg.getLayoutParams().height = Globals.SCREEN_HEIGHT / 9;TextView fileName = (TextView) convertView.findViewById(R.id.file_name);// 取得数据,设置到组件里Map<String, Object> map = allValues.get(position);// 设置内容, 文字fileName.setText(map.get("fileName").toString());// 图片要根据扩展名取得String extName = map.get("extName").toString();// 取得图片的idint imgId = Globals.allIconImgs.get(extName);// 设置图片fileImg.setBackgroundResource(imgId);return convertView;}}

五、创建mainActivity,显示的是activity_main.xml,这中包含一个点击事件,一个退出按钮的监听,还有对扩展名的截取,只显示文件夹和文本文件

package org.liky.txt;import java.io.File;import java.util.ArrayList;import java.util.HashMap;import java.util.List;import java.util.Map;import org.liky.txt.adapter.FileAdapter;import org.liky.txt.util.Globals;import android.app.Activity;import android.app.AlertDialog.Builder;import android.content.DialogInterface;import android.content.DialogInterface.OnClickListener;import android.content.Intent;import android.os.Bundle;import android.os.Environment;import android.view.KeyEvent;import android.view.View;import android.widget.AdapterView;import android.widget.AdapterView.OnItemClickListener;import android.widget.AdapterView.OnItemLongClickListener;import android.widget.ListView;import android.widget.TextView;public class MainActivity extends Activity {private TextView titleText;private ListView list;private FileAdapter adapter;private List<Map<String, Object>> allValues = new ArrayList<Map<String, Object>>();@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);Globals.init(this);setContentView(R.layout.activity_main);// 取得组件titleText = (TextView) findViewById(R.id.title_text);list = (ListView) findViewById(R.id.list);// 准备数据// 取得SD卡根目录File root = Environment.getExternalStorageDirectory();loadFileData(root);// 建立Adapteradapter = new FileAdapter(this, allValues);list.setAdapter(adapter);// 加入监听事件list.setOnItemClickListener(new OnItemClickListener() {@Overridepublic void onItemClick(AdapterView<?> arg0, View arg1, int arg2,long arg3) {// 取得当前操作的数据Map<String, Object> map = allValues.get(arg2);// 判断所点的是文件还是文件夹boolean dirFlag = (Boolean) map.get("dirFlag");if (dirFlag) {// 文件夹// 建立该文件夹的File对象// 取得绝对路径String fullPath = (String) map.get("fullPath");// 建立FileFile dir = new File(fullPath);// 先清空原有数据allValues.clear();if (!Environment.getExternalStorageDirectory().getAbsolutePath().equals(fullPath)) {// 加入返回上一级的操作行Map<String, Object> parent = new HashMap<String, Object>();parent.put("fileName", "返回上一级");parent.put("extName", "dir_open");parent.put("dirFlag", true);parent.put("fullPath", dir.getParent());// 保存一个标志parent.put("flag", "TRUE");// 将这一行加入到数据集合中allValues.add(parent);}// 加入新数据loadFileData(dir);// 使用Adapter通知界面ListView,数据已经被修改了,你也要一起改adapter.notifyDataSetChanged();} else {// 弹出该文件的详细信息File f = new File((String) map.get("fullPath"));// 切换界面Intent in = new Intent(MainActivity.this,DetailActivity.class);in.putExtra("fullPath", map.get("fullPath").toString());startActivity(in);}}});list.setOnItemLongClickListener(new OnItemLongClickListener() {@Overridepublic boolean onItemLongClick(AdapterView<?> arg0, View arg1,final int arg2, long arg3) {// 取得数据Map<String, Object> map = allValues.get(arg2);final File f = new File(map.get("fullPath").toString());if (f.isFile()) {// 弹出确认框Builder builder = new Builder(MainActivity.this);builder.setTitle("提示");builder.setMessage("确定要删除该文件(" + f.getName() + ")吗?");builder.setPositiveButton("确定", new OnClickListener() {@Overridepublic void onClick(DialogInterface dialog, int which) {// 将SD卡中的文件删除if (f.exists()) {f.delete();}// 将列表中的数据删除allValues.remove(arg2);// 通知adapter.notifyDataSetChanged();}});builder.setNegativeButton("取消", new OnClickListener() {@Overridepublic void onClick(DialogInterface dialog, int which) {}});builder.create().show();}return false;}});}@Overridepublic boolean onKeyDown(int keyCode, KeyEvent event) {// 根据keyCode判断用户按下了哪个键if (keyCode == KeyEvent.KEYCODE_BACK) {// 判断当前是否在SD卡跟目录.// 取得第一行数据Map<String, Object> map = allValues.get(0);if ("TRUE".equals(map.get("flag"))) {// 里面,需要返回上一级list.performItemClick(list.getChildAt(0), 0, list.getChildAt(0).getId());} else {// 弹出提示框Builder builder = new Builder(MainActivity.this);builder.setTitle("提示");builder.setMessage("亲,真的要离开我吗?");builder.setPositiveButton("真的", new OnClickListener() {@Overridepublic void onClick(DialogInterface dialog, int which) {// 关闭当前Activityfinish();}});builder.setNegativeButton("再待会儿", new OnClickListener() {@Overridepublic void onClick(DialogInterface dialog, int which) {}});builder.create().show();}return false;}return super.onKeyDown(keyCode, event);}private void loadFileData(File dir) {// 列出该目录下的所有文件File[] allFiles = dir.listFiles();// 设置当前位置的提示信息titleText.setText("当前位置: " + dir.getAbsolutePath());// 判断if (allFiles != null) {// 循环for (int i = 0; i < allFiles.length; i++) {File f = allFiles[i];Map<String, Object> map = new HashMap<String, Object>();map.put("fileName", f.getName());// 多保存一个文件的绝对路径,方便在进行点击时使用map.put("fullPath", f.getAbsolutePath());// 判断是文件夹还是文件if (f.isDirectory()) {// 是文件夹map.put("extName", "dir_close");map.put("dirFlag", true);} else {// 是文件// 截取出扩展名String extName = f.getName().substring(f.getName().lastIndexOf(".") + 1).toLowerCase();map.put("extName", extName);map.put("dirFlag", false);}// 只有文件夹或文本文件才要显示.if (f.isDirectory() || "txt".equals(map.get("extName"))) {allValues.add(map);}}}}}

六、建立一个DetailActivity的用以读取文本文件,和显示进度条。使用的是ProgressDialog来完成进度条的显示,但同时要加入线程操作。

需要建立一个Handler类,来处理消息接收的功能。

注:测试时提示错误,ANdroid只允许主线程对UI界面显示的内容进行修改,子线程不允许修改界面。

但这里必须在子线程对界面进行修改,这种情况Android是通过消息通道机制来解决的。

package org.liky.txt;import java.io.BufferedReader;import java.io.File;import java.io.FileInputStream;import java.io.InputStreamReader;import android.app.Activity;import android.app.ProgressDialog;import android.os.Bundle;import android.os.Handler;import android.os.Message;import android.widget.TextView;public class DetailActivity extends Activity {private TextView detail;// 声明这个Handler类private Handler handler;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_detail);detail = (TextView) findViewById(R.id.detail);// 建立这个handler对象,并覆写handleMessage方法,该方法会在接收到子线程的消息时自动执行,进行界面修改handler = new Handler() {@Overridepublic void handleMessage(Message msg) {// msg就是子线程发送过来的消息// 修改text内容detail.setText(msg.obj.toString());}};// 建立进度条final ProgressDialog dialog = new ProgressDialog(this);// 设置为水平进度条dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);dialog.setTitle("提示");dialog.setMessage("正在加载数据, 请稍候...");// 设置最大值File f = new File(getIntent().getStringExtra("fullPath"));dialog.setMax((int) f.length());dialog.show();Thread t = new Thread() {@Overridepublic void run() {// 接收参数,并根据参数建立文件对象File f = new File(getIntent().getStringExtra("fullPath"));// 使用IO流读取try {BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(f), "GBK"));String line = null;StringBuilder builder = new StringBuilder();// 定义保存读取内容大小的变量int size = 0;while ((line = reader.readLine()) != null) {builder.append(line + "\r\n");// 当读取了一部分内容后,根据读取内容的大小,然后增长进度size += line.getBytes().length;// 每增加超过10k的内容,就改变一次进度条if (size >= 10240) {dialog.incrementProgressBy(size);size = 0;// 睡眠一段时间Thread.sleep(5);}}reader.close();// detail.setText(builder.toString());Message msg = new Message();// 将builder中的内容设置到msg里msg.obj = builder;// 发送消息handler.sendMessage(msg);// 关闭进度条dialog.dismiss();} catch (Exception e) {e.printStackTrace();}}};t.start();}}

Android中经典的两句话:不允许主线程联网,不允许子线程更改界面;主线程要联网必须通过子线程,子线程要修改界面,必须告知主线程。

如果觉得《Android文本阅读器 SD卡文本阅读》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。