我有一个ListView,每行上有两个图像按钮。当用户单击列表行时,它将启动一个新活动。我不得不建立我自己的标签,因为相机布局的问题。为结果启动的活动是一个映射。如果我点击我的按钮来启动图像预览(从SD卡加载图像),应用程序从活动返回到ListView活动到结果处理程序,重新启动我的新活动,这只不过是一个图像小部件。
ListView上的图像预览是用光标和ListAdapter完成的。这使得它非常简单,但我不确定我如何可以把一个调整大小的图像(即较小的位大小而不是像素作为图像按钮的src)。我只是调整了从手机摄像头拍下来的图片的大小。
问题是,当它试图返回并重新启动第二个活动时,我得到一个OutOfMemoryError。
是否有一种方法,我可以轻松地构建列表适配器行,在那里我可以调整大小的飞行(位)?
这将是可取的,因为我还需要对每行中的小部件/元素的属性做出一些更改,因为我无法选择一行与触摸屏,因为焦点问题。(我会用滚轮。)
我知道我可以做带外调整并保存我的图像,但这不是我真正想做的,但一些示例代码会很好。
当我在ListView上禁用图像时,它又可以正常工作了。
供你参考:我是这样做的:
String[] from = new String[] { DBHelper.KEY_BUSINESSNAME, DBHelper.KEY_ADDRESS,
DBHelper.KEY_CITY, DBHelper.KEY_GPSLONG, DBHelper.KEY_GPSLAT,
DBHelper.KEY_IMAGEFILENAME + ""};
int[] to = new int[] { R.id.businessname, R.id.address, R.id.city, R.id.gpslong,
R.id.gpslat, R.id.imagefilename };
notes = new SimpleCursorAdapter(this, R.layout.notes_row, c, from, to);
setListAdapter(notes);
其中R.id.imagefilename是一个ButtonImage。
这是我的LogCat:
01-25 05:05:49.877: ERROR/dalvikvm-heap(3896): 6291456-byte external allocation too large for this process.
01-25 05:05:49.877: ERROR/(3896): VM wont let us allocate 6291456 bytes
01-25 05:05:49.877: ERROR/AndroidRuntime(3896): Uncaught handler: thread main exiting due to uncaught exception
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): java.lang.OutOfMemoryError: bitmap size exceeds VM budget
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.graphics.BitmapFactory.nativeDecodeStream(Native Method)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:304)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.graphics.BitmapFactory.decodeFile(BitmapFactory.java:149)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.graphics.BitmapFactory.decodeFile(BitmapFactory.java:174)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.graphics.drawable.Drawable.createFromPath(Drawable.java:729)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.widget.ImageView.resolveUri(ImageView.java:484)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.widget.ImageView.setImageURI(ImageView.java:281)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.widget.SimpleCursorAdapter.setViewImage(SimpleCursorAdapter.java:183)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.widget.SimpleCursorAdapter.bindView(SimpleCursorAdapter.java:129)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.widget.CursorAdapter.getView(CursorAdapter.java:150)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.widget.AbsListView.obtainView(AbsListView.java:1057)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.widget.ListView.makeAndAddView(ListView.java:1616)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.widget.ListView.fillSpecific(ListView.java:1177)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.widget.ListView.layoutChildren(ListView.java:1454)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.widget.AbsListView.onLayout(AbsListView.java:937)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.view.View.layout(View.java:5611)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1119)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.widget.LinearLayout.layoutHorizontal(LinearLayout.java:1108)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.widget.LinearLayout.onLayout(LinearLayout.java:922)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.view.View.layout(View.java:5611)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.widget.FrameLayout.onLayout(FrameLayout.java:294)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.view.View.layout(View.java:5611)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1119)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.widget.LinearLayout.layoutVertical(LinearLayout.java:999)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.widget.LinearLayout.onLayout(LinearLayout.java:920)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.view.View.layout(View.java:5611)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.widget.FrameLayout.onLayout(FrameLayout.java:294)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.view.View.layout(View.java:5611)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.view.ViewRoot.performTraversals(ViewRoot.java:771)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.view.ViewRoot.handleMessage(ViewRoot.java:1103)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.os.Handler.dispatchMessage(Handler.java:88)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.os.Looper.loop(Looper.java:123)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.app.ActivityThread.main(ActivityThread.java:3742)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at java.lang.reflect.Method.invokeNative(Native Method)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at java.lang.reflect.Method.invoke(Method.java:515)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:739)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:497)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at dalvik.system.NativeStart.main(Native Method)
01-25 05:10:01.127: ERROR/AndroidRuntime(3943): ERROR: thread attach failed
我也有一个新的错误时显示的图像:
22:13:18.594: DEBUG/skia(4204): xxxxxxxxxxx jpeg error 20 Improper call to JPEG library in state %d
22:13:18.604: INFO/System.out(4204): resolveUri failed on bad bitmap uri:
22:13:18.694: ERROR/dalvikvm-heap(4204): 6291456-byte external allocation too large for this process.
22:13:18.694: ERROR/(4204): VM won't let us allocate 6291456 bytes
22:13:18.694: DEBUG/skia(4204): xxxxxxxxxxxxxxxxxxxx allocPixelRef failed
我使用解码文件描述符,它为我工作:
FileInputStream fileInputStream = null;
try {
fileInputStream = new FileInputStream(file);
FileDescriptor fd = fileInputStream.getFD();
Bitmap imageBitmap = decodeSampledBitmapFromDescriptor(fd , 612,
816);
imageView.setImageBitmap(imageBitmap);
} catch (Exception e) {
e.printStackTrace();
}finally {
if(fileInputStream != null){
try {
fileInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
从文件描述符解码采样位图的代码:
/**
* Decode and sample down a bitmap from a file input stream to the requested width and height.
*
* @param fileDescriptor The file descriptor to read from
* @param reqWidth The requested width of the resulting bitmap
* @param reqHeight The requested height of the resulting bitmap
* @return A bitmap sampled down from the original with the same aspect ratio and dimensions
* that are equal to or greater than the requested width and height
*/
public static Bitmap decodeSampledBitmapFromDescriptor(
FileDescriptor fileDescriptor, int reqWidth, int reqHeight) {
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFileDescriptor(fileDescriptor, null, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeFileDescriptor(fileDescriptor, null, options);
}
/**
* Calculate an inSampleSize for use in a {@link android.graphics.BitmapFactory.Options} object when decoding
* bitmaps using the decode* methods from {@link android.graphics.BitmapFactory}. This implementation calculates
* the closest inSampleSize that will result in the final decoded bitmap having a width and
* height equal to or larger than the requested width and height. This implementation does not
* ensure a power of 2 is returned for inSampleSize which can be faster when decoding but
* results in a larger bitmap which isn't as useful for caching purposes.
*
* @param options An options object with out* params already populated (run through a decode*
* method with inJustDecodeBounds==true
* @param reqWidth The requested width of the resulting bitmap
* @param reqHeight The requested height of the resulting bitmap
* @return The value to be used for inSampleSize
*/
public static int calculateInSampleSize(BitmapFactory.Options options,
int reqWidth, int reqHeight) {
// Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
// Calculate ratios of height and width to requested height and width
final int heightRatio = Math.round((float) height / (float) reqHeight);
final int widthRatio = Math.round((float) width / (float) reqWidth);
// Choose the smallest ratio as inSampleSize value, this will guarantee a final image
// with both dimensions larger than or equal to the requested height and width.
inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
// This offers some additional logic in case the image has a strange
// aspect ratio. For example, a panorama may have a much larger
// width than height. In these cases the total pixels might still
// end up being too large to fit comfortably in memory, so we should
// be more aggressive with sample down the image (=larger inSampleSize).
final float totalPixels = width * height;
// Anything more than 2x the requested pixels we'll sample down further
final float totalReqPixelsCap = reqWidth * reqHeight * 2;
while (totalPixels / (inSampleSize * inSampleSize) > totalReqPixelsCap) {
inSampleSize++;
}
}
return inSampleSize;
}
这似乎是与社区分享我的加载和处理图像的实用工具类的合适地方,欢迎您自由使用和修改它。
package com.emil;
import java.io.IOException;
import java.io.InputStream;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
/**
* A class to load and process images of various sizes from input streams and file paths.
*
* @author Emil http://stackoverflow.com/users/220710/emil
*
*/
public class ImageProcessing {
public static Bitmap getBitmap(InputStream stream, int sampleSize, Bitmap.Config bitmapConfig) throws IOException{
BitmapFactory.Options options=ImageProcessing.getOptionsForSampling(sampleSize, bitmapConfig);
Bitmap bm = BitmapFactory.decodeStream(stream,null,options);
if(ImageProcessing.checkDecode(options)){
return bm;
}else{
throw new IOException("Image decoding failed, using stream.");
}
}
public static Bitmap getBitmap(String imgPath, int sampleSize, Bitmap.Config bitmapConfig) throws IOException{
BitmapFactory.Options options=ImageProcessing.getOptionsForSampling(sampleSize, bitmapConfig);
Bitmap bm = BitmapFactory.decodeFile(imgPath,options);
if(ImageProcessing.checkDecode(options)){
return bm;
}else{
throw new IOException("Image decoding failed, using file path.");
}
}
public static Dimensions getDimensions(InputStream stream) throws IOException{
BitmapFactory.Options options=ImageProcessing.getOptionsForDimensions();
BitmapFactory.decodeStream(stream,null,options);
if(ImageProcessing.checkDecode(options)){
return new ImageProcessing.Dimensions(options.outWidth,options.outHeight);
}else{
throw new IOException("Image decoding failed, using stream.");
}
}
public static Dimensions getDimensions(String imgPath) throws IOException{
BitmapFactory.Options options=ImageProcessing.getOptionsForDimensions();
BitmapFactory.decodeFile(imgPath,options);
if(ImageProcessing.checkDecode(options)){
return new ImageProcessing.Dimensions(options.outWidth,options.outHeight);
}else{
throw new IOException("Image decoding failed, using file path.");
}
}
private static boolean checkDecode(BitmapFactory.Options options){
// Did decode work?
if( options.outWidth<0 || options.outHeight<0 ){
return false;
}else{
return true;
}
}
/**
* Creates a Bitmap that is of the minimum dimensions necessary
* @param bm
* @param min
* @return
*/
public static Bitmap createMinimalBitmap(Bitmap bm, ImageProcessing.Minimize min){
int newWidth, newHeight;
switch(min.type){
case WIDTH:
if(bm.getWidth()>min.minWidth){
newWidth=min.minWidth;
newHeight=ImageProcessing.getScaledHeight(newWidth, bm);
}else{
// No resize
newWidth=bm.getWidth();
newHeight=bm.getHeight();
}
break;
case HEIGHT:
if(bm.getHeight()>min.minHeight){
newHeight=min.minHeight;
newWidth=ImageProcessing.getScaledWidth(newHeight, bm);
}else{
// No resize
newWidth=bm.getWidth();
newHeight=bm.getHeight();
}
break;
case BOTH: // minimize to the maximum dimension
case MAX:
if(bm.getHeight()>bm.getWidth()){
// Height needs to minimized
min.minDim=min.minDim!=null ? min.minDim : min.minHeight;
if(bm.getHeight()>min.minDim){
newHeight=min.minDim;
newWidth=ImageProcessing.getScaledWidth(newHeight, bm);
}else{
// No resize
newWidth=bm.getWidth();
newHeight=bm.getHeight();
}
}else{
// Width needs to be minimized
min.minDim=min.minDim!=null ? min.minDim : min.minWidth;
if(bm.getWidth()>min.minDim){
newWidth=min.minDim;
newHeight=ImageProcessing.getScaledHeight(newWidth, bm);
}else{
// No resize
newWidth=bm.getWidth();
newHeight=bm.getHeight();
}
}
break;
default:
// No resize
newWidth=bm.getWidth();
newHeight=bm.getHeight();
}
return Bitmap.createScaledBitmap(bm, newWidth, newHeight, true);
}
public static int getScaledWidth(int height, Bitmap bm){
return (int)(((double)bm.getWidth()/bm.getHeight())*height);
}
public static int getScaledHeight(int width, Bitmap bm){
return (int)(((double)bm.getHeight()/bm.getWidth())*width);
}
/**
* Get the proper sample size to meet minimization restraints
* @param dim
* @param min
* @param multipleOf2 for fastest processing it is recommended that the sample size be a multiple of 2
* @return
*/
public static int getSampleSize(ImageProcessing.Dimensions dim, ImageProcessing.Minimize min, boolean multipleOf2){
switch(min.type){
case WIDTH:
return ImageProcessing.getMaxSampleSize(dim.width, min.minWidth, multipleOf2);
case HEIGHT:
return ImageProcessing.getMaxSampleSize(dim.height, min.minHeight, multipleOf2);
case BOTH:
int widthMaxSampleSize=ImageProcessing.getMaxSampleSize(dim.width, min.minWidth, multipleOf2);
int heightMaxSampleSize=ImageProcessing.getMaxSampleSize(dim.height, min.minHeight, multipleOf2);
// Return the smaller of the two
if(widthMaxSampleSize<heightMaxSampleSize){
return widthMaxSampleSize;
}else{
return heightMaxSampleSize;
}
case MAX:
// Find the larger dimension and go bases on that
if(dim.width>dim.height){
return ImageProcessing.getMaxSampleSize(dim.width, min.minDim, multipleOf2);
}else{
return ImageProcessing.getMaxSampleSize(dim.height, min.minDim, multipleOf2);
}
}
return 1;
}
public static int getMaxSampleSize(int dim, int min, boolean multipleOf2){
int add=multipleOf2 ? 2 : 1;
int size=0;
while(min<(dim/(size+add))){
size+=add;
}
size = size==0 ? 1 : size;
return size;
}
public static class Dimensions {
int width;
int height;
public Dimensions(int width, int height) {
super();
this.width = width;
this.height = height;
}
@Override
public String toString() {
return width+" x "+height;
}
}
public static class Minimize {
public enum Type {
WIDTH,HEIGHT,BOTH,MAX
}
Integer minWidth;
Integer minHeight;
Integer minDim;
Type type;
public Minimize(int min, Type type) {
super();
this.type = type;
switch(type){
case WIDTH:
this.minWidth=min;
break;
case HEIGHT:
this.minHeight=min;
break;
case BOTH:
this.minWidth=min;
this.minHeight=min;
break;
case MAX:
this.minDim=min;
break;
}
}
public Minimize(int minWidth, int minHeight) {
super();
this.type=Type.BOTH;
this.minWidth = minWidth;
this.minHeight = minHeight;
}
}
/**
* Estimates size of Bitmap in bytes depending on dimensions and Bitmap.Config
* @param width
* @param height
* @param config
* @return
*/
public static long estimateBitmapBytes(int width, int height, Bitmap.Config config){
long pixels=width*height;
switch(config){
case ALPHA_8: // 1 byte per pixel
return pixels;
case ARGB_4444: // 2 bytes per pixel, but depreciated
return pixels*2;
case ARGB_8888: // 4 bytes per pixel
return pixels*4;
case RGB_565: // 2 bytes per pixel
return pixels*2;
default:
return pixels;
}
}
private static BitmapFactory.Options getOptionsForDimensions(){
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds=true;
return options;
}
private static BitmapFactory.Options getOptionsForSampling(int sampleSize, Bitmap.Config bitmapConfig){
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = false;
options.inDither = false;
options.inSampleSize = sampleSize;
options.inScaled = false;
options.inPreferredConfig = bitmapConfig;
return options;
}
}