Android自定义View绘图基础

简介: 转载请说明出处! 作者:kqw攻城狮 出处:个人站 | CSDNAndroid自定义View绘图基础Android自定义View绘图基础View的测量View的...

转载请说明出处!
作者:kqw攻城狮
出处:个人站 | CSDN


Android自定义View绘图基础

View的测量

控件的测量可以说是固定写法,原生的View只支持EXACTLY的测量模式,我们自定义的控件可以重写onMeasure方法

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    setMeasuredDimension(getMeasuredSize(widthMeasureSpec), getMeasuredSize(heightMeasureSpec));
}

onMeasure方法给我们返回的widthMeasureSpecheightMeasureSpec,我们并不能直接拿过来使用,需要使用MeasureSpec类进行解析,来获取测量后的具体值。
首先需要获取测量模式

MeasureSpec.getMode(measureSpec)

getMode方法返回是测量的模式,有以下3种类型:
- MeasureSpec.EXACTLY : 精确值模式(指定值/match_parent)
- MeasureSpec.AT_MOST : 最大值模式(wrap_content)
- MeasureSpec.UNSPECIFIED : 不指定大小的测量模式(一般用不上)

获取到了测量模式以后,获取测量后的大小

MeasureSpec.getSize(measureSpec)

根据上面的意思,可以封装我们的getMeasuredSize方法

// 默认大小
private static final int DEFAULT_SIZE = 200;

/**
 * 获取测量后的大小
 *
 * @param measureSpec measureSpec
 * @return measuredSize
 */
private int getMeasuredSize(int measureSpec) {
    switch (MeasureSpec.getMode(measureSpec)) {
        case MeasureSpec.EXACTLY: // 精确值模式(指定值/match_parent)
            Log.i(TAG, "getMeasuredSize: 精确值模式");
            return MeasureSpec.getSize(measureSpec);
        case MeasureSpec.AT_MOST: // 最大值模式(wrap_content)
            Log.i(TAG, "getMeasuredSize: 最大值模式");
            return Math.min(DEFAULT_SIZE, MeasureSpec.getSize(measureSpec));
        case MeasureSpec.UNSPECIFIED: // 不指定大小的测量模式
            return DEFAULT_SIZE;
        default:
            return DEFAULT_SIZE;
    }
}

现在我们自定义的View就支持自定义的大小了,包括match_parentwrap_content具体值

View的绘制

画笔属性

创建画笔

Paint paint = new Paint();
方法 描述 举例
public void setAntiAlias(boolean aa) 设置画笔锯齿效果 true 无锯齿效果
public void setColor(@ColorInt int color) 设置画笔颜色
public void setARGB(int a, int r, int g, int b) 设置画笔的A、R、G、B值
public void setAlpha(int a) 设置画笔的Alpha值
public void setTextSize(float textSize) 设置字体的尺寸
public void setStyle(Style style) 设置画笔的风格(空心或实心) paint.setStyle(Paint.Style.STROKE);// 空心 paint.setStyle(Paint.Style.FILL); // 实心
public void setStrokeWidth(float width) 设置空心边框的宽度

Shader

BitmapShader, ComposeShader, LinearGradient, RadialGradient, SweepGradient

/**
 * Helper for drawPoints() for drawing a single point.
 */
public void drawPoint(float x, float y, @NonNull Paint paint)
canvas.drawPoint(10, 10, paint);

直线

绘制一条直线

/**
 * Draw a line segment with the specified start and stop x,y coordinates,
 * using the specified paint.
 *
 * <p>Note that since a line is always "framed", the Style is ignored in the paint.</p>
 *
 * <p>Degenerate lines (length is 0) will not be drawn.</p>
 *
 * @param startX The x-coordinate of the start point of the line
 * @param startY The y-coordinate of the start point of the line
 * @param paint  The paint used to draw the line
 */
public void drawLine(float startX, float startY, float stopX, float stopY, @NonNull Paint paint)

绘制多条直线

