Bitmap精炼详解第(二)节:Bitmap常见处理方式

一、前期基础知识储备

笔者在第一课相关讲解中,对Bitmap的加载、高效加载和图片压缩进行了详细的解析,感兴趣的读者可以参考下Bitmap精炼详解第(一)课:Bitmap解析和加载。通过前面内容的学习,我们已经掌握了将Bitmap高效的加载出来,当然了实际开发中,产品经理是不可能仅仅满足于将图片加载出来,那样应用程序不够吸引人,在市面上的竞争力也没有那么强,所以我们在将Bitmap加载出来之后,要进行进一步的处理,做出用户体验更好,界面更吸引人的效果。

常见的Bitmap的处理包括:圆角图片、圆形图片、图片截取、图片透明度、图片色调

二、上代码,具体实现

(1)自定义一个view—BitmapEffect

package com.example.administrator.animation_practice;

public class BitmapEffect extends View {
    private Bitmap bitmap;
    public BitmapEffect(Context context) {
        super(context, null);
    }
    public BitmapEffect(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public BitmapEffect(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

    public BitmapEffect(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawBitmap(toRoundCorner(bitmap,10) ,0 ,0 ,null);
    }
        public Bitmap toRoundCorner(Bitmap bitmap, int pixels) {
            bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.bv);
            Bitmap roundCornerBitmap = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.ARGB_8888);
            Canvas canvas = new Canvas(roundCornerBitmap);
            int color = 0xff424242;// int color = 0xff424242;
            Paint paint = new Paint();
            paint.setColor(color);
            // 防止锯齿
            paint.setAntiAlias(true);
            Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
            RectF rectF = new RectF(rect);
            float roundPx = pixels;
            // 相当于清屏
            canvas.drawARGB(0, 0, 0, 0);
            // 先画了一个带圆角的矩形
            canvas.drawRoundRect(rectF, 30, 30, paint);
            paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
            // 再把原来的bitmap画到现在的bitmap
            canvas.drawBitmap(bitmap, rect, rect, paint);
            return roundCornerBitmap;
        }
}

(2)创建一个新Activity加载自定义的View

package com.example.administrator.animation_practice;

import android.app.Activity;
import android.os.Bundle;

/**
 * Created by Administrator on 2018/4/2 0002.
 * powered by Cpf.com.
 */
public class BitmapEffectTest extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        BitmapEffect bitmapEffect = new BitmapEffect(this);
        setContentView(bitmapEffect);
    }
}

(3)改换边框的代码,设置为圆形图片

canvas.drawCircle(500,500,400,mPaint); 

运行效果如图:

小结:这里以两个设置图片的边框特效为例,讲解了Bitmap的常用用法。可能初学Bitmap的读者对于这部分内容会有些疑惑:1)Canvas;2)PorterDuffXfermode。

下面笔者来具体讲解一下——我们来看看自定义的toRoundCorner()方法里面的四行关键代码

//第一行代码——获得图片
bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.bv);
//第二行代码——获得Bitmap对象
Bitmap roundCornerBitmap = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.ARGB_8888);
//第三行代码——装载画布
Canvas canvas = new Canvas(roundCornerBitmap);
//第四行代码——绘制Bitmap
canvas.drawBitmap(bitmap, rect, rect, paint);

第一行代码就是我们在Bitmap精炼详解第(一)课中学到的Bitmap图片的加载,这里我们使用的是从本地资源文件里加载的方式,比较简单就没有使用采样率,调用这行代码之后,我们加载了这张图片,也即我们获得了这张图片;

第二代码是我们要重点理解的代码之一,这段代码调用了Bitmap的标准方法createBitmap()方法,传入刚刚获取到的图片,最后得到的了Bitmap实例(注意这里,第一行代码中我们得到的是这张图片,第二行代码我们得到的是这张图片的实例对象);

第三行代码是创建canvas实例的标准方法,canvas是onDraw()方法里的唯一参数,谷歌官方给出的创建canvas实例的标准方法就是Canvas canvas = new Canvas(bitmap),要求传入一个Bitmap对象,而这里传入的Bitmap实例对象就是我们通过第二行代码创建的,创建了canvas对象,传入了bitmap实例,就相当于把bitmap和canvas绑定了,这样后来调用canvas的draw.XXXX()方法,就可以直接在bimap上进行操作了——which is normal in PS,这种方法在PS中非常常见,“在画布上添加一张图片,然后在对这张图片做各种处理”,比如在本例中,就是给这张图片加上一个类似于“边框”的效果;

