View背后不为人知的勾当(二)--onDraw

内容太多,尚未完工,估计得写到这个周末

  • 需要知道的问题
    • 点线面和Paint
    • 图形变换,色彩混合,Shader,网格等
    • 16ms和掉帧问题
    • SurfaceView和TextureView
    • 硬件加速

1 关于onDraw

1.1 16ms和掉帧的问题

得知道16ms一帧的问题,如果渲染一帧超过16ms,如用了20ms渲染,下一帧的切换就来不及,这一帧就持续了32ms,就出现了掉帧,掉帧多了,用户就会感觉到界面卡顿

1.2 onDraw基本套路

  • 不要在onDraw里new可以重用的对象,onDraw里的性能必须严格考虑
/**
 * 自定义控件:绘制一个圆环
 */
public class CustomView1 extends View{

    private Paint mPaint;

    public CustomView1(Context context) {
        super(context);
        initPaint();
    }

    public CustomView1(Context context, AttributeSet attrs) {
        super(context, attrs);
        initPaint();
    }

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

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public CustomView1(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        initPaint();
    }

    private void initPaint(){
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        /*
         * 设置画笔样式为描边,圆环嘛……当然不能填充不然就么意思了
         *
         * 画笔样式分三种:
         * 1.Paint.Style.STROKE:描边
         * 2.Paint.Style.FILL_AND_STROKE:描边并填充
         * 3.Paint.Style.FILL:填充
         */
        mPaint.setStyle(Paint.Style.STROKE);

        // 设置画笔颜色为浅灰色
        mPaint.setColor(Color.LTGRAY);

        /*
         * 设置描边的粗细,单位:像素px
         * 注意:当setStrokeWidth(0)的时候描边宽度并不为0而是只占一个像素
         */
        mPaint.setStrokeWidth(10);

    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        // 绘制圆环
        canvas.drawCircle(Display.screenWidth / 2, Display.screenHeight / 2, radius, mPaint);

    }

    private float radius = 50;

    public void setRadius(float r){
        this.radius = r;
        if(this.radius >= 150) this.radius = 50;
        postInvalidate();
    }
}
  • 效果就是画了一个圆环,至于padding和wrap_content的考虑,请参考上一篇文章
  • 如果你在外部用动画或者其他方式不断调用setRadius,圆环就动起来了

  • 这里注意invalidate和postInvalidate,还有requestLayout

    • invalidate:当前View重绘,只能在UI线程调用
    • postInvalidate:当前View重绘,不管在哪个线程调用,都在UI线程执行
    • requestLayout:请求父控件重新measure和layout,当然此控件本身也会重绘

可惜这里没图

2 canvas和paint入门

canvas是纸,paint是笔

@Override  
protected void onDraw(Canvas canvas) {  
    super.onDraw(canvas);  
    Paint paint = new Paint();  //just demo
    paint.setAntiAlias(true);  
} 

3 Canvas

3.1 能画什么

canvas.drawARGB();
canvas.drawColor();
canvas.drawPaint();

canvas.drawPoint();
canvas.drawPoints();

canvas.drawLine();
canvas.drawLines();
canvas.drawPath();

canvas.drawArc();
canvas.drawCircle();
canvas.drawOval();
canvas.drawRect();
canvas.drawRoundRect();

canvas.drawText();
canvas.drawTextOnPath();
canvas.drawTextRun();

canvas.drawBitmap();
canvas.drawPicture();

canvas.drawVertices();

canvas.translate();
canvas.scale();
canvas.rotate();
canvas.skew();

canvas.setDrawFilter();
canvas.setMatrix();
canvas.setDensity();
canvas.setBitmap();


canvas.save();
canvas.saveLayer();
canvas.restore();
canvas.restoreToCount();

canvas.clipPath();
canvas.clipRect();

canvas.concat();
canvas.isHardwareAccelerated();
canvas.isOpaque();
canvas.quickReject();

