# Android学习笔记———在Android平台上的图片加载

2019-10-13  本文已影响0人  问荆_

标签(空格分隔): Android


图像数字化

在现实生活中,我们通过眼睛看见的景象是连续不断的自然景象,我们想要通过计算机得到或处理这些真实的图像的话,我们就必须对这些图像进行数字化,图像数字化是用离散来表示连续,图像数字化分为三个步骤:

图像在文件中的存储

我们都知道存储图像实际上就是存储它的像素值,将一张图像放的很大的话,我们会发现一个个排列有序的色块,用不同的二进制序列表示不同的颜色,这样来看的话,一幅图像就是一个由数字组成的颜色矩阵,通过一定规则存储这些颜色矩阵,我们就存储了这个颜色矩阵对应的图像。当然,通过一定的方式对这个颜色矩阵进行变换,我们就可以对这幅图像进行变换(伸缩、旋转、压缩等等)以达到我们想要的图片效果。

基于Android操作图像

  1. 加载图片
    Android中提供了BitmapFactory这个类支持对图片的操作,这个类提供了四个方法:decodeFile(),decodeResource(),decodeSream()和decodeByteArray(),分别分别用于支持从文件系统、资源、输入流和字节数组中加载出一个Bitmap对象,这样我们就拿到了图片的对象,我们可以通过ImageView提供的方法setImageBitmap()将图片设置给该控件完成图片的加载。下面举个栗子:
//从文件中加载bitmap

private void loadBitmap (){
        Bitmap test = BitmapFactory.decodeFile("/storage/131B-1A02/IMG_20191011_135824.jpg");
        //Rect rect = new Rect(0,0,test.getWidth(),test.getHeight());
        showBitmap(test);
    }

    private void showBitmap(Bitmap bitmap){
        ConstraintLayout.LayoutParams params = (ConstraintLayout.LayoutParams)ivTest.getLayoutParams();
        params.width = bitmap.getWidth();
        params.height = bitmap.getHeight();
        ivTest.setImageBitmap(bitmap);
    }
    
    //从网上通过url加载bitmap
     private void loadBitmapFromHttp(final String url){

        new Thread(new Runnable() {
            @Override
            public void run() {
                HttpURLConnection connection = null;
                try {
                    URL path = new URL(url);
                    connection = (HttpURLConnection)path.openConnection();
                    connection.setRequestMethod("GET");
                    connection.setConnectTimeout(6000);
                    connection.setReadTimeout(6000);

                    InputStream in = connection.getInputStream();
                    final Bitmap gank = BitmapFactory.decodeStream(in);
                    Log.d("loadBitmap:",""+(gank==null));
                    mHandler.post(new Runnable() {
                        @Override
                        public void run() {
                            if(gank!=null)
                            showBitmap(gank);
                        }
                    });
                } catch (Exception e) {
                    e.printStackTrace();
                }finally {
                    if(connection!=null)
                    connection.disconnect();
                }
            }
        }).start();
    }

2 . 高效的加载图片

我们都知道,在Android值用于显示图像的控件是ImageView,把资源图像设置后,图像会根据ImageView的宽高伸缩。如果过资源图像分辨率很大,而ImageView的宽高很小,这时候我们就可以对图像进行压缩去更加高效的展示图像。我们可以通过Android中提供的BitmapFactory.Options这个类,这个类中有一个inSampleSize参数,这个参数决定了图像的压缩比,他有一下属性:

下面举个例子:

 private Bitmap resizeBitmapFromFile(String path , int scaleFactor){
        final  BitmapFactory.Options options= new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeFile(path,options);
        int width = options.outWidth;
        int height = options.outHeight;
        int requireWidth = width >> scaleFactor;
        int requireHeight = height >> scaleFactor;
        int inSampleSize = 1;

        if (height>requireHeight ||  width> requireWidth){
            final int halfHeight=height/2;
            final int halfWidth=width/2;
            //计算最大的采样率,采样率为2的指数
            while ((halfWidth/inSampleSize)>=requireWidth && (halfHeight/inSampleSize)>=requireHeight){
                inSampleSize = inSampleSize << 1;
            }
        }
        options.inSampleSize = inSampleSize;
        options.inJustDecodeBounds = false;
        return BitmapFactory.decodeFile(path,options);
    }

3 . 图像的变换

