目录介绍

  • 1.Paint画笔介绍

    • 1.1 图形绘制
    • 1.2 文本绘制
  • 2.Canvas画布介绍
    • 2.1 设置属性
    • 2.2 画图【重点】
    • 2.3 Canvas对象的获取方式
    • 2.4 Canvas的作用
    • 2.5 Canvas绘制圆和椭圆
    • 2.6 Canvas绘制矩形、圆角矩形
    • 2.7 Canvas绘制文字
    • 2.8 Canvas绘制弧形、封闭弧形
    • 2.9 Canvas绘制Path路径
  • 3.Matrix变换矩阵介绍
    • 3.1 translate平移
    • 3.2 rorate旋转
    • 3.3 scale缩放
    • 3.4 skew扭曲
  • 4.RectF介绍
    • 4.1 Rect简单属性
    • 4.2 Rect父类的实现
    • 4.3 Rect常用的一些方法
  • 5.关于使用到这几个属性的自定义View
    • 5.0 知道了这几个,需要练手写下案例
    • 5.1 自定义轮播图圆点
    • 5.2 自定义圆环百分比进度条

好消息

1.Paint画笔介绍

  • Paint即画笔,在绘图过程中起到了极其重要的作用,画笔主要保存了颜色, 样式等绘制信息,指定了如何绘制文本和图形,画笔对象有很多设置方法,大体上可以分为两类,一类与图形绘制相关,一类与文本绘制相关。

1.1 图形绘制

  • 常用的方法有这些

    * setARGB(int a,int r,int g,int b);
    设置绘制的颜色,a代表透明度,r,g,b代表颜色值。
    * setAlpha(int a);
    设置绘制图形的透明度。
    * setColor(int color);
    设置绘制的颜色,使用颜色值来表示,该颜色值包括透明度和RGB颜色。
    * setAntiAlias(boolean aa);
    设置是否使用抗锯齿功能,会消耗较大资源,绘制图形速度会变慢。
    * setDither(boolean dither);
    设定是否使用图像抖动处理,会使绘制出来的图片颜色更加平滑和饱满,图像更加清晰
    * setFilterBitmap(boolean filter);
    如果该项设置为true,则图像在动画进行中会滤掉对Bitmap图像的优化操作,加快显示速度,本设置项依赖于dither和xfermode的设置
    * setMaskFilter(MaskFilter maskfilter);
    设置MaskFilter,可以用不同的MaskFilter实现滤镜的效果,如滤化,立体等
    * setColorFilter(ColorFilter colorfilter);
    设置颜色过滤器,可以在绘制颜色时实现不用颜色的变换效果
    * setPathEffect(PathEffect effect);
    设置绘制路径的效果,如点画线等
    * setShader(Shader shader);
    设置图像效果,使用Shader可以绘制出各种渐变效果
    * setShadowLayer(float radius ,float dx,float dy,int color);
    在图形下面设置阴影层,产生阴影效果,radius为阴影的角度,dx和dy为阴影在x轴和y轴上的距离,color为阴影的颜色
    * setStyle(Paint.Style style);
    设置画笔的样式,为FILL,FILL_AND_STROKE,或STROKE
    * setStrokeCap(Paint.Cap cap);
    当画笔样式为STROKE或FILL_AND_STROKE时,设置笔刷的图形样式,如圆形样式 Cap.ROUND,或方形样式Cap.SQUARE
    * setSrokeJoin(Paint.Join join);
    设置绘制时各图形的结合方式,如平滑效果等
    * setStrokeWidth(float width);
    当画笔样式为STROKE或FILL_AND_STROKE时,设置笔刷的粗细度
    * setXfermode(Xfermode xfermode);
    设置图形重叠时的处理方式,如合并,取交集或并集,经常用来制作橡皮的擦除效果