public void drawLines(@Size(multiple=4) @NonNull float[] pts, @NonNull Paint paint)

矩形

/**
 * Draw the specified Rect using the specified paint. The rectangle will
 * be filled or framed based on the Style in the paint.
 *
 * @param left   The left side of the rectangle to be drawn
 * @param top    The top side of the rectangle to be drawn
 * @param right  The right side of the rectangle to be drawn
 * @param bottom The bottom side of the rectangle to be drawn
 * @param paint  The paint used to draw the rect
 */
public void drawRect(float left, float top, float right, float bottom, @NonNull Paint paint)
canvas.drawRect(100, 100, 200, 200, paint);

这里写图片描述

圆角矩形

/**
 * Draw the specified round-rect using the specified paint. The roundrect
 * will be filled or framed based on the Style in the paint.
 *
 * @param rx    The x-radius of the oval used to round the corners
 * @param ry    The y-radius of the oval used to round the corners
 * @param paint The paint used to draw the roundRect
 */
public void drawRoundRect(float left, float top, float right, float bottom, float rx, float ry, @NonNull Paint paint)
canvas.drawRoundRect(100, 100, 200, 200, 20, 20, paint);

这里写图片描述

/**
 * Draw the specified circle using the specified paint. If radius is <= 0,
 * then nothing will be drawn. The circle will be filled or framed based
 * on the Style in the paint.
 *
 * @param cx     The x-coordinate of the center of the cirle to be drawn
 * @param cy     The y-coordinate of the center of the cirle to be drawn
 * @param radius The radius of the cirle to be drawn
 * @param paint  The paint used to draw the circle
 */
public void drawCircle(float cx, float cy, float radius, @NonNull Paint paint)
// 画圆
canvas.drawCircle(200, 200, 100, paint);

这里写图片描述

扇形

/**
 * <p>Draw the specified arc, which will be scaled to fit inside the
 * specified oval.</p>
 *
 * <p>If the start angle is negative or >= 360, the start angle is treated
 * as start angle modulo 360.</p>
 *
 * <p>If the sweep angle is >= 360, then the oval is drawn
 * completely. Note that this differs slightly from SkPath::arcTo, which
 * treats the sweep angle modulo 360. If the sweep angle is negative,
 * the sweep angle is treated as sweep angle modulo 360</p>
 *
 * <p>The arc is drawn clockwise. An angle of 0 degrees correspond to the
 * geometric angle of 0 degrees (3 o'clock on a watch.)</p>
 *
 * @param startAngle Starting angle (in degrees) where the arc begins
 * @param sweepAngle Sweep angle (in degrees) measured clockwise
 * @param useCenter If true, include the center of the oval in the arc, and
                    close it if it is being stroked. This will draw a wedge
 * @param paint      The paint used to draw the arc
 */
public void drawArc(float left, float top, float right, float bottom, float startAngle, float sweepAngle, boolean useCenter, @NonNull Paint paint)
// 扇形 起始角度0度 旋转角度120度
canvas.drawArc(100, 100, 200, 200, 0, 120, true, paint);

这里写图片描述

弧形

扇形通过调整属性,可以实现弧形的效果,首先画笔设置成空心

// 空心
paint.setStyle(Paint.Style.STROKE);

设置画扇形的useCenter参数为false

// 弧形 起始角度0度 旋转角度120度
canvas.drawArc(100, 100, 200, 200, 0, 120, false, paint);

这里写图片描述

椭圆

/**
 * Draw the specified oval using the specified paint. The oval will be
 * filled or framed based on the Style in the paint.
 */
public void drawOval(float left, float top, float right, float bottom, @NonNull Paint paint)
// 椭圆
canvas.drawOval(100, 100, 500, 300, paint);

这里写图片描述

文字

/**
 * Draw the text, with origin at (x,y), using the specified paint. The
 * origin is interpreted based on the Align setting in the paint.
 *
 * @param text  The text to be drawn
 * @param x     The x-coordinate of the origin of the text being drawn
 * @param y     The y-coordinate of the baseline of the text being drawn
 * @param paint The paint used for the text (e.g. color, size, style)
 */
