Bitmap实现照片墙
2015-09-07 17:30
253 查看
Bitmap实现照片墙
Android开发中 位图 的展示 存储 缓存是很重要部分,现代化的用户界面有很多信息都是通过图片形式进行展现,如何更好的展示图片也是衡量一款App 性能和用户体验的重要部分。Training–>Building Apps with Graphics & Animation–>Displaying Bitmaps Efficiently
今天就写一个简单的照片墙应用来实现Android的Bitmap图片展示功能。
目录结构:
1.GridView实现图片展示界面
在activity_main.xml中设置GridView布局
<?xml version="1.0" encoding="utf-8"?> <GridView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/gridview" android:layout_width="match_parent" android:layout_height="match_parent" android:columnWidth="120dp" android:numColumns="auto_fit" android:verticalSpacing="10dp" android:horizontalSpacing="10dp" android:stretchMode="columnWidth" android:gravity="center" />
GridView 设置列数 列的宽度 item横向 纵向间距 和 stretchMode 属性。
在MainActivity中实例化GridView,并实现ImageAdapter
setContentView(R.layout.activity_main); //实例化GridView,并设置adpter gridView = (GridView) findViewById(R.id.gridview); gridView.setAdapter(new ImageAdapter(this));
public class ImageAdapter extends BaseAdapter { private Context context; public ImageAdapter(Context context) { super(); this.context = context; } @Override public int getCount() { return images.length; } @Override public Object getItem(int arg0) { // TODO Auto-generated method stub return null; } @Override public long getItemId(int arg0) { // TODO Auto-generated method stub return 0; } @Override public View getView(int position, View convertView, ViewGroup parent) { LayoutInflater inflater = LayoutInflater.from(context); View v; if (convertView == null) { //若为空,则动态加载一个View v = inflater.inflate(R.layout.grid_item, null); v.setPadding(8, 8, 8, 8); } else { v = convertView; } ImageView imageView = (ImageView) v.findViewById(R.id.item_image); // 1.imageView.setImageResource(images[position]); // 2.imageView.setImageBitmap(BitmapUtils.decodeSampledBitmapFromResource(getResources(), // images[position], 220, 220)); //3.loadImageView(imageView, images[position]); loadImageViewFromCache(imageView, images[position]); TextView textview = (TextView) v.findViewById(R.id.item_text); textview.setText(images[position] + ""); return v; } private Integer[] images = new Integer[] { R.drawable.krystal001, R.drawable.krystal002, R.drawable.krystal003, R.drawable.krystal004, R.drawable.krystal005, R.drawable.krystal006, R.drawable.krystal007, R.drawable.krystal008, R.drawable.krystal009, R.drawable.krystal010, R.drawable.krystal011, R.drawable.krystal012, R.drawable.krystal013, R.drawable.krystal014 }; }
3.设置每个Item的布局 grid_item.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="120dp" android:layout_height="120dp" android:orientation="vertical" > <ImageView android:id="@+id/item_image" android:layout_width="110dp" android:layout_height="110dp" android:layout_gravity="center_horizontal" android:padding="5dp" android:scaleType="centerCrop" android:src="@drawable/ic_launcher"> </ImageView> <TextView android:id="@+id/item_text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:text="try" android:textColor="#ff0000"> </TextView> </LinearLayout>
4.现在已经可以展示图片,不过默认是加载了源文件到内存,耗费内存资源,当图片比较多时间,可能会出现
OutOfMemory exception这里需要运用技巧加载大位图到实际需要的尺寸。
Loading Large Bitmaps Efficiently
运用BitmapFactory.options,来计算原位图的尺寸和需要的尺寸,进行压缩加载。
BitmapUtils.java
package com.example.utils; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.BitmapFactory.Options; public class BitmapUtils { /** * 从资源文件加载大位图,从字符数组 文件也一样 * @param res 资源引用 * @param id 资源ID * @param image_width 需要的图片宽度 * @param image_height 需要的图片高度 * @return bitmap位图对象 */ public static Bitmap decodeSampledBitmapFromResource(Resources res, int id, int image_width, int image_height){ Bitmap bitmap=null; BitmapFactory.Options options=new BitmapFactory.Options(); //不会分配内存,只是检查编码尺寸 options.inJustDecodeBounds=true; BitmapFactory.decodeResource(res, id, options); //,把options传入,decode一次可以计算出原文件的尺寸信息,计算并设置insampleSize options.inSampleSize = calculateInSampleSize(options, image_width, image_height); //设置完inSampleSize(压缩倍数),置inJustDecodeBounds为false,编码读入内存 options.inJustDecodeBounds=false; bitmap=BitmapFactory.decodeResource(res, id, options); return bitmap; } /** * 计算压缩的倍数 * @param options BitmapFactory.options 对象 * @param image_width 需要的图片宽度 * @param image_height 需要的图片高度 * @return InSampleSize 压缩倍数 */ private static int calculateInSampleSize(Options options, int image_width, int image_height) { //源文件的尺寸 final int width=options.outWidth; final int height=options.outHeight; //压缩的倍数 int inSampleSize=1; if (height > image_width || width > image_height) { final int halfHeight = height / 2; final int halfWidth = width / 2; // Calculate the largest inSampleSize value that is a power of 2 and keeps both // height and width larger than the requested height and width. while ((halfHeight / inSampleSize) > image_width && (halfWidth / inSampleSize) > image_height) { inSampleSize *= 2; } } return inSampleSize; } }
5.在UI主线程加载Bitmap显然不是明智的选择,这里用AsyncTask进行异步加载实现
写成内部类应该会简单一点,不用传那么多参数了,只不过看起来乱糟糟的
BitmapWorkerTask.java
package com.example.utils; import java.lang.ref.WeakReference; import android.content.Context; import android.graphics.Bitmap; import android.os.AsyncTask; import android.util.LruCache; import android.widget.ImageView; public class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> { // 貌似是虛引用,用空在看吧 private final WeakReference<ImageView> imageViewReference; private int res_id = 0; Context context; LruCache<String, Bitmap> cache; public BitmapWorkerTask(ImageView imageView, Context context,LruCache<String, Bitmap> cache) { // Use a WeakReference to ensure the ImageView can be garbage collected imageViewReference = new WeakReference<ImageView>(imageView); this.context = context; this.cache=cache; } @Override protected Bitmap doInBackground(Integer... arg0) { res_id = arg0[0]; Bitmap bitmap= BitmapUtils.decodeSampledBitmapFromResource( context.getResources(), res_id, 220, 220); //因为BitmapWorkerTask不像写成activity中的内部类,看起来很冗余,所以这里要用原生put(K,V) //写在activity构造函数不用传来res 和 cache,可以调用封装的addBitmapToMemoryCache() String key=String.valueOf(res_id); if(cache.get(key)==null){ cache.put(key, bitmap); } return bitmap; } @Override protected void onPostExecute(Bitmap result) { if (imageViewReference != null && result != null) { final ImageView imageView = imageViewReference.get(); if (imageView != null) { imageView.setImageBitmap(result); } } } }
ImageAdapter添加以下方法
/** * 异步加载位图 * * @param imageView * 要展示的imageView * @param res_id * 资源ID */ private void loadImageView(ImageView imageView, int res_id) { BitmapWorkerTask task = new BitmapWorkerTask(imageView, MainActivity.this, mMemoryCache); task.execute(res_id); }
6.添加缓存
在ListView 或者 GridView中图片是动态加载,但是也可能刚才销毁的,优于用户滚动屏幕,item又需要被重新生成,每次都重新生成对于资源是一种浪费,同时也会影响程序的性能表现。所以缓存就很重要一种解决问题的手段。
缓存重要分为两种:
Lrucache 内存缓存
disk cache 磁盘缓存
这里先只说内存缓存Lrucache,磁盘缓存晚上在研究。
LruCache 緩存 ,基于LRU算法,数据结构为 LinkedHashMap
// LruCache 緩存 ,基于LRU算法,数据结构为 LinkedHashMap private LruCache<String, Bitmap> mMemoryCache;
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //实例化GridView,并设置adpter gridView = (GridView) findViewById(R.id.gridview); gridView.setAdapter(new ImageAdapter(this)); // 获得虚拟环境的最大内存,也就是当前程序可以占用最大内存,一个应用对应一个davilk虚拟环境 // Get max available VM memory, exceeding this amount will throw an // OutOfMemory exception. Stored in kilobytes as LruCache takes an // int in its constructor. final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024); // 用最大内存的八分之一作为缓存的容量 // Use 1/8th of the available memory for this memory cache. final int cacheSize = maxMemory / 8; mMemoryCache = new LruCache<String, Bitmap>(cacheSize) { @Override protected int sizeOf(String key, Bitmap bitmap) { // The cache size will be measured in kilobytes rather than // number of items. // 以KB为单位 return bitmap.getByteCount() / 1024; } }; ..... }
/** * 添加到位图到缓存,对put(K,V)的封装 * * @param key * @param bitmap */ public void addBitmapToMemoryCache(String key, Bitmap bitmap) { if (getBitmapFromMemCache(key) == null) { mMemoryCache.put(key, bitmap); } } /** * 从缓存得到位图,对get(K)的封装 * * @param key * @return */ public Bitmap getBitmapFromMemCache(String key) { return mMemoryCache.get(key); }
ImageAdapter 加入一下方法
/** * 从缓存中加载位图 * * @param imageView * 目标ImageView * @param resId * 资源ID */ private void loadImageViewFromCache(ImageView imageView, int resId) { final String imageKey = String.valueOf(resId); // 从缓存获取位图 final Bitmap bitmap = getBitmapFromMemCache(imageKey); if (bitmap != null) { imageView.setImageBitmap(bitmap); } else { // 默认图片资源,当没加载出来不会是空白 imageView.setImageResource(R.drawable.krystal_palceholder); BitmapWorkerTask task = new BitmapWorkerTask(imageView, MainActivity.this, mMemoryCache); task.execute(resId); } }
7.程序运行结果
disk cache 和 Managing Bitmap Memory需要研究一下。
MainActivity.java 完整的源代码
package com.example.gridview;
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.os.Bundle;
import android.util.LruCache;
import android.view.LayoutInflater;
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.AdapterView.OnItemLongClickListener;
import android.widget.BaseAdapter;
import android.widget.GridView;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import com.example.utils.BitmapWorkerTask;
public class MainActivity extends Activity {
private GridView gridView;
// LruCache 緩存 ,基于LRU算法,数据结构为 LinkedHashMap
private LruCache<String, Bitmap> mMemoryCache;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//实例化GridView,并设置adpter
gridView = (GridView) findViewById(R.id.gridview);
gridView.setAdapter(new ImageAdapter(this));
// 获得虚拟环境的最大内存,也就是当前程序可以占用最大内存,一个应用对应一个davilk虚拟环境
// Get max available VM memory, exceeding this amount will throw an
// OutOfMemory exception. Stored in kilobytes as LruCache takes an
// int in its constructor.
final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
// 用最大内存的八分之一作为缓存的容量
// Use 1/8th of the available memory for this memory cache.
final int cacheSize = maxMemory / 8;
mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
@Override
protected int sizeOf(String key, Bitmap bitmap) {
// The cache size will be measured in kilobytes rather than
// number of items.
// 以KB为单位
return bitmap.getByteCount() / 1024;
}
};
gridView.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
Toast.makeText(MainActivity.this, "开启另一个activity",
Toast.LENGTH_LONG).show();
}
});
gridView.setOnItemLongClickListener(new OnItemLongClickListener() {
@Override
public boolean onItemLongClick(AdapterView<?> parent, View view,
int position, long id) {
Toast.makeText(MainActivity.this, "收藏" + position,
Toast.LENGTH_LONG).show();
return false;
}
});
}
/** * 添加到位图到缓存,对put(K,V)的封装 * * @param key * @param bitmap */ public void addBitmapToMemoryCache(String key, Bitmap bitmap) { if (getBitmapFromMemCache(key) == null) { mMemoryCache.put(key, bitmap); } } /** * 从缓存得到位图,对get(K)的封装 * * @param key * @return */ public Bitmap getBitmapFromMemCache(String key) { return mMemoryCache.get(key); }
/**
* ImageAdapter
* @author QT
*
*/
public class ImageAdapter extends BaseAdapter {
private Context context;
public ImageAdapter(Context context) {
super();
this.context = context;
}
@Override
public int getCount() {
return images.length;
}
@Override
public Object getItem(int arg0) {
// TODO Auto-generated method stub
return null;
}
@Override
public long getItemId(int arg0) {
// TODO Auto-generated method stub
return 0;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
LayoutInflater inflater = LayoutInflater.from(context);
View v;
if (convertView == null) {
//若为空,则动态加载一个View
v = inflater.inflate(R.layout.grid_item, null);
v.setPadding(8, 8, 8, 8);
} else {
v = convertView;
}
ImageView imageView = (ImageView) v.findViewById(R.id.item_image);
// 1.imageView.setImageResource(images[position]);
// 2.imageView.setImageBitmap(BitmapUtils.decodeSampledBitmapFromResource(getResources(),
// images[position], 220, 220));
//3.loadImageView(imageView, images[position]);
loadImageViewFromCache(imageView, images[position]);
TextView textview = (TextView) v.findViewById(R.id.item_text);
textview.setText(images[position] + "");
return v;
}
/** * 从缓存中加载位图 * * @param imageView * 目标ImageView * @param resId * 资源ID */ private void loadImageViewFromCache(ImageView imageView, int resId) { final String imageKey = String.valueOf(resId); // 从缓存获取位图 final Bitmap bitmap = getBitmapFromMemCache(imageKey); if (bitmap != null) { imageView.setImageBitmap(bitmap); } else { // 默认图片资源,当没加载出来不会是空白 imageView.setImageResource(R.drawable.krystal_palceholder); BitmapWorkerTask task = new BitmapWorkerTask(imageView, MainActivity.this, mMemoryCache); task.execute(resId); } }
/** * 异步加载位图 * * @param imageView * 要展示的imageView * @param res_id * 资源ID */ private void loadImageView(ImageView imageView, int res_id) { BitmapWorkerTask task = new BitmapWorkerTask(imageView, MainActivity.this, mMemoryCache); task.execute(res_id); }
private Integer[] images = new Integer[] { R.drawable.krystal001,
R.drawable.krystal002, R.drawable.krystal003,
R.drawable.krystal004, R.drawable.krystal005,
R.drawable.krystal006, R.drawable.krystal007,
R.drawable.krystal008, R.drawable.krystal009,
R.drawable.krystal010, R.drawable.krystal011,
R.drawable.krystal012, R.drawable.krystal013,
R.drawable.krystal014 };
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.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();
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
}
相关文章推荐
- Mac中Myeclipse 10安装破解,以及安装出现的问题解决
- 每天一道算法题(35)——删除字符串首尾的空格
- 每天一道算法题(35)——删除字符串首尾的空格
- Junit4单元测试
- isset()和empty()
- jquery中的$("")与原生的document.getElementById("")的区别
- Lua:Nginx Lua环境配置,使用openresty
- EBS R12 LOG files 位置
- ArcGIS教程:填洼
- EBS R12 LOG files 位置
- EBS R12 LOG files 位置
- 磁盘及文件系统管理(一)
- ACM —— 1004 Financial Management
- 自定义android圆形ImageView
- Kaldi语音识别工具使用 问题Memo
- flash透明\flash置底无效\flash遮挡div的解决方法 兼容Firefox ,IE,chrome浏览器
- php模式设计之 单例模式
- bzoj2759
- Redis安装与基本配置
- css+div三列宽度自适应