1.2 文本绘制

  • 常用的方法有这些

    * setFakeBoldText(boolean fakeBoldText);
    模拟实现粗体文字,设置在小字体上效果会非常差
    * setSubpixelText(boolean subpixelText);
    设置该项为true,将有助于文本在LCD屏幕上的显示效果
    * setTextAlign(Paint.Align align);
    设置绘制文字的对齐方向
    * setTextScaleX(float scaleX);
    设置绘制文字x轴的缩放比例,可以实现文字的拉伸的效果
    * setTextSize(float textSize);
    设置绘制文字的字号大小
    * setTextSkewX(float skewX);
    设置斜体文字,skewX为倾斜弧度
    * setTypeface(Typeface typeface);
    设置Typeface对象,即字体风格,包括粗体,斜体以及衬线体,非衬线体等
    * setUnderlineText(boolean underlineText);
    设置带有下划线的文字效果
    * setStrikeThruText(boolean strikeThruText);
    设置带有删除线的效果

2.Canvas画布介绍

  • 当我们调整好画笔之后,现在需要绘制到画布上,这就得用Canvas类了。在android中既然把Canvas当做画布,那么就可以在画布上绘制我们想要的任何东西。除了在画布上绘制之外,还需要设置一些关于画布的属性,比如,画布的颜色、尺寸等。

2.1 设置属性

  • 一般属性有:

    * Canvas(Bitmap bitmap): 以bitmap对象创建一个画布,则将内容都绘制在bitmap上,因此bitmap不得为null。
    * Canvas(GL gl): 在绘制3D效果时使用,与OpenGL相关。
    * isOpaque(boolean isOpaque):检测是否支持透明。
    * setViewport(int left, int top, int right, int bottom, int clipflag): 设置画布中显示窗口。
    * drawColor(int color): 设置Canvas的背景颜色。
    * setBitmap(Bitmap mBitmap): 设置具体画布,画的内容,保存为一个Bitmap。
    * clipRect(float left, float top, float right, float bottom): 设置显示区域,即设置裁剪区。
    * translate(float x, float y): 平移画布。
    * rotate(float degree, float px, float py): 旋转画布 。
    * skew(float sx, float sy): 设置偏移量。
    * save(): 将Canvas当前状态保存在堆栈,save之后可以调用Canvas的平移、旋转、错切、剪裁等操作。
    * restore(): 恢复为之前堆栈保存的Canvas状态,防止save后对Canvas执行的操作对后续的绘制有影响。restore和save要配对使用,restore可以比save少,但不能比save多,否则会引发error。save和restore之间,往往夹杂的是对Canvas的特殊操作。
    * save(int num):将Canvas当前状态保存在堆栈,并予以编号int
    * restoreToCount(int num):恢复为之前堆栈保存的编号为int的Canvas状态
    * concat(Matrix matrix):画布关联矩阵,画出来的内容按矩阵改变,而不是画布改变。
    * Drawable.draw(Canvas canvas):将Drawable画到Canvas中
    注:这种方式画Drawable怎么设置透明度呢?((BitmapDrawable)Drawable).getPaint().setAlpha(mBgAlpha);

2.2 画图【重点】

  • 画图部分

    *  canvas.drawPaint(Paint paint)
    将画笔设置的颜色和透明度铺满画布
    * drawRect(RectF rect, Paint paint)
    绘制矩形,参数一为RectF一个区域
    * drawRect(float left, float top, float right, float bottom, Paint paint)
    绘制矩形,left:矩形left的x坐标,top:矩形top的y坐标,right:矩形right的x坐标,bottom:矩形bottom的y坐标
    * drawRoundRect(RectF rect, float rx, float ry, Paint paint)
    绘制圆角矩形, rx:x方向的圆角半径,ry:y方向的圆角半径
    * drawRoundRect(float left, float top, float right, float bottom, float rx, float ry, Paint paint)
    * drawPath(Path path, Paint paint)
    绘制一个路径,参数一为Path路径对象
    * drawBitmap(Bitmap bitmap, Rect src, Rect dst, Paint paint)
    贴图,参数一就是我们常规的Bitmap对象,参数二是源区域(这里是bitmap),参数三是目标区域(应该在canvas的位置和大小),参数四是Paint画刷对象,因为用到了缩放和拉伸的可能,当原始Rect不等于目标Rect时性能将会有大幅损失。
    * drawBitmap (Bitmap bitmap, float left, float top, Paint paint)
    * drawLine(float startX, float startY, float stopX, float stopY, Paintpaint)
    画线,参数一起始点的x轴位置,参数二起始点的y轴位置,参数三终点的x轴水平位置,参数四y轴垂直位置,最后一个参数为Paint 画刷对象。
    * drawPoint(float x, float y, Paint paint)
    画点,参数一水平x轴,参数二垂直y轴,第三个参数为Paint对象。
    * drawText(String text, float x, floaty, Paint paint)
    渲染文本,Canvas类除了上面的还可以描绘文字,参数一是String类型的文本,参数二文字左侧到x轴距离,参数三文字BaseLine到y轴距离,参数四是Paint对象。
    * drawOval(RectF oval, Paint paint)
    绘制椭圆,参数一是扫描区域,参数二为paint对象
    * drawOval(float left, float top, float right, float bottom, Paint paint)
    * drawCircle(float cx, float cy, float radius,Paint paint)
    绘制圆,参数一是中心点的x轴,参数二是中心点的y轴,参数三是半径,参数四是paint对象;
    * drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter, Paint paint)
    画弧,参数一是RectF对象,指定圆弧的外轮廓矩形区域,参数二是起始角(度)在电弧的开始,参数三扫描角(度)开始顺时针测量的,参数四是如果这是真的话,包括椭圆中心的电弧,并关闭它,如果它是假这将是一个弧线,参数五是Paint对象;