public void drawText(@NonNull String text, float x, float y, @NonNull Paint paint)
// 文字
paint.setTextSize(30);
paint.setColor(Color.BLACK);
canvas.drawText("KongQingwei", 100, 100, paint);

这里写图片描述

在指定位置显示每个字符

/**
 * Draw the text in the array, with each character's origin specified by
 * the pos array.
 *
 * @param text  The text to be drawn
 * @param pos   Array of [x,y] positions, used to position each character
 * @param paint The paint used for the text (e.g. color, size, style)
 *
 * @deprecated This method does not support glyph composition and decomposition and
 * should therefore not be used to render complex scripts. It also doesn't
 * handle supplementary characters (eg emoji).
 */
@Deprecated
public void drawPosText(@NonNull String text, @NonNull @Size(multiple=2) float[] pos, @NonNull Paint paint)
canvas.drawPosText("KongQingwei", new float[]{
        30,30,
        60,60,
        90,90,
        120,120,
        150,150,
        180,180,
        210,210,
        240,240,
        270,270,
        300,300,
        330,330
}, paint);

这里写图片描述

绘制路径

可以自定义画出想要的任意图形

五角星

/**
 * Draw the specified path using the specified paint. The path will be
 * filled or framed based on the Style in the paint.
 *
 * @param path  The path to be drawn
 * @param paint The paint used to draw the path
 */
public void drawPath(@NonNull Path path, @NonNull Paint paint)
Path path = new Path();
path.moveTo(0,100);
path.lineTo(250,300);
path.lineTo(150,0);
path.lineTo(50,300);
path.lineTo(300,100);
path.lineTo(0,100);
canvas.drawPath(path,paint);

这里写图片描述

空心

paint.setStyle(Paint.Style.STROKE);

这里写图片描述

图形裁剪

以上面的实心五角星为例,以五角星的中心为圆心,裁剪出一个半径为100的圆形

/**
    * Modify the current clip with the specified path.
 *
 * @param path The path to operate on the current clip
 * @param op   How the clip is modified
 * @return     true if the resulting is non-empty
 */
public boolean clipPath(@NonNull Path path, @NonNull Region.Op op)
// 裁剪一个圆形
Path path = new Path();
path.reset();
path.addCircle(150, 150, 100, Path.Direction.CCW);
canvas.clipPath(path, Region.Op.INTERSECT);
// canvas.save();
// 画五角星
path.reset();
path.moveTo(0,100);
path.lineTo(250,300);
path.lineTo(150,0);
path.lineTo(50,300);
path.lineTo(300,100);
path.lineTo(0,100);
canvas.drawPath(path,paint);

这里写图片描述

