六、Android绘制知识总结(图像篇)

256 阅读5分钟

一、Drawable

可绘制对象,它不同于Bitmap,Bitmap是能够绘制在自身里,而它,只能绘制在别的Canvas中。

Drawable变色的通用代码

//1:通过图片资源文件生成Drawable实例
Drawable drawable = getResources().getDrawable(R.mipmap.ic_launcher).mutate();
//2:先调用DrawableCompat的wrap方法
drawable = DrawableCompat.wrap(drawable);
//3:再调用DrawableCompat的setTint方法,为Drawable实例进行着色
DrawableCompat.setTint(drawable, Color.RED);
  • ShapeDrawable
  • GradientDrawable

ShapeDrawable和GradientDrawable都对应的是shape标签。但GradientDrawable只能完成gradient标签的功能,并不能完成shape标签所能完成的绘制矩形、椭圆等功能。

selector 标签对应 Java 类是 StateListDrawable ,

ShapeDrawable需要传入一个Shape对象,其派生子类有:RectShapeArcShapeOvalShapeRoundRectShapePathShape

其中,前四个(RectShapeArcShapeOvalShapeRoundRectShape),都是通过调用ShapeDrawable.setBounds来控制Shape的显示区域,单位是px。

public PathShape(Path path, float stdWidth, float stdHeight)

参数:

  • path:表示所要画的路径。
  • stdWidth:表示标准宽度,即将整个ShapeDrawable 的宽度分成多少份。Path 中的moveTo(x,y)、lineTo(x2,y2)这些函数中的数值在这里其实都是以每一份的位置来计算的。当ShapeDrawable动态变大、变小时,每一份都会变小,而根据这些份的数值画出来的Path图形就会动态缩放。
  • stdHeight:表示标准高度,即将ShapeDrawable的高度分成多少份。

1.2、常用函数

1、setBounds

指定当ShapeDrawable在当前控件中的显示位置。

2、getPaint

获取ShapeDrawable自带的画笔。

3、setShader

4、setAlpha

5、setColorFilter

6、setIntrinsicHeigh和setIntrinsicWidth

设置默认宽高。当Drawable以setBackgroundDrawable及setImageDrawable方式使用时,会使用默认宽度和默认高度来计算当前Drawable的大小与位置。如果不设置,则默认的宽高、都是-1px。

7、setPadding

设置边距

自定义Drawable

public class CustomDrawable extends Drawable {
    @Override
    public void draw(@NonNull Canvas canvas) {
        
    }

    @Override
    public void setAlpha(int alpha) {

    }

    @Override
    public void setColorFilter(@Nullable ColorFilter colorFilter) {

    }

    @Override
    public int getOpacity() {
        return 0;
    }
}

这4个函数是Drawable类里的虚函数,是必须实现的。

draw()函数是我们将会用到的,与View类似,传入的参数是一个Canvas对象,我们只需要调用Canvas的一些方法,效果就会直接显示在 Drawable 上。

setAlpha()和 setColorFilter()函数是非常容易实现的。当外层调用CustomDrawable 的这两个函数时,我们只需要将对应的参数传给CustomDrawable的 Paint即可。

getOpacity():当外部需要知道我们自定义的CustomDrawable的显示模式时会调用这个函数。它有4个取值: PixelFormat.UNKNOWN,TRANSLUCENT,TRANSPARENT,OPAQUE。其中,PixelFormat.TRANSLUCENT表示当前CustomDrawable的绘图是具有Alpha通道的,即使用CustomDrawable后,其底部的图像仍有可能看得到; PixelFormat.TRANSPARENT表示当前CustomDrawable是完全透明的,其中什么都没画,如果使用CustomDrawable,则将完全显示其底部图像;PixelFormat.OPAQUE表示当前的CustomDrawable是完全没有Ahpa通道的,使用CustomDrawable后,其底层的图像将被完全覆盖,而只显示 CustomDrawable本身的图像;PixelFormat.UNKNOWN表示未知。一般而言,如果我们不知道该如何返回,则直接返回PixelFormat.TRANSLUCENT是最靠谱的做法。