2.3 Canvas对象的获取方式

  • 2.3.1 Canvas对象的获取方式有两种:

    • 第一种通过重写View.onDraw方法,View中的Canvas对象会被当做参数传递过来,操作这个Canvas,效果会直接反应在View中。
    • 第二种通过new创建一个Canvas对象
    • 代码如下所示
    @Override
    protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    } Canvas canvas = new Canvas();

2.4 Canvas的作用

  • Canvas可以绘制的对象有:弧线(arcs)、填充颜色(argb和color)、Bitmap、圆(circle和oval)、点(point)、线(line)、矩形(Rect)、图片(Picture)、圆角矩形(RoundRect)、文本(text)、顶点(Vertices)、路径(path)。

2.5 Canvas绘制圆和椭圆

  • 绘制圆

    private Paint paint = new Paint();
    
    @Override
    protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    paint.setAntiAlias(true);
    paint.setColor(Color.BLUE);
    paint.setStyle(Paint.Style.FILL);
    canvas.drawCircle(200,200,100 , paint);
    }

2.6 Canvas绘制矩形、圆角矩形

  • 如下所示
  • private Paint paint = new Paint();
    
    @Override
    protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    paint.setAntiAlias(true);
    paint.setColor(Color.RED);
    paint.setStyle(Paint.Style.FILL);
    canvas.drawRect(100, 100, 200, 200, paint);
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
    canvas.drawRoundRect(400, 100, 600, 300, 30, 30, paint);
    }
    paint.setStyle(Paint.Style.STROKE);
    paint.setStrokeWidth(20);
    canvas.drawRect(100, 400, 300, 600, paint);
    }

2.7 Canvas绘制文字

  • Canvas绘制文字
  • private Paint paint = new Paint();
    
    @SuppressLint("DrawAllocation")
    @Override
    protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    paint.setAntiAlias(true);
    paint.setColor(Color.RED);
    paint.setTextSize(100);
    canvas.drawText("潇湘剑雨", 100, 100, paint);
    }

2.8 Canvas绘制弧形、封闭弧形

  • 绘制弧形、封闭弧形
  • private Paint paint = new Paint();
    
    @SuppressLint("DrawAllocation")
    @Override
    protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    paint.setAntiAlias(true);
    paint.setColor(Color.RED);
    RectF rel = new RectF(50, 50, 150, 150);
    //实心圆弧
    canvas.drawArc(rel, 0, 135, false, paint);
    //实心圆弧 将圆心包含在内
    RectF rel2 = new RectF(50, 200, 150, 300);
    canvas.drawArc(rel2, 0, 135, true, paint);
    //设置空心Style
    paint.setStyle(Paint.Style.STROKE);
    paint.setStrokeWidth(20);
    RectF rel3 = new RectF(50, 350, 150, 450);
    canvas.drawArc(rel3, 0, 270, false, paint);
    RectF rel4 = new RectF(50, 250, 150, 600);
    canvas.drawArc(rel4, 0, 270, true, paint);
    }

