Android简单的图片浏览器demo遇到的各种坑

最近在做一个用户反馈的模块,可以上传最多三张图片,点击小图片后跳转到图片浏览器,可以查看图片。

图像处理是个很消耗内存的事情,android中对Bitmap的处理需要格外小心,一个不留神你的应用可能就因为Bitmap导致oom。

首先点击加号按钮可以打开系统图片浏览器,选择图片,并返回图片的Uri,在加号按钮的点击事件中发送Intent:

Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
                    intent.addCategory(Intent.CATEGORY_OPENABLE);
                    intent.setType("image/*");
                    intent.putExtra("return-data",true);
                    Rc.m_pActivity.startActivityForResult(intent, GET_LOCAL_IMAGE_REQUEST_CODE);

Rc.m_pActivity是项目全局存储的Activity的实例,如果是在Activity中实现,替换成对应的Activity实例即可。

在系统图片浏览器中选择相应的图片后返回到自己的Activity,然后返回图片的Uri,我们对Uri做相应的处理,并使其显示在界面上。

@Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (resultCode == Activity.RESULT_OK) {
            //        Bitmap cameraBitmap = (Bitmap) data.getExtras().get("data");
            //        mAddScreenImage1.setImageBitmap(cameraBitmap);
            switch (requestCode) {
            case GET_LOCAL_IMAGE_REQUEST_CODE:
                ContentResolver resolver = Rc.m_pActivity.getContentResolver();
                //照片的原始资源地址
                Uri originalUri = data.getData();

                try {
                    //使用ContentProvider通过URI获取原始图片
                    Bitmap photo = MediaStore.Images.Media.getBitmap(resolver, originalUri);
                    if (photo != null) {
                        //为防止原始图片过大导致内存溢出,这里先缩小原图显示,然后释放原始Bitmap占用的内存
                        Bitmap smallBitmap = GalleryActivity.zoomImage(photo, Rc.GetIns().Dip2Pix(48), Rc.GetIns().Dip2Pix(48));
                        //释放原始图片占用的内存,防止out of memory异常发生
                        photo.recycle();
                    }
                } catch (FileNotFoundException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                break;
            default:
                break;
            }
        }
    }

zoomImage(Bitmap bitmap, double width, double height)是个封装的方法,是为了缩小成缩略图,同时防止图片过大导致oom。方法如下:

public static Bitmap zoomImage(Bitmap bmp, double newWidth,
                                   double newHeight) {
        // 获取这个图片的宽和高
        float width = bmp.getWidth();
        float height = bmp.getHeight();
        // 创建操作图片用的matrix对象
        Matrix matrix = new Matrix();
        // 计算宽高缩放率
        float scaledWidth = ((float) newWidth) / width;
        float scaledHeight = ((float) newHeight) / height;
        // 缩放图片动作
        matrix.postScale(scaledWidth, scaledHeight);
        Bitmap bitmap = Bitmap.createBitmap(bmp, 0, 0, (int) width,
                (int) height, matrix, true);
        return bitmap;
    }

接下来要做的就是将用户选择的图片的Uri装到一个ArrayList中,然后通过Intent传递给自己写的图片浏览器的Activity。

本来GalleryActivity(图片浏览器)是想用TabHost+ViewPager来实现的,但是好像TabHost的每一个Tab都得是一个Activity和Fragment不知道好不好添加View,另外可能是想尝试一下RecyclerView,就采用了ViewPager(上面的大图浏览区域)+RecyclerView(下面的下图预览区域)的实现方式,如果有TabHost的独特优势或者更加好的方式,欢迎指教。

在GalleryActivity中ViewPager的每一个页是一个ImageView,每个RecyclerView的item也是一个ImageView,具体实现不详述了。这里主要说的是图片处理中的问题,开始的时候直接将图片添加到ImageView中,遇到稍微大一点的图片,ViewPager就无法显示,后来对图片进行了自适应屏幕和缩放的处理,才解决了这个问题,缩放的方法就是上面的方法。下面贴上自适应屏幕的处理方法。