总结:

  • 当使用setlmageDrawable(drawable)函数来设置ImageView数据源时,自定义Drawable 的位置和大小与ImageView的scaleType有关。
  • 当使用setBackgroundDrawable(drawable)函数来设置 View 背景时,自定义Drawable 的宽、高与控件大小- -致,控件的宽、高则选取本身宽、高和自定义Drawable宽、高 中的最大值。

二、Bitmap

decodeFile

decodeFileDescriptor

通过 BitmapFactory.decodeFileDescriptor 解析的方式比使用BitmapFactory. decodeFile更节省内存。

2.1、BitmapFactory.Options

这个参数的作用非常大,它可以设置Bitmap的采样率,通过改变图片的宽度、高度、缩放比例等,以达到减少图片的像素的目的。总的来说,通过设置这个值,可以更好地控制、显示、使用Bitmap(位图)。我们在实际开发中可以灵活使用该值,以降低0OM的发生概率。

以in开头的代表的就是设置某某参数;以out开头的代表的就是获取某某参数。 比如,inSampleSize就是设置Bitmap的缩放比例,outWidth就是获取Bitmap高度。

public boolean inJustDecodeBounds;
public int inSampleSize;
public int inDensity; //用于设置文件所在资源文件夹的屏幕分辨率。
public int inTargetDensity; //表示真实显示的屏幕分辨率
public int inScreenDensity;
public boolean inScaled; //在需要缩放时,是否对当前文件进行缩放,默认为true
public Bitmap.Config inPreferredConfig;  //设置像素的存储格式的。图片的像素存储格式有ALPHA_8、RGB 565、ARGB_4444、RGB_8888 默认使用 ARGB_8888。
public int outwidth;
public int outHeight;
public String outMimeType;
  1. inJustDecodeBounds 获取图片信息 如果将这个字段设置为true, 则表示只解析图片信息,不获取图片,不分配内存。能获 取的信息有图片的宽度、高度和图片的MIME类型。图片的宽度、高度通过options.outWidth (图片的原始宽度)和options.outHeight (图片的原始高度)返回;图片的MIME类型通过 options.outMimeType返回。

  2. inSampleSize 压缩图片 这个字段表示采样率。采样率的全称是采样频率,是指每隔多少个样本采样一次作为结果。比如,将这个字段设置为4,意思就是从原本图片的4个像素中取一个像素作为结果返回,其余的都被丢弃,这样,结果图片的宽和高都为原来的1/4。 同样,如果将这个字段设置为16,意思就是从每16个像素中取一个像素返回,同样,宽和高都为原来的1/16。很明显,采样率越大,图片越小,同时图片越失真。 针对inSampleSize的值,官方建议取2的幂数,比如1、2、4、8、16等,否则会被系统向下取整并找到一个最接近的值。不能取小于1的值,否则系统将一直使用1来作为采样率。 设置 inSampleSize 时应该注意使得缩放后的图片尺寸尽量大于等于相应的 lmageView 大小

public static Bitmap getScaledBitmap(String path, int destWidth, int destHeight) {
    // Read in the dimensions of the image on disk
    BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    BitmapFactory.decodeFile(path, options);

    float srcWidth = options.outWidth;
    float srcHeight = options.outHeight;

    // Figure out how much to scale down by
    int inSampleSize = 1;
    if (srcHeight > destHeight || srcWidth > destWidth) {
        float heightScale = srcHeight / destHeight;
        float widthScale = srcWidth / destWidth;

        inSampleSize = Math.round(Math.max(heightScale, widthScale));
    }

    options.inJustDecodeBounds = false;
    options.inSampleSize = inSampleSize;

    // Read in and create final bitmap
    return BitmapFactory.decodeFile(path, options);
}

image.png

image.png

image.png

image.png

image.png

image.png

image.png

image.png

public int getBitmapSize(Bitmap bitmap) {
    //API 19 
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
        return bitmap.getAllocationByteCount();
    }
    // API 12
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1) {
        return bitmap.getByteCount();
    }
    // 更早版本
    return bitmap.getRowBytes() * bitmap.getHeight();
}

image.png image.png

image.png

该如何解决这个问题?这里有两种选择 避免重绘。 在重绘前清空 Bitmap

image.png

image.png

image.png

image.png

image.png

三、Picture