2.9 Canvas绘制Path路径

  • Canvas绘制Path路径
  • private Paint paint = new Paint();
    @SuppressLint("DrawAllocation")
    @Override
    protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    Path angle = new Path();
    angle.moveTo(250, 0);
    angle.lineTo(0, 500);
    angle.lineTo(100, 300);
    angle.lineTo(200, 350);
    angle.lineTo(500, 500);
    angle.close();
    canvas.drawPath(angle, paint);
    }

3.Matrix变换矩阵介绍【Canvas位置转换】

  • 思考:如果要画一个仪表盘(数字围绕显示在一个圆圈中),或者类似钟表指针样的控件,如何实现?
  • Android还提供了一些对Canvas位置转换的方法:rorate、scale、translate、skew(扭曲)等,而且它允许你通过获得它的转换矩阵对象(getMatrix方法)直接操作它。这些操作就像是虽然你的笔还是原来的地方画,但是画纸旋转或者移动了,所以你画的东西的方位就产生变化。为了方便一些转换操作,Canvas还提供了保存和回滚属性的方法(save和restore),比如你可以先保存目前画纸的位置(save),然后旋转90度,向下移动100像素后画一些图形,画完后调用restore方法返回到刚才保存的位置。

3.1 translate平移

3.2 rorate旋转

  • rorate旋转
  • private Paint mPaint = new Paint();
    
    @SuppressLint("DrawAllocation")
    @Override
    protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    canvas.drawColor(Color.BLUE);
    mPaint.setColor(Color.RED);
    canvas.drawRect(new Rect(0, 0, 800, 800), mPaint);
    canvas.save();
    mPaint.setColor(Color.GREEN);
    canvas.rotate(45,400,400);
    canvas.drawRect(new Rect(0, 0, 800, 800), mPaint);
    canvas.restore();
    }
  • 源代码有两个可以使用的方法:
    /**
    * Preconcat the current matrix with the specified rotation.
    * @param degrees The amount to rotate, in degrees
    */
    public native void rotate(float degrees);
    /**
    * Preconcat the current matrix with the specified rotation.
    * @param degrees The amount to rotate, in degrees
    * @param px The x-coord for the pivot point (unchanged by the rotation)
    * @param py The y-coord for the pivot point (unchanged by the rotation)
    */
    public final void rotate(float degrees, float px, float py) {
    translate(px, py);
    rotate(degrees);
    translate(-px, -py);
    }

3.3 scale缩放

  • scale缩放
  • private Paint mPaint = new Paint();
    
    @SuppressLint("DrawAllocation")
    @Override
    protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    canvas.drawColor(Color.YELLOW);
    mPaint.setColor(Color.RED);
    canvas.drawRect(new Rect(0, 0, 800, 800), mPaint);
    // 保存画布状态
    canvas.save();
    canvas.scale(0.5f, 0.5f);
    mPaint.setColor(Color.GREEN);
    canvas.drawRect(new Rect(0, 0, 800, 800), mPaint);
    // 画布状态回滚
    canvas.restore();
    canvas.scale(0.5f, 0.5f, 400, 400);
    mPaint.setColor(Color.BLUE);
    canvas.drawRect(new Rect(0, 0, 800, 800), mPaint);
    }
  • 源码如下所示
    /**
    * Preconcat the current matrix with the specified scale.
    * @param sx The amount to scale in X
    * @param sy The amount to scale in Y
    */
    public native void scale(float sx, float sy); /**
    * Preconcat the current matrix with the specified scale.
    * @param sx The amount to scale in X
    * @param sy The amount to scale in Y
    * @param px The x-coord for the pivot point (unchanged by the scale)
    * @param py The y-coord for the pivot point (unchanged by the scale)
    */
    public final void scale(float sx, float sy, float px, float py) {
    translate(px, py);
    scale(sx, sy);
    translate(-px, -py);
    }

3.4 skew扭曲

4.RectF介绍