Region.Op 描述 样式
INTERSECT 裁剪内部交集 这里写图片描述
DIFFERENCE 外部 这里写图片描述
相关文章
|
8月前
|
Android开发 UED 计算机视觉
Android自定义view之线条等待动画(灵感来源:金铲铲之战)
本文介绍了一款受游戏“金铲铲之战”启发的Android自定义View——线条等待动画的实现过程。通过将布局分为10份,利用`onSizeChanged`测量最小长度,并借助画笔绘制动态线条,实现渐变伸缩效果。动画逻辑通过四个变量控制线条的增长与回退,最终形成流畅的等待动画。代码中详细展示了画笔初始化、线条绘制及动画更新的核心步骤,并提供完整源码供参考。此动画适用于加载场景,提升用户体验。
569 5
Android自定义view之线条等待动画(灵感来源:金铲铲之战)
|
8月前
|
Android开发
Android自定义view之利用PathEffect实现动态效果
本文介绍如何在Android自定义View中利用`PathEffect`实现动态效果。通过改变偏移量,结合`PathEffect`的子类(如`CornerPathEffect`、`DashPathEffect`、`PathDashPathEffect`等)实现路径绘制的动态变化。文章详细解析了各子类的功能与参数,并通过案例代码展示了如何使用`ComposePathEffect`组合效果,以及通过修改偏移量实现动画。最终效果为一个菱形图案沿路径运动,源码附于文末供参考。
156 0
|
8月前
|
XML Java Android开发
Android自定义view之网易云推荐歌单界面
本文详细介绍了如何通过自定义View实现网易云音乐推荐歌单界面的效果。首先,作者自定义了一个圆角图片控件`MellowImageView`,用于绘制圆角矩形图片。接着,通过将布局放入`HorizontalScrollView`中,实现了左右滑动功能,并使用`ViewFlipper`添加图片切换动画效果。文章提供了完整的代码示例,包括XML布局、动画文件和Java代码,最终展示了实现效果。此教程适合想了解自定义View和动画效果的开发者。
394 65
Android自定义view之网易云推荐歌单界面
|
8月前
|
XML 前端开发 Android开发
一篇文章带你走近Android自定义view
这是一篇关于Android自定义View的全面教程,涵盖从基础到进阶的知识点。文章首先讲解了自定义View的必要性及简单实现(如通过三个构造函数解决焦点问题),接着深入探讨Canvas绘图、自定义属性设置、动画实现等内容。还提供了具体案例,如跑马灯、折线图、太极图等。此外,文章详细解析了View绘制流程(measure、layout、draw)和事件分发机制。最后延伸至SurfaceView、GLSurfaceView、SVG动画等高级主题,并附带GitHub案例供实践。适合希望深入理解Android自定义View的开发者学习参考。
746 84
|
8月前
|
前端开发 Android开发 UED
讲讲Android为自定义view提供的SurfaceView
本文详细介绍了Android中自定义View时使用SurfaceView的必要性和实现方式。首先分析了在复杂绘制逻辑和高频界面更新场景下,传统View可能引发卡顿的问题,进而引出SurfaceView作为解决方案。文章通过Android官方Demo展示了SurfaceView的基本用法,包括实现`SurfaceHolder.Callback2`接口、与Activity生命周期绑定、子线程中使用`lockCanvas()`和`unlockCanvasAndPost()`方法完成绘图操作。
241 3
|
8月前
|
Android开发 开发者
Android自定义view之围棋动画(化繁为简)
本文介绍了Android自定义View的动画实现,通过两个案例拓展动态效果。第一个案例基于`drawArc`方法实现单次动画,借助布尔值控制动画流程。第二个案例以围棋动画为例,从简单的小球直线运动到双向变速运动,最终实现循环动画效果。代码结构清晰,逻辑简明,展示了如何化繁为简实现复杂动画,帮助读者拓展动态效果设计思路。文末提供完整源码,适合初学者和进阶开发者学习参考。
160 0
Android自定义view之围棋动画(化繁为简)
|
8月前
|
Java Android开发 开发者
Android自定义view之围棋动画
本文详细介绍了在Android中自定义View实现围棋动画的过程。从测量宽高、绘制棋盘背景,到创建固定棋子及动态棋子,最后通过属性动画实现棋子的移动效果。文章还讲解了如何通过自定义属性调整棋子和棋盘的颜色及动画时长,并优化视觉效果,如添加渐变色让白子更明显。最终效果既可作为围棋动画展示,也可用作加载等待动画。代码完整,适合进阶开发者学习参考。
179 0
|
XML 前端开发 Android开发
Android面试高频知识点(3) 详解Android View的绘制流程
Android面试高频知识点(3) 详解Android View的绘制流程
Android面试高频知识点(3) 详解Android View的绘制流程
|
XML 前端开发 Android开发
Android面试高频知识点(3) 详解Android View的绘制流程
Android面试高频知识点(3) 详解Android View的绘制流程
189 2
|
XML 前端开发 Android开发
Android View的绘制流程和原理详细解说
Android View的绘制流程和原理详细解说
400 3

热门文章

最新文章