private Bitmap createScaledBitmap(Bitmap bitmap){
        DisplayMetrics dm = new DisplayMetrics();
        getWindowManager().getDefaultDisplay().getMetrics(dm);
        int width = dm.widthPixels+1;
        int height = dm.heightPixels+1;
        int t_width;
        int t_height;
        if (bitmap.getWidth()>width || bitmap.getHeight()>height){
            t_width = width;
            t_height = bitmap.getHeight()*width/bitmap.getWidth();
            if (t_height>height){
                t_width = t_width*height/t_height;
                t_height = height;
            }
        } else
        if (bitmap.getWidth()<width && bitmap.getHeight()<height){
            t_width = width;
            t_height = bitmap.getHeight()*width/bitmap.getWidth();
            if (t_height>height){
                t_width = t_width*height/t_height;
                t_height = height;
            }
        } else {
            t_width = bitmap.getWidth();
            t_height = bitmap.getHeight();
        }
        return Bitmap.createScaledBitmap(bitmap, t_width, t_height, true);
    }

下面的RecyclerView的item小图还需要圆角处理,所以又从网上找了个圆角处理的方法:

private Bitmap getRoundedCornerBitmap(Bitmap bitmap, float radius) {
        try {
            Bitmap output = Bitmap.createBitmap(bitmap.getWidth(),
                    bitmap.getHeight(), Config.ARGB_8888);
            Canvas canvas = new Canvas(output);
            final Paint paint = new Paint();
            final Rect rect = new Rect(0, 0, bitmap.getWidth(),
                    bitmap.getHeight());
            final RectF rectF = new RectF(new Rect(0, 0, bitmap.getWidth(),
                    bitmap.getHeight()));
            final float roundPx = radius;
            paint.setAntiAlias(true);
            canvas.drawARGB(0, 0, 0, 0);
            paint.setColor(Color.BLACK);
            canvas.drawRoundRect(rectF, roundPx, roundPx, paint);
            paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
            final Rect src = new Rect(0, 0, bitmap.getWidth(),
                    bitmap.getHeight());
            canvas.drawBitmap(bitmap, src, rect, paint);
            return output;
        } catch (Exception e) {
            return bitmap;
        }
    }

还有一个比较坑的地方,是在使用RecyclerView的时候,设置方向是水平,宽度为wrap_content,然后使其居中,开始在android studio上面的时候这个效果是好的,但是后来我原封不动的拷贝到我们eclipse结构的项目中时,结果竟然不好用了!!!后来把android studio中的包拷贝过来,又出现了其他一些类找不到的问题,难道是我拷错了包?算了,项目里的包也不太好换,怕出问题背锅,所以只能通过设置ItemDecoration来解决了。

class SpaceItemDecoration extends RecyclerView.ItemDecoration{

        private int space;

        public SpaceItemDecoration(int space) {
            this.space = space;
        }

        @Override
        public void getItemOffsets(Rect outRect, View view, RecyclerView parent,
                                   RecyclerView.State state) {
            int itemCount = images.size();
            int pos = parent.getChildAdapterPosition(view);

            //设定item之间的间隔
            if (pos != (itemCount -1)) {
                outRect.right = space;
            } else {
                outRect.right = 0;
            }

            //动态设定四周的边距
            outRect.top = Rc.GetIns().Dip2Pix(100) - mUsedBitmaps.get(pos).getHeight();
            WindowManager wm = GalleryActivity.this.getWindowManager();
            Rect screenSize = new Rect();
            wm.getDefaultDisplay().getRectSize(screenSize);
            int width = screenSize.width();
            if (pos == 0) {
                outRect.left = (width - Rc.GetIns().Dip2Pix((mUsedBitmaps.size() - 1)
                        * (SMALL_PREVIEW_IMAGE_WIDTH + PREVIEW_IMAGE_SPACE) + BIG_PREVIEW_IMAGE_WIDTH)) / 2;
            }
        }
    }

动态的设定边距,然后previewGallery.addItemDecoration(new SpaceItemDecoration(
Rc.GetIns().Dip2Pix(PREVIEW_IMAGE_SPACE)))
使其生效。
好了,要记录的大概就这些,如果各位有指教,欢迎留言!