4.1 Rect简单属性

  • 这是一个我们常用的一个“绘画相关的工具类”,常用语描述长方形/正方形,他只有4个属性

    public int left;
    public int top;
    public int right;
    public int bottom;
  • 其中常用的构造方法如下所示
    public Rect(int left, int top, int right, int bottom) {
    this.left = left;
    this.top = top;
    this.right = right;
    this.bottom = bottom;
    } public Rect(Rect r) {
    if (r == null) {
    left = top = right = bottom = 0;
    } else {
    left = r.left;
    top = r.top;
    right = r.right;
    bottom = r.bottom;
    }
    }
  • 这4个属性描述着这一个“方块”,但是这有一个知识点需要理清楚,先看这张图

4.2 Rect父类的实现

  • 实现了Parcelable 所以需要实现一堆Object的方法,诸如equals,toString等等,来简单看一看

    • 对于equals方法,首先先对传来的对象进行判空,类型判断,再强转成Rect对象,最后还是一个个去比对那4个属性。
    @Override
    public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false; Rect r = (Rect) o;
    return left == r.left && top == r.top && right == r.right && bottom == r.bottom;
    } @Override
    public int hashCode() {
    int result = left;
    result = 31 * result + top;
    result = 31 * result + right;
    result = 31 * result + bottom;
    return result;
    } @Override
    public String toString() {
    StringBuilder sb = new StringBuilder(32);
    sb.append("Rect("); sb.append(left); sb.append(", ");
    sb.append(top); sb.append(" - "); sb.append(right);
    sb.append(", "); sb.append(bottom); sb.append(")");
    return sb.toString();
    }

4.3 Rect常用的一些方法

  • 获取“宽”

    //文章开头说的公式在这里得到了应验
    public final int width() {
    return right - left;
    }
  • 获取“高”
    public final int height() {
    return bottom - top;
    }
  • 有效性的判断
    //因为left是最左侧,right比left还小不就不成形了么?宽高同是如此
    public final boolean isEmpty() {
    return left >= right || top >= bottom;
    }
  • 全部置0操作
    public void setEmpty() {
    left = right = top = bottom = 0;
    }
  • 设置参数方法,和构造函数的区别仅在于不会创建新对象
    public void set(int left, int top, int right, int bottom) {
    this.left = left;
    this.top = top;
    this.right = right;
    this.bottom = bottom;
    }

5.关于使用到这几个属性的自定义View

  • 上面比较详细介绍了Canvas,Paint,Matrix,RectF等等的属性,作用,常用方法,接下来就需要结合具体业务需求练手写一下小案例自定义控件呢