第四行代码就是toRoundCorner()方法的终点站——利用canvas实例对象调用drawBitmap()方法绘制出Bitmap。这里调用的时机是在canvas.drawRoundRect()之后,就是已经在这个Bitmap中画上了圆角矩形或者圆形,并且已经定义了“混合区域”的显示方式,所以最终显示的就是一张加了圆角矩形或者圆形的“边框”的图片;

最后在onDraw()方法体中再次调用canvas.drawBitmap()方法,并且传入toRoundCorner()方法需要的参数,我们就可以画出一个加了“边框”特效的图片。

另,PorterDuffXfermode,这个东西是属于画笔特效的范畴,在开发中常用的作用就是定义2张图片“混合区域”的显示方式,而最常用的就是实现一个圆角矩形或者一个圆形边框的效果。

————————————————————我是分隔线—————————————————————

三、使用Bitmap的其他注意事项

(1)不用的Bitmap及时释放

if (!bmp.isRecycle()) {
    bmp.recycle();   //回收图片所占的内存
    bitmap = null;
    system.gc();  //提醒系统及时回收
}

虽然调用recycle()并不能保证立即释放占用的内存,但是可以加速Bitmap的内存的释放。释放内存以后,就不能再使用该Bitmap对象了,如果再次使用,就会抛出异常。所以一定要保证不再使用的时候释放。比如,如果是在某个Activity中使用Bitmap,就可以在Activity的onStop()或者onDestroy()方法中进行回收。

(2)捕获异常

Bitmap bitmap = null;
    try {
        // 实例化Bitmap
        bitmap = BitmapFactory.decodeFile(path);
    } catch (OutOfMemoryError e) {
    
    }
    if (bitmap == null) {
        return defaultBitmapMap; // 如果实例化失败 返回默认的Bitmap对象
    }

因为Bitmap非常耗内存,了避免应用在分配Bitmap内存的时候出现OutOfMemory异常以后Crash掉,需要特别注意实例化Bitmap部分的代码。通常,在实例化Bitmap的代码中,一定要对OutOfMemory异常进行捕获。很多开发者会习惯性的在代码中直接捕获Exception。但是对于OutOfMemoryError来说,这样做是捕获不到的。因为OutOfMemoryError是一种Error,而不是Exception。

(3)缓存通用的Bitmap对象

有时候,可能需要在一个Activity里多次用到同一张图片。比如一个Activity会展示一些用户的头像列表,而如果用户没有设置头像的话,则会显示一个默认头像,而这个头像是位于应用程序本身的资源文件中的。如果有类似上面的场景,就可以对同一Bitmap进行缓存。如果不进行缓存,尽管看到的是同一张图片文件,但是使用BitmapFactory类的方法来实例化出来的Bitmap,是不同的Bitmap对象。缓存可以避免新建多个Bitmap对象,避免内存的浪费。在Android应用开发过程中所说的缓存有两个级别,一个是硬盘缓存,一个是内存缓存。

(4)Android加载大量图片内存溢出解决方案

1)开发者在实际开发过程中尽量不要使用setImageBitmap或setImageResource或BitmapFactory.decodeResource来设置一张大图,因为这些函数在完成decode后,最终都是通过java层的createBitmap来完成的,需要消耗更多内存,可以通过BitmapFactory.decodeStream方法,创建出一个bitmap,再将其设为ImageView的 source;

2)使用BitmapFactory.Options对图片进行压缩(上述第二部分);

3)运用Java软引用,进行图片缓存,将需要经常加载的图片放进缓存里,避免反复加载;

 

总结:本节内容较多,然后笔者写着写着也出现了疑问,为什么要采用Bitmap,不直接使用ImageView?其实,这个问题挺傻的,因为ImageView加载出来只能是矩形图片,只能是原始矩形图片,而要对图片进行一系列的操作,就必须借用Bitmap,把PNG\JPG的图片转换成Bitmap,然后调用Canvas和Bitmap的一系列方法,对这张图片进行处理。(╯‵□′)╯︵┻━┻为什么UI不直接做好效果给我们呢?因为UI只能做好内置的图片,而真实用户手机里的只有原始未处理的矩形图片,我们开发者要写好代码,然后从网络/本地中获取用户的图片,加载后再对其进行处理,最终显示为一个更加好的界面元素。伟大的开发者!

。୧(๑•◡•๑)૭୧(๑•◡•๑)૭(๑•ㅂ•)و✧(๑•ㅂ•)و✧——开发者就是我们!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值