前面讲了如何加载Bitmap,但是有些时候我们拿到的Bitmap并不适合直接拿来使用,我们要通过一些图像变换或者其他处理之后才能满足使用的需求。

最常见的栗子就是圆角图像——用户想要设置圆形头像,但是我们从他指定的位置读出的图像都是矩形的,我们就需要进行一些操作去满足矩形图像转化成为圆形图像的应用,下面提供一个简单的思路:

private Bitmap roundBitmap(Bitmap bitmap,float rx,float ry){
        //首先创建一个可变的位图对象,颜色空间是RGB,
        Bitmap result = Bitmap.createBitmap(requireWidth,requireHeight,
                Bitmap.Config.ARGB_8888);
        final Paint paint = new Paint();
        //创建一个画布,并指定该画布能绘制之前我们创建的位图
        Canvas canvas = new Canvas(result);
        paint.setAntiAlias(true);
        RectF rectF = new RectF(0,0,requireWidth,requireHeight);
        //先画一个圆角矩形的图层
        canvas.drawRoundRect(rectF,rx,ry,paint);
        //然后指定图层的叠加模式为,形状取下层,图像取上层
        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
        canvas.drawBitmap(bitmap,0,0,paint);
        return result;
    }

这里的重点在于13行的图层叠加模式的使用,Android提供了多种不同的模式,通过这个方法设置不同的模式可以完成很多意向不到的变化。

再举一个栗子:用户觉得只有圆形头像太单调,他想要一个笑脸的头像。刚听到用户的需求,某底层程序小王只有暗自叹气,但是冷静过后,想了想之前圆角图像的实现,发现好像并不是很难,撸着袖子开干:

private Bitmap smileBitmap(Bitmap bitmap){
        Bitmap result = Bitmap.createBitmap(bitmap.getWidth(),bitmap.getHeight(),
                Bitmap.Config.ARGB_8888);
        final Paint paint = new Paint();
        Canvas canvas = new Canvas(result);
        paint.setAntiAlias(true);
        RectF rectF = new RectF(0,0,bitmap.getWidth()/2,bitmap.getHeight()/2);
        canvas.drawArc(rectF,180,360,false,paint);
        RectF rectF1 = new RectF(bitmap.getWidth()/2,0,bitmap.getWidth(),bitmap.getHeight()/2);
        canvas.drawArc(rectF1,180,360,false,paint);
        RectF rectF2 = new RectF(0,0,bitmap.getWidth(),bitmap.getHeight());
        canvas.drawArc(rectF2,0,180,false,paint);
        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
        canvas.drawBitmap(bitmap,0,0,paint);
        return result;
    }

做完这个需求之后,他老是不安心,总觉得“贪得无厌”的用户还有更多的需求,他准备把这个问题抽象出来,用户无非是想要各种类型的头像,那么他可以准备各种图片框供用户选择,什么圆角图片框,三角图片框,笑脸图片框,花型图片框,心形图片框等等。用户选择完图片后,他可以直接把这图片套进这个图片框中就行了。

过了几天这边又发现了几个问题,用户下线了之后,头像还是彩色的,让人觉得体验不好,需要下线后把图像转化成灰色的提醒一下别人。 接受这个需求的码农挠了挠脑袋,决定从图像的底层入手,把RGB的混合相加彩色空间,平均一下实现单通道的灰度图像:

 private Bitmap bitmapToGray(Bitmap bitmap){
        Bitmap result = bitmap.copy(Bitmap.Config.ARGB_8888,true);
        int width = result.getWidth();
        int height = result.getHeight();

        // 保存所有的像素的数组,图片宽×高
        int[] pixels = new int[width * height];
        result.getPixels(pixels,0,width,0,0,width,height);
        for(int i =0;i < pixels.length;i++){
            int a = (pixels[i] & 0xff000000)>>24; //透明通道
            int red  = (pixels[i] & 0x00ff0000) >> 16; //红色通道
            int green = (pixels[i] & 0x0000ff00) >> 8; //绿色通道
            int blue = pixels[i] & 0x000000ff; //蓝色通道
            int average = (red + green + blue )/3; // 转换成为灰度图像需要平均三个通道
            pixels[i] = (a << 24) | (average << 16) | (average << 8) | average;
        }
        result.setPixels(pixels,0,width,0,0,width,height);
        return result;
    }

作者(小白)水平有限,如有问题还请大佬指点。

上一篇下一篇

猜你喜欢

热点阅读