5.1 自定义轮播图圆点

  • 5.1.1 需求介绍

    • 绘制圆环,一个实心中心圆,还有一个外圆环
    • 此控件可以设置宽度和高度,可以设置颜色
  • 5.1.2 思路介绍
    • 3.2.1 既然是绘制圆形,可以写一个继承View的自定义view
    • 3.2.2 重写onDraw方法,获取控件宽高,然后比较宽高值,取小值的一半作为圆的半径
    • 3.2.3 然后分别绘制选中状态和未选中状态的圆
    • 3.2.4 创建画笔Paint,并且设置相关属性,比如画笔颜色,类型等
    • 3.2.5 利用canvas绘制圆,然后再又用相同方法绘制外边缘
    • 3.2.6 自定义一个是否选中状态的方法,传入布尔值是否选中,然后调用view中invalidate方法
  • 5.1.3 代码介绍
    • 具体代码如下所示:
    /**
    * <pre>
    * @author yangchong
    * blog : https://github.com/yangchong211
    * time : 2016/5/18
    * desc : 红点自定义控件
    * revise: 建议设置红点宽高一样,否则是椭圆
    * </pre>
    */
    public class DotView extends View { private boolean isInit = false;
    private boolean isSelected = false;
    private float mViewHeight;
    private float mViewWidth;
    private float mRadius;
    private Paint mPaintBg = new Paint();
    private int mBgUnselectedColor = Color.parseColor("#1A000000");
    private int mBgSelectedColor = Color.parseColor("#FDE26E");
    private static final float mArcWidth = 2.0f; public DotView(Context context) {
    super(context);
    } public DotView(Context context, AttributeSet attrs) {
    super(context, attrs);
    } public DotView(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    } @Override
    protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    if (!isInit) {
    isInit = true;
    mViewHeight = getHeight();
    mViewWidth = getWidth();
    if (mViewHeight >= mViewWidth) {
    mRadius = mViewWidth / 2.f;
    } else {
    mRadius = mViewHeight / 2.f;
    }
    } //是否选中
    if (isSelected){
    drawSelectedDot(canvas);
    } else{
    drawUnSelectedDot(canvas);
    }
    } /**
    * 绘制选中指示器红点
    * @param canvas canvas
    */
    private void drawSelectedDot(Canvas canvas) {
    //设置paint相关属性
    mPaintBg.setAntiAlias(true);
    mPaintBg.setColor(mBgSelectedColor);
    mPaintBg.setStyle(Style.FILL); //绘制圆
    canvas.drawCircle(mViewWidth / 2.f, mViewHeight / 2.f, mRadius - 8.f, mPaintBg); mPaintBg.setStyle(Style.STROKE);
    float offset = 1.f + mArcWidth;
    RectF oval = new RectF(mViewWidth / 2.f - mRadius + offset, mViewHeight / 2.f - mRadius + offset,
    mViewWidth / 2.f + mRadius - offset, mViewHeight / 2.f + mRadius - offset); //绘制指定的弧线,该弧线将被缩放以适应指定的椭圆形。
    canvas.drawArc(oval, 0.f, 360.f, false, mPaintBg);
    } /**
    * 绘制未选中指示器红点
    * @param canvas canvas
    */
    private void drawUnSelectedDot(Canvas canvas) {
    mPaintBg.setAntiAlias(true);
    mPaintBg.setColor(mBgUnselectedColor);
    mPaintBg.setStyle(Style.FILL);
    canvas.drawCircle(mViewWidth / 2.f, mViewHeight / 2.f, mRadius - 8.f, mPaintBg);
    } /**
    * 设置是否选中
    * @param isSelected isSelected
    */
    public void setIsSelected(boolean isSelected) {
    this.isSelected = isSelected;
    //使整个视图无效。如果视图是可见的,则{@link#onDraw(android.Graphics.Canvas)}将在将来的某个时候被调用。
    //调用该方法,会进行重新绘制,也就是调用onDraw方法
    this.invalidate();
    }
    }

