zoukankan      html  css  js  c++  java
  • 【Android】内存卡图片读取器,图库app

    上一篇《【Android】读取sdcard卡上的全部图片而且显示,读取的过程有进度条显示》(点击打开链接)在真机上測试非常有问题。常常遇到内存溢出。卡死的情况。由于如今真机上的内存上,2G已经非常少见了,基本上都8G的样子了。

    由于把读取出来的图片一次性地放到app上,而且读取的过程中,又没有正在读取到哪个文件,尽管可以在AVD安卓模拟器上完毕主要的功能,可是这个app非常不友好。

    因此採用Handler、Message配合线程等安卓消息机制。完毕读取过程,而且利用GridView把读取到的图片显示出来。改进这个内存卡图片读取器,详细效果例如以下,基本是可以媲美部分安卓系统自带的图库了吧……就差个对目录的分类而已了……


    一開始。在读取的过程。有读取进度的显示,详细读取到哪个目录。

    这对于大内存卡非常有意义。我的8G内存卡亲測,仅30秒完毕读取。

    之后,用网格视图把图片显示出来。点击图片能够查看图片的大图。与图片的路径。

    在app的右上角能够退出。

    当然本app也完毕对返回键的监听。

    返回键也可以退出。

    这个在《【Android】各式各样的弹出框与对菜单键、返回键的监听》(点击打开链接)已经说过了,这里不再赘述。

    1、首先改动resvaluesstrings.xml,设置各个字符,例如以下。作者什么的,请忽略这些细节!

    <?xml version="1.0" encoding="utf-8"?

    > <resources> <string name="app_name">内存卡图片查看器</string> <string name="menu_author">作者:yongh701</string> <string name="menu_exit">退出</string> <string name="img_description">大图</string> <string name="view_button1">返回</string> </resources>

    2、之后改动菜单的字符文件resmenumain.xml例如以下,一个配置到MainActivity.java其中,这个在《【Android】日期拾取器、时间拾取器与菜单》(点击打开链接)已经说过了。这里不再赘述。

    <menu xmlns:android="http://schemas.android.com/apk/res/android" >
    
        <item
            android:id="@+id/menu_exit"
            android:title="@string/menu_exit"/>
        <item android:title="@string/menu_author"/>
    
    </menu>
    3、随后完毕AndroidManifest.xml的改动。要求系统对本app赋予SDCard的读写权限。同一时候。由于app查看大图要用还有一个Activity,这里同一时候在这里注冊查看大图的ViewActivity。该文件改动例如以下:

    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.sdcard_read_all"
        android:versionCode="1"
        android:versionName="1.0" >
    
        <uses-sdk
            android:minSdkVersion="8"
            android:targetSdkVersion="18" />
    
        <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <!-- 要求向SDCard读取数据权限 -->
        <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <!-- 要求向SDCard写入数据权限 -->
    
        <application
            android:allowBackup="true"
            android:icon="@drawable/ic_launcher"
            android:label="@string/app_name"
            android:theme="@style/AppTheme" >
            <activity
                android:name="com.sdcard_read_all.MainActivity"
                android:label="@string/app_name" >
                <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
    
                    <category android:name="android.intent.category.LAUNCHER" />
                </intent-filter>
            </activity>
            <activity
                android:name="com.sdcard_read_all.ViewActivity"
                android:label="@string/img_description" > <!-- 注冊ViewActivity -->
            </activity>
        </application>
    
    </manifest>
    4、事实上之后的过程,与《【Android】图片资源的訪问与网格式图片浏览器》(点击打开链接)是一样的。先改动MainActivity的布局文件reslayoutactivity_main.xml,布置一个网格布局GridView。当然,同一时候布置一个TextView,用于在读取完毕之前。显示读取信息。此文件改动之后例如以下:

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical" >
    
        <TextView
            android:id="@+id/textView1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textSize="36sp" />
    
        <GridView
            android:id="@+id/gridView1"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:numColumns="2" >
        </GridView>
    
    </LinearLayout>
    5、同一时候在reslayout新建一个查看大图的的ViewActivity的布局文件activity_view.xml。这里和《【Android】画廊式的图片浏览器,使用HorizontalScrollView代替Gallery,OnClickListener的參数传递》(点击打开链接)同理。仅仅只是是在一个垂直滚动布局下。新建一个线性子布局LinearLayout。这样就不用操心组件的超过一屏,能够达到滚动效果。线性布局的滚动栏就是这样做的。三个组件皆沾满屏幕宽度,同一时候分别赋予id,一会儿通过ViewActivity.java赋予对应的值。当中图片视图ImageView配置的adjustViewBounds与scaleType。让其自己主动缩放大小。

    android:contentDescription="@string/img_description"是图片的描写叙述,不赋予。Eclipse会出现警告。

    <ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scrollbars="vertical" >
    
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical" >
    
            <TextView
                android:id="@+id/textView1"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:textSize="24sp" />
    
            <ImageView
                android:id="@+id/imageView1"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:adjustViewBounds="true"
                android:scaleType="centerInside"
                android:paddingTop="10dp"
                android:paddingBottom="10dp"
                android:contentDescription="@string/img_description" />
    
            <Button
                android:id="@+id/button1"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="@string/view_button1"
                android:textSize="24sp" />
        </LinearLayout>
    
    </ScrollView>

    6、在src当前project包下(这里是com.sdcard_read_all)中。如《【Android】多个Activity之间利用bundle传递数值》(点击打开链接)一样,新建一个继承android.app.Activity的ViewActivity.java。里面代码例如以下:

    package com.sdcard_read_all;
    
    import android.app.Activity;
    import android.content.Intent;
    import android.graphics.Bitmap;
    import android.graphics.BitmapFactory;
    import android.os.Bundle;
    import android.view.KeyEvent;
    import android.view.View;
    import android.view.View.OnClickListener;
    import android.widget.Button;
    import android.widget.ImageView;
    import android.widget.TextView;
    
    public class ViewActivity extends Activity {
    	private TextView textView1;
    	private ImageView imageView1;
    	private Button button1;
    
    	@Override
    	protected void onCreate(Bundle savedInstanceState) {
    		super.onCreate(savedInstanceState);
    		setContentView(R.layout.activity_view);
    		//获取组件
    		textView1 = (TextView) findViewById(R.id.textView1);
    		imageView1 = (ImageView) findViewById(R.id.imageView1);
    		button1 = (Button) findViewById(R.id.button1);
    		//得到MainActivity传递过来的图片路径,进行对应的处理。
    		Intent intent = getIntent();
    		Bundle bundle = intent.getExtras();
    		String imgPath = bundle.getString("imgPath");
    		textView1.setText("图片路径:" + imgPath);
    		Bitmap bm = BitmapFactory.decodeFile(imgPath);
    		imageView1.setImageBitmap(bm);
    		//按钮的返回事件
    		button1.setOnClickListener(new OnClickListener() {
    
    			@Override
    			public void onClick(View arg0) {
    				finish();
    			}
    		});
    
    	}
    
    	// 对物理按钮的监听
    	@Override
    	public boolean onKeyDown(int keyCode, KeyEvent event) {
    		switch (keyCode) {
    		case KeyEvent.KEYCODE_BACK:
    			finish();// 关闭这个Activity。
    			break;
    		}
    		return super.onKeyDown(keyCode, event);
    	}
    }
    
    7、完毕了查看大图ViewActivity之后,便是我们的压轴大戏MainActivity,MainActivity也是整个程序的核心,代码例如以下。代码在结构上分为五部分:1、遍历sdcard的方法。2、载入图片的缓存工具方法,这两个方法与《【Android】读取sdcard卡上的全部图片而且显示。读取的过程有进度条显示》(点击打开链接)是相同的。

    3、程序的运行onCreate方法,4、app的菜单,这《【Android】日期拾取器、时间拾取器与菜单》(点击打开链接)已经说过了。

    5、对物理按键的监听。这能够參考《【Android】各式各样的弹出框与对菜单键、返回键的监听》(点击打开链接)。

    程序的入口与其他安卓程序相同为onCreate方法。

    程序的运行将会分成两部分:

    1、读取时候。开一条读取进行,利用安卓的线程消息传递机制。不停在更新textView1。这与《【Android】进度条与线程之间的消息处理》(点击打开链接)事实上是同理。仅仅能这样完毕更新,通过安卓的线程消息传递机制,不至于app会出现死机的线程。安卓系统会自己主动对程序上的线程进行资源良好调度与分配。

    之前的程序就由于对线程的处理不好,导致读大内存卡会卡死。如今通过安卓的线程消息传递机制读多大的内存卡也是没事的!

    2、读取完成,与《【Android】图片资源的訪问与网格式图片浏览器》(点击打开链接)相同。利用适配器与网格视图,把全部图片显示。这里因为网格视图自带的适配器是自己主动会分配好资源的,因此占用系统资源也不大的。同一时候对网格视图赋予监听,点击图片传递图片路径,并打开查看大图ViewActivity。

    package com.sdcard_read_all;
    
    import java.io.File;
    import java.util.ArrayList;
    
    import android.os.Bundle;
    import android.os.Environment;
    import android.os.Handler;
    import android.os.Message;
    import android.view.KeyEvent;
    import android.view.Menu;
    import android.view.MenuItem;
    import android.view.View;
    import android.view.ViewGroup;
    import android.widget.AdapterView;
    import android.widget.AdapterView.OnItemClickListener;
    import android.widget.BaseAdapter;
    import android.widget.GridView;
    import android.widget.ImageView;
    import android.widget.ImageView.ScaleType;
    import android.widget.TextView;
    import android.app.Activity;
    import android.content.Intent;
    import android.graphics.Bitmap;
    import android.graphics.BitmapFactory;
    
    public class MainActivity extends Activity {
    	private TextView textView1;
    	private GridView gridView1;
    	private static Handler handler;// 线程消息处理器
    	// 用于存放sdcard卡上的全部图片路径
    	public static ArrayList<String> dirAllStrArr = new ArrayList<String>();
    
    	// 用于遍历sdcard卡上全部文件的类
    	public static void DirAll(File dirFile) throws Exception {
    		if (dirFile.exists()) {
    			File files[] = dirFile.listFiles();
    			for (File file : files) {
    				String fileName = file.getName();
    				String filePath = file.getPath();
    				// 读取文件路径,作为信息发送给handler进行处理
    				Message msg = new Message();
    				msg.obj = "正在读取:" + filePath;
    				handler.sendMessage(msg);
    				if (file.isDirectory()) {
    					// 除sdcard上Android这个目录以外。
    					if (!fileName.endsWith("Android")) {
    						// 假设遇到目录则递归调用。

    DirAll(file); } } else { // 假设是图片文件压入数组 if (fileName.endsWith(".jpg") || fileName.endsWith(".jpeg") || fileName.endsWith(".bmp") || fileName.endsWith(".gif") || fileName.endsWith(".png")) { if (dirFile.getPath().endsWith(File.separator)) { dirAllStrArr .add(dirFile.getPath() + file.getName()); } else { dirAllStrArr.add(dirFile.getPath() + File.separator + file.getName()); } } } } } } // 图片载入的缓存工具类,利用安卓自带的方法,依据大小压缩图片 public static BitmapFactory.Options getHeapOpts(File file) { BitmapFactory.Options opts = new BitmapFactory.Options(); // 数字越大读出的图片占用的内存必须越小,不然总是溢出 if (file.length() < 20480) { // 0-20k opts.inSampleSize = 1;// 这里意为缩放的大小 ,数字越多缩放得越厉害 } else if (file.length() < 51200) { // 20-50k opts.inSampleSize = 2; } else if (file.length() < 307200) { // 50-300k opts.inSampleSize = 4; } else if (file.length() < 819200) { // 300-800k opts.inSampleSize = 6; } else if (file.length() < 1048576) { // 800-1024k opts.inSampleSize = 8; } else { opts.inSampleSize = 10; } return opts; } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 获取组件 textView1 = (TextView) findViewById(R.id.textView1); gridView1 = (GridView) findViewById(R.id.gridView1); // 线程開始之前。先把网格视图因此 gridView1.setVisibility(View.INVISIBLE); /* 遍历sdcard旗下的全部目录的线程開始 */ Thread readSdcard = new Thread() { private String sdpath = Environment.getExternalStorageDirectory() .getAbsolutePath();// 获取sdcard的根路径 private File dirFile = new File(sdpath); public void run() { try { DirAll(dirFile);// 遍历sdcard旗下的全部目录 // 完毕之后,在线程自身的消亡之前,发送一条“0”的标志信息。给handler Message msg = new Message(); msg.obj = "0"; handler.sendMessage(msg); } catch (Exception e) { e.printStackTrace(); } }; }; textView1.setVisibility(View.VISIBLE); readSdcard.start();// 线程開始,默认不開始。

    /* 遍历sdcard旗下的全部目录结束 */ // 不停在接受线性的消息,依据消息。进行处理 // 随着线程readSdcard的開始而出现。随其死亡而消亡。与线程是一对的。 handler = new Handler(new Handler.Callback() {// 这样写。就不弹出什么泄漏的警告了 @Override public boolean handleMessage(Message msg) { textView1.setText(msg.obj + ""); if (msg.obj.equals("0")) {// 完毕读取之后 textView1.setVisibility(View.GONE);// 隐藏读取信息 gridView1.setVisibility(View.VISIBLE);// 显示网格视图 } return false; } }); // 网格视图适配器 BaseAdapter baseAdapter = new BaseAdapter() { @Override public View getView(int position, View convertView, ViewGroup arg2) { ImageView imageView1; if (convertView == null) { imageView1 = new ImageView(MainActivity.this); imageView1.setAdjustViewBounds(true);// 自己主动缩放为宽高比 imageView1.setScaleType(ScaleType.CENTER_INSIDE);// 设置图片保持宽高比显示 imageView1.setPadding(5, 5, 5, 5); } else { imageView1 = (ImageView) convertView; } // 把图片载入到网格视图 String filePath = dirAllStrArr.get(position); File file = new File(filePath); Bitmap bm = BitmapFactory.decodeFile(filePath, getHeapOpts(file)); imageView1.setImageBitmap(bm); return imageView1; } // 获取当前选项 @Override public long getItemId(int position) { return position; } @Override public Object getItem(int position) { return position; } // 获取数量 @Override public int getCount() { return dirAllStrArr.size(); } }; gridView1.setAdapter(baseAdapter);// 把适配器与网格视图链接起来 gridView1.setOnItemClickListener(new OnItemClickListener() {// 点击网格组件的随意一张图片时候的事件 @Override public void onItemClick(AdapterView<?> arg0, View arg1, int position,// position为点击的id long arg3) { Intent intent = new Intent(MainActivity.this, ViewActivity.class);// 激活ViewActivity Bundle bundle = new Bundle(); bundle.putString("imgPath", dirAllStrArr.get(position));// 传递点击的图片的id到ViewActivity intent.putExtras(bundle); startActivity(intent); } }); } // 创建menu的方法,没有该方法。不会在右上角设置菜单。 @Override public boolean onCreateOptionsMenu(Menu menu) { // 设置menu界面为resmenumenu.xml getMenuInflater().inflate(R.menu.main, menu); return true; } // 处理菜单事件 public boolean onOptionsItemSelected(MenuItem item) { // 得到当前选中的MenuItem的ID, int item_id = item.getItemId(); switch (item_id) { // 设置id为menu_exit的菜单子项所要运行的方法。 case R.id.menu_exit: System.exit(0);// 结束程序 break; } return true; } // 对物理button的监听 @Override public boolean onKeyDown(int keyCode, KeyEvent event) { switch (keyCode) { case KeyEvent.KEYCODE_BACK: finish();// 关闭这个Activity。 break; } return super.onKeyDown(keyCode, event); } }

    整个制作过程如上所看到的了,我还上传了一份Ecllipse,在ADT的编写源代码,大家有兴趣能够下载来看一下:

    安卓读取SD卡上的全部图片(点击打开链接

  • 相关阅读:
    java编码过滤器
    DAO设计模式
    常用的SQL语句
    IO流总结
    IO流的登录与注册
    设计模式之模板方法模式(Template Method)详解及代码示例
    设计模式之享元模式(Flyweight)详解及代码示例
    设计模式之桥接模式(Bridge)详解及代码示例
    设计模式之组合模式(Composite)详解及代码示例
    设计模式之外观模式(Facade)详解及代码示例
  • 原文地址:https://www.cnblogs.com/gcczhongduan/p/5286968.html
Copyright © 2011-2022 走看看