3.2 clip功能:限定绘制区域

3.3 图层变换

4 Paint

4.1 Paint简介

Paint paint = new Paint();

paint.setAntiAlias(true);
或者mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
抗锯齿,让图像边缘显得更圆滑光泽动感的算法
抗锯齿,锯齿是依赖于算法的,算法决定抗锯齿的效率,在我们绘制棱角分明的图像时,比如一个矩形、一张位图,我们不需要打开抗锯齿

mPaint.setDither(false);
抗抖动,使颜色的过渡更柔和

mPaint.set(Paint src);
把另一个画笔的属性设置Copy过来

mPaint.setAlpha(255);  //[0..255]
mPaint.setARGB(a, r, g, b);  // 和setColor一样,这里每个值都是(0..255)
mPaint.setColor(Color.BLUE);

mPaint.setFlags(int flags);

mPaint.setStyle(Paint.Style style);
Paint.Style.FILL    填充
Paint.Style.STROKE   画边框
Paint.Style.FILL_AND_STROKE 即填充,也画边框

//边框样式
mPaint.setStrokeCap(Cap cap);
mPaint.setStrokeJoin(Join join);
mPaint.setStrokeMiter(float miter);
mPaint.setStrokeWidth(float width);

//什么
mPaint.setFilterBitmap(false);

//颜色矩阵:对颜色进行过滤,即对颜色进行变换,可以实现滤镜
//ColorMatrixColorFilter
//LightingColorFilter:光照颜色过滤
//PorterDuffColorFilter:画布图像和指定颜色的混合模式
mPaint.setColorFilter();

//BlurMaskFilter,模糊处理,阴影效果; EmbossMaskFilter,浮雕处理,使图像凸起,有立体感
mPaint.setMaskFilter(MaskFilter mf);

//路径
mPaint.setPathEffect(PathEffect pe);

//图像混合:画两层图像,指定二者的混合算法,就能得到各种效果
mPaint.setXfermode(Xfermode xfermode);

//BitmapShader:使用一个位图作为画刷,用这个位图来画图形
//LinearGradient:线性渐变
//RadialGradient:辐射渐变
//SweepGradient:扫描渐变
//可以画阴影,遮罩等,功能也很强大
mPaint.setShader(Shader shader);

//已废弃
//设置光栅,光栅这东西涉及太多太多物理知识,而且该方法同样不支持HW在API 21中遗弃了
mPaint.setRasterizer(Resterizer rasterizer);

mPaint.setShadowLayer(float radius, float dx, float dy, int shadowColor);


///画文字相关的,后面会单独分析字体相关
mPaint.setTextAlign(Align align);
mPaint.setTextLocale(Locale.CHINESE);
mPaint.setTextScaleX(float scaleX);
mPaint.setTextSize(float textSize);
mPaint.setTextSkewX(float skewZ);
mPaint.setTypeface(Typeface typeface);

mPaint.setFakeBoldText(false);
mPaint.setLinearText(false);
mPaint.setStrikeThruText(false);
mPaint.setUnderlineText(false);
mPaint.setSubpixelText(false);
mPaint.setHinting(int mode);

抗锯齿效果:

抗抖动效果

大家看到的这张七彩渐变图是一张RGB565模式下图片,即便图片不是很大我们依然可以很清晰地看到在两种颜色交接的地方有一些色块之类的东西感觉很不柔和,因为在RGB模式下只能显示2^16=65535种色彩,因此很多丰富的色彩变化无法呈现,而Android呢为我们提供了抗抖动这么一个方法,它会将相邻像素之间颜色值进行一种“中和”以呈现一个更细腻的过渡色
放大来看,其在很多相邻像素之间插入了一个“中间值”
抗抖动不是Android的专利,是图形图像领域的一种解决位图精度的技术

4.2 ColorFilter:颜色矩阵