5.2 自定义圆环百分比进度条

  • 5.2.1 需求分析

    • 1.业务需求:可以设置圆角,可以设置圆形,如果是圆角则必须设置半径,默认圆角半径为10dp
    • 2.如果设置了圆形,则即使设置圆角也无效;如果设置非圆形,则圆角生效,同时需要判断圆角半径是否大于控件宽高,处理边界逻辑
    • 3.当设置圆形的时候,即使设置宽高不一样,那么取宽高中的最小值的一半为圆形半径
  • 5.2.2 代码介绍

    • 代码如下所示
    public class ARoundImageView extends AppCompatImageView {
    
        /*
    * Paint:画笔
    * Canvas:画布
    * Matrix:变换矩阵
    *
    * 业务需求:可以设置圆角,可以设置圆形,如果是圆角则必须设置半径,默认圆角半径为10dp
    */
    /**
    * 圆形模式
    */
    private static final int MODE_CIRCLE = 1;
    /**
    * 普通模式
    */
    private static final int MODE_NONE = 0;
    /**
    * 圆角模式
    */
    private static final int MODE_ROUND = 2;
    /**
    * 圆角半径
    */
    private int currRound = dp2px(10);
    /**
    * 画笔
    */
    private Paint mPaint;
    /**
    * 默认是普通模式
    */
    private int currMode = 0; public ARoundImageView(Context context) {
    this(context,null);
    } public ARoundImageView(Context context, AttributeSet attrs) {
    this(context, attrs, 0);
    } public ARoundImageView(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    obtainStyledAttrs(context, attrs, defStyleAttr);
    initViews();
    } private void obtainStyledAttrs(Context context, AttributeSet attrs, int defStyleAttr) {
    TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ARoundImageView, defStyleAttr, 0);
    currMode = a.hasValue(R.styleable.ARoundImageView_type) ? a.getInt(R.styleable.ARoundImageView_type, MODE_NONE) : MODE_NONE;
    currRound = a.hasValue(R.styleable.ARoundImageView_radius) ? a.getDimensionPixelSize(R.styleable.ARoundImageView_radius, currRound) : currRound;
    a.recycle();
    } private void initViews() {
    //ANTI_ALIAS_FLAG 用于绘制时抗锯齿
    mPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
    } /**
    * 当模式为圆形模式的时候,我们强制让宽高一致
    */
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    if (currMode == MODE_CIRCLE) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    int result = Math.min(getMeasuredHeight(), getMeasuredWidth());
    // 此方法必须由{@link#onMeasure(int,int)}调用,以存储已测量的宽度和测量的高度。
    // 如果不这样做,将在测量时触发异常。
    setMeasuredDimension(result, result);
    } else {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }
    } @SuppressLint("DrawAllocation")
    @Override
    protected void onDraw(Canvas canvas) {
    //获取ImageView图片资源
    Drawable mDrawable = getDrawable();
    //获取Matrix对象
    Matrix mDrawMatrix = getImageMatrix();
    if (mDrawable == null) {
    return;
    }
    if (mDrawable.getIntrinsicWidth() == 0 || mDrawable.getIntrinsicHeight() == 0) {
    return;
    }
    if (mDrawMatrix == null && getPaddingTop() == 0 && getPaddingLeft() == 0) {
    mDrawable.draw(canvas);
    } else {
    final int saveCount = canvas.getSaveCount();
    canvas.save();
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
    if (getCropToPadding()) {
    final int scrollX = getScrollX();
    final int scrollY = getScrollY();
    canvas.clipRect(scrollX + getPaddingLeft(), scrollY + getPaddingTop(),
    scrollX + getRight() - getLeft() - getPaddingRight(),
    scrollY + getBottom() - getTop() - getPaddingBottom());
    }
    }
    canvas.translate(getPaddingLeft(), getPaddingTop());
    switch (currMode){
    case MODE_CIRCLE:
    Bitmap bitmap1 = drawable2Bitmap(mDrawable);
    mPaint.setShader(new BitmapShader(bitmap1, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP));
    canvas.drawCircle(getWidth() / 2, getHeight() / 2, getWidth() / 2, mPaint);
    break;
    case MODE_ROUND:
    Bitmap bitmap2 = drawable2Bitmap(mDrawable);
    mPaint.setShader(new BitmapShader(bitmap2, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP));
    canvas.drawRoundRect(new RectF(getPaddingLeft(), getPaddingTop(),
    getWidth() - getPaddingRight(), getHeight() - getPaddingBottom()),
    currRound, currRound, mPaint);
    break;
    case MODE_NONE:
    default:
    if (mDrawMatrix != null) {
    canvas.concat(mDrawMatrix);
    }
    mDrawable.draw(canvas);
    break;
    }
    canvas.restoreToCount(saveCount);
    }
    } /**
    * drawable转换成bitmap
    */
    private Bitmap drawable2Bitmap(Drawable drawable) {
    if (drawable == null) {
    return null;
    }
    Bitmap bitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
    Canvas canvas = new Canvas(bitmap);
    //根据传递的scaleType获取matrix对象,设置给bitmap
    Matrix matrix = getImageMatrix();
    if (matrix != null) {
    canvas.concat(matrix);
    }
    drawable.draw(canvas);
    return bitmap;
    } private int dp2px(float value) {
    return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
    value, getResources().getDisplayMetrics());
    }
    }

关于其他内容介绍

01.关于博客汇总链接

02.关于我的博客