4.2.1 ColorMatrixColorFilter:滤镜
4.2.2 LightingColorFilter:光照滤镜
4.2.3 PorterDuffColorFilter:图片和指定颜色的混合

4.3 Xfermode:图像混合

AvoidXfermode和PixelXorXfermode已被废弃(因为不支持硬件加速在API 16已经过时了)

常用的就是PorterDuffXfermode(图形混合模式)

4.4 Shader

4.4.1 BitmapShader

4.4.2 渐变

LinearGradient:线性渐变
RadialGradient:辐射渐变
SweepGradient:扫描渐变
渐变可以给图像加浮层,周围最模糊,中间最清楚

4.5 MaskFilter:遮罩过滤器

BlurMaskFilter和EmbossMaskFilter

4.6 Canvas.drawBitmapMesh

网格化

4.7 PathEffect

4.8 字体相关

5 你还能在哪儿draw

  • 可以draw的地方
    • View的onDraw
    • Drawable
    • SurfaceView
    • TextureView

5.1 自定义Drawable

下面自定义一个圆角显示图片的Drawable,可以作为ImageView的src,或者作为background

package com.ayoview.sample.deepmind;

import android.graphics.Bitmap;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.RectF;
import android.graphics.Shader;
import android.graphics.drawable.Drawable;

import org.ayo.jlog.JLog;

/**
 * 圆角drawable
 * 实现方式是将Bitmap作为BitmapShader,来画一个圆角矩形
 * 注意draw方法里的canvas.drawRoundRect()方法,这里canvas画个什么形状,就可以显示什么形状的图片
 */
public class RoundImageDrawable extends Drawable{

    private Paint mPaint;
    private Bitmap mBitmap;
    private RectF rectF;

    public RoundImageDrawable(Bitmap bitmap){
        this.mBitmap = bitmap;

        //初始化paint
        BitmapShader shader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        mPaint.setShader(shader);
    }

    /**
     这个方法由外部父控件调用,这里把drawable对象设置给谁,谁就是其父控件
     这里是由外部来定义Drawable的最终大小,这是可以理解的,因为Drawable作为View的附属,
     虽然可以通过getIntrinsicWidth和getIntrinsicHeight来作为View的宽高为wrap_content
     时的参考,但并不具有决定作用
     大多数情况下,Drawable大小是由其所属View决定
     所以这里设置的四个值,必须得到你足够的重视,在draw时,要根据这四个值来
     你可以认为这里的值是控件被测量和layout之后的结果
     */
    @Override
    public void setBounds(int left, int top, int right, int bottom) {
        super.setBounds(left, top, right, bottom);
        JLog.i("RoundImageDrawable", "setBounds--" + left + ", " + top + ", " + right + ", " + bottom);
        rectF = new RectF(left, top, right, bottom);
    }

    @Override
    public void draw(Canvas canvas)
    {
        canvas.drawRoundRect(rectF, 30, 30, mPaint);
    }

    //getIntrinsicWidth、getIntrinsicHeight主要是为了在View使用wrap_content的时候,提供一下尺寸,默认为-1可不是我们希望的。
    @Override
    public int getIntrinsicWidth()
    {
        JLog.i("RoundImageDrawable", "getIntrinsicWidth--bitmap宽-" + mBitmap.getWidth());
        return mBitmap.getWidth();
    }

    @Override
    public int getIntrinsicHeight()
    {
        JLog.i("RoundImageDrawable", "getIntrinsicHeight--bitmap高-" + mBitmap.getHeight());
        return mBitmap.getHeight();
    }

    @Override
    public void setAlpha(int alpha)
    {
        mPaint.setAlpha(alpha);
    }

    /**
     * Specify a color and Porter-Duff mode to be the color filter for this
     * drawable.
     */
    @Override
    public void setColorFilter(ColorFilter cf)
    {
        mPaint.setColorFilter(cf);
    }

    @Override
    public int getOpacity()
    {
        return PixelFormat.TRANSLUCENT;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值