View之Canvas,Paint,Matrix,RectF等介绍的更多相关文章

  1. Android查缺补漏(View篇)--自定义View利器Canvas和Paint详解

    上篇文章介绍了自定义View的创建流程,从宏观上给出了一个自定义View的创建步骤,本篇是上一篇文章的延续,介绍了自定义View中两个必不可少的工具Canvas和Paint,从细节上更进一步的讲解自定 ...

  2. 图形绘制 Canvas Paint Path 详解

    图形绘制简介        Android中使用图形处理引擎,2D部分是android SDK内部自己提供,3D部分是用Open GL ES 1.0.大部分2D使用的api都在android.grap ...

  3. 安卓自定义View进阶-Canvas之画布操作 转载

    安卓自定义View进阶-Canvas之画布操作 转载 https://www.gcssloop.com/customview/Canvas_Convert 本来想把画布操作放到后面部分的,但是发现很多 ...

  4. android 绘图之Canvas,Paint类

    Canvas,Paint 1.在android 绘图但中经常要用到Canvas和Paint类,Canvas好比是一张画布,上面已经有你想绘制图画的轮廓了,而Paint就好比是画笔,就要给Canvas进 ...

  5. android学习7——canvas.concat(Matrix matrix)作用

    canvas.concat的作用可以理解成对matrix的变换应用到canvas上的所有对象. 看下面的代码. public class ConcatMatrixActivity extends Ac ...

  6. 改善用户体验,用图片的自身变化以及进度通知摆脱传统的进度条,okhttp,Canvas,Paint实现

    转载请注明出处:王亟亟的大牛之路 从最開始的白页面等待,到后来的进度条告知用户.到如今的WebBO/微信这样的先下缩略图点击才又一次下大图的方式,我们开发人员对用户感知的注意度越来越高.昨天刷微博的时 ...

  7. 自定义View(4)Canvas和Paint常用绘制函数

    画布Canvas 在Android下进行2D绘图需要Canvas类的支持,它位于"android.graphics.Canvas"包下,直译过来为画布的意思,用于完成在View上的 ...

  8. Android Paint、Canvas、Matrix使用讲解(一、Paint)

    http://blog.csdn.net/tianjian4592/article/details/44336949 好了,前面主要讲了Animation,Animator 的使用,以及桌面火箭效果和 ...

  9. Paint、Canvas、Matrix使用解说(一、Paint)

    username=tianjian4592">我正在參加 CSDN 2015博客之星评选 感恩分享活动,假设认为文章还不错,请投个票鼓舞下吧:http://vote.blog.csdn ...

  10. 安卓自己定义View进阶-Canvas之绘制基本形状

    Canvas之绘制基本形状 作者微博: @GcsSloop [本系列相关文章] 在上一篇自己定义View分类与流程中我们了解自己定义View相关的基本知识,只是,这些东西依然还是理论,并不能拿来(zh ...

随机推荐

  1. HBase-compact介绍(minor和major区别)

    一.minor和major的区别: Minor Compaction:指选取一些小的.相邻的HFile将他们合并成一个更大的HFile,但不会清理过期(TTL)和删除(打上Delete标记)的数据.  ...

  2. Python subProcess库以及Popen类的使用

    subprocess库是一个十分强大且常用的库,它可以用来调用第三方工具(例如:exe.另一个python文件.命令行工具). 1.常用函数call() :执行由参数提供的命令,把数组作为参数运行命令 ...

  3. KQL笔记

    KQL: {'query': {'bool': {'must': [{'match': {'Sql': 'insert'}}, {'match': {'PolicyName.keyword': 'd8 ...

  4. gitlab/github 设置 SSH

    最近项目要部署到另一台机器上,故要重新 git clone 一下 一开始只是简单的设置 config 里的 user.name 和 user.email,以为就可以 clone(邮箱账户拥有项目的权限 ...

  5. win32-UI Automation

    使用UI Automation遍历窗口的所有控件标题和类 #include <Windows.h> #include <stdio.h> #include <UIAuto ...

  6. less变量书写及样式混入

    定义变量 定义混入样式 变量及混入样式使用 样式文件中   ~@代表src

  7. ECMA Script Module(ES module)知识点

    1.每个 ES Module 都是运行在单独的私有作用,ESM 自动采用严格模式,忽略use strict <script type="module">console. ...

  8. 【Azure 应用服务】App Service下部署的应用报错 Out of Memory

    问题描述 应用部署到App Service后,遇见了Out of Memory的错误. 报错信息:GetData  Error:, Exception of type 'System.OutOfMem ...

  9. 从源码看webpack3打包流程

    在javascript刚刚流行时,前端项目通常比较简单,不需要考虑项目的开发效率.性能和扩展性等. 随着前端项目越来越复杂,需要更正式的软件开发实践,比如单元测试(unit testing).代码检查 ...

  10. 关闭mysql上锁的表/数据

    一.输入查询语句,查看是否有数据被上锁 select * from information_schema.innodb_trx; 取 trx_mysql_thread_id 字段值 kill < ...