通过圆形载入View了解自定义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 : 不指定大小的测量模式(一般用不上)
获取到了测量模式以后,获取测量后的大小
``` java
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的第一篇文章,通过塑造简便的自定义View来通晓自定义View的流水线。自定义View是Android学习和开荒中必不可缺的一片段。通过自定义View我们能够制造丰盛光彩夺目的控件,自定义View首要有三种方式,具体如下:

View的绘制

  1. 接轨已部分View,来扩张大家的View
  2. 整合四个View来促成叁个复合的View
  3. 一心重写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) 设置空心边框的宽度

此间,大家讲第三种办法来打探自定义View的流程。

Shader

BitmapShader, ComposeShader, LinearGradient, RadialGradient,
SweepGradient

自定义View重要信赖的主意

自定义VIew中,大家第一重写onMeasureonDraw这两种办法来显现二个View。

onMeasure:首要办事是对我们要绘制的View举办度量,因为不实行度量的话,系统不知底要绘制的View有多大,不或然绘制出大家要求的轨范。onDraw:
主要办事正是绘制大家需求的图样,这些是自定义View中定制性最强也是最重大的干活。

上边我们先精晓一下那多个措施具体能做怎么样,然后我们通过一个实例来读书一下切实可行的用法。

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)

大家从上面的方式能够见见onMeasure
方法中有八个参数widthMeasureSpecheightMeasureSpec这三个参数每一个参数中都含有了度量值的轻重和度量的形式。衡量方式有弹指间两种:

  1. EXACTLY当大家把控layout_width或者layout_height品质设为match_parent依然是个100dp那样的正确值时就能用这种衡量情势。
  2. AT_MOST即最大值格局,当控件的layout_width属性或许layout_height指定为wrap_content时,空间大小相同随着控件的子控件大概内容的变型而变化,那是控件的尺寸只要不抢先父控件同意的最大尺寸就可以了。
  3. UNSPECIFIED那个是大家依照本身的主张,想绘制多大就绘制多大,未有别的节制。

实际的做法日常是,我们先依据参数,得到实际的衡量形式与衡量值,在依附测量检验的格局不一样,总结不一样的大幅度和冲天。最后经过setMeasuredDimension(int measuredWidth,int measuredHeight)将大家总计过的宽和高设置进去,达成衡量专门的学问。

onDraw(Canvas canvas)是我们显示自定义View的根本方法,他的参数是几个canvas也正是说二个画布,为了绘制图案,大家有了画布以外,我们还供给三个画笔Paint。那些画笔来决定你画图案的颜色,线条的粗细,是或不是抗锯齿,图案的品格等等。而画什么图案就交由canvas对象,调用canvas.drawXXX()来促成想要的图腾,具体的文书档案,参见Canvas官方文档

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

canvas.drawPoint(10, 10, paint);

自制圆形载入View

地方说了这么多,都是在YY,大家切实经过几个例证来走一次自定义View的流程。效果如下:

图片 1|center

第一大家新建三个CircleLoadingView.java文件。该类世袭View,生成布局器,显式调用父类的布局器,并初阶化大家的Paint对象,覆写onMeasure,onDraw方法,完结自定义View.

在布局方法中,大家调用本身写的initView方法来初步化Paint对象

private void initView(){ paint = new Paint(); //设置画笔的颜色 paint.setColor(circleColor); //设置抗锯齿,让图像更清晰 paint.setAntiAlias; //设置画笔的风格,有三种属性FILL,STROLE,FILL_AND_STROKE,我们不需要填充,所以设置为STROKE paint.setStyle(Paint.Style.STROKE); //设置画笔的粗细 paint.setStrokeWidth(circleStrokewidth); }

接下去,我们要求度量我们的View的大小,重写onMeasure()方法

@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { //设置一个默认的宽和高,AT_MOST模式需要 int result = 0; //通过MeasureSpec.getMode与getSize方法获取宽和高的测量方式与测量大小 int widthMode = MeasureSpec.getMode(widthMeasureSpec); int widthSize = MeasureSpec.getSize(widthMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); //保存最后的测量值,优化代码的话可以不用这个变量的。 int width = 0,height = 0; //对测量模式进行判断,如果是EXACTLY的话则最后的测量值就是系统帮我们测量的结果。 if (widthMode == MeasureSpec.EXACTLY) { width = widthSize; }else{ //如果是UNSPECIFIED 则使用我们的默认值作为最后的测量值 result = 300; width = result; //如果是AT_MOST 则就要用系统测量结果与我们默认结果取最小值来决定最后的测量结果 if (widthMode == MeasureSpec.AT_MOST) { width = Math.min(result,widthSize); } } //高度和宽度的过程是一致的。 if (heightMode == MeasureSpec.EXACTLY) { height = heightSize; }else { result = 300; height = result; if (heightMode == MeasureSpec.AT_MOST) { height = Math.min(result, heightSize); } } //把我们最后的宽和高设置进去 setMeasuredDimension(width,height); }

这就是onMeasure主意的代码,能够看出来,这几个都以足以放置任什么位置方复用的沙盘模拟经营代码。

我们着力已经达成了百分之九十的行事了,接下去只需求重写onDraw()绘制大家须要的叁个圆弧就足以了。

@Override protected void onDraw(Canvas canvas) { super.onDraw; //决定我们的弧形外接的矩形的宽和高 float wdistance = (wlength * 0.6); float hdistance = (hlength * 0.6); //定义外接矩形 RectF rectF = new RectF( wlength / 2 - wdistance / 2, hlength / 2 - hdistance / 2, wlength / 2 + wdistance / 2, hlength / 2 + hdistance / 2); //画弧形 canvas.drawArc( rectF,//外接矩形 270,//起始角度 ,//划过的度数 false,//是否是扇形 paint//画笔 ); }

看了上面包车型地铁代码,恐怕有个别杂乱,因为里面多了数不尽未曾涉嫌的变量。首先的变量正是wlength,与hlength以此指的是大家onMeasure衡量现在的宽和高,大家保留下来。在onMeasure方法中,大家踏入上面代码。

 wlength = width; hlength = height;

同有时间大家定义了外接矩形的宽和高正是大家View的0.6倍,上,下,
左,右各留了0.2倍的内边距。然后经过new
Rectf(卡塔尔方法来生成矩形对象,4个参数分别为矩形的左边距Y轴的偏离(约等于说X轴的坐标),上面距X轴的相距(也正是说Y轴的坐标),左侧距Y轴的间距(也正是说X轴的坐标),上面距Y轴的离开(也正是说Y轴的坐标)。具体见下图

图片 2|center

canvas
实际上就是这些坐标轴。定义好外接矩形之后,我们开端调用drawArc方法初始绘制弧形,那么些点子选取5个参数,第二个参数是弧起初的角度,这么些选取一个寸头代表角度,他是这么规定开场的角度的。以大家的钟表的3点起来,顺时针转过大家定义的角度,这个时候的职位就是我们伊始的地点,譬如大家现在定义的是270,这正是电子钟3点的职分转过270度为我们弧形开始的职位。正是12点的职位。然后第一个参数,代表弧形划过的角度。也是顺时针。第多个参数则表示是还是不是用扇形,我们那边不用,也正是说只是一个八个端点不一连圆心的弧,第多少个是大家伊始化过的paint。那样咱们的自定义的拱形就出来了。大家就足以把我们的控件放到xml中,当成普通的view去援引了。直接看代码:

<com.example.byhieg.circleloadingview.CircleLoadingView android: android:layout_width="100dp" android:layout_height="100dp" android:layout_centerInParent="true" />这里的引用View一定要用全名。我们就可以直接运行程序。

以此弧形其实并从未卵用,不可能动。大家接下去让她动起来,这里我们就用绘图自带的章程postInvalidate()来得以完毕重绘。要让三个图像动起来最简易的办法就是让他在每二个坐标点就绘制一下,然后让她在各类坐标点都现身二次就落实了动漫的机能。就疑似小时候有这种比相当多页的漫画书,大家快速翻阅漫画书就认为漫画书中的漫画动起来了,道理是同一的。那此次大家只供给在onDraw方法中,不断改正弧形的第二个参数,让他老是区别就足以了。具体的我们看代码:

@Override protected void onDraw(Canvas canvas) { super.onDraw; float wdistance = (wlength * 0.6); float hdistance = (hlength * 0.6); RectF rectF = new RectF( wlength / 2 - wdistance / 2, hlength / 2 - hdistance / 2, wlength / 2 + wdistance / 2, hlength / 2 + hdistance / 2); canvas.drawArc( rectF, //每次重绘,起始的角度就会多5度。 270 + 5 * i, , false, paint ); i++; //调用重绘,来实现图像不断绘制 postInvalidate(); }

那般,大家重新运路程序的时候,我们的半圆形就动起来。当我们在触及动漫概念的时候,又会意识有众多主意完毕弧形旋转。

前方4步,大家早就达成了简便易行的自定义View,而且有多少个莫斯中国科学技术大学学的效果。然而我们还要完备一下,例如大家得以在XML中钦定弧形的水彩,弧形的粗细。那个时候,我们就必要在values文件下新建叁个attrs.xml。在里面拟定我们的自定义属性,然后在我们的View文件中读取这一个属性。我们先看一下attrs.xml中,大家的代码:

<resources> <declare-styleable name="CircleLoadingView"> <attr name="circleColor" format="color" /> <attr name="circleStrokewidth" format="float" /> </declare-styleable></resources>

这里,大家就定义了2个属性,三个是弧形的颜色,三个是弧形的粗细,格式分别是color和float。然后大家在view中的initView代码中,获取这一个属性。代码如下:

TypedArray ta = getContext().obtainStyledAttributes(attrs, R.styleable.CircleLoadingView); circleStrokewidth = ta.getFloat(R.styleable.CircleLoadingView_circleStrokewidth, 0); circleColor = ta.getColor(R.styleable.CircleLoadingView_circleColor, 0);

下一场大家就能够在大家的xml中引用那么些属性了。

<com.example.byhieg.circleloadingview.CircleLoadingView android: android:layout_width="100dp" android:layout_height="100dp" android:layout_centerInParent="true" circleview:circleColor="@android:color/holo_blue_dark" circleview:circleStrokewidth="15"/>

这边的circleview是要在根结构的设置中宣示的
xmlns:circleview="http://schemas.android.com/apk/res-auto"因为,那么些根本出以往加载进程中,所以她索要三个办法来让他来得和隐身。我们在View的文件中,设置二个措施叫做setViewVisable当为true的时候,就足以显得,当为false就足以隐敝。那样,这么些自定义View就相比较康健了。隐藏方法代码如下:

public void setViewVisable(boolean choose) { if  { this.setVisibility(View.VISIBLE); }else{ this.setVisibility(View.GONE); } }

直线

绘制一条直线

/**
 * 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)

PS

以此小控件的源码作者放到网络了,大家能够对照参照他事他说加以考察下CircleLoadingView的源码

矩形

/**
 * 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);

图片 3

圆角矩形

/**
 * 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);

图片 4

/**
 * 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);

图片 5

扇形

/**
 * <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);

图片 6

弧形

扇形通过调解属性,能够实现弧形的意义,首先画笔设置成空心

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

设置画扇形的useCenter参数为false

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

图片 7

椭圆

/**
 * 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);

图片 8

文字

/**
 * 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);

图片 9

在钦命地点显得每种字符

/**
 * 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);

图片 10

绘制路径

可以自定义画出想要的狂谋算形

五角星

/**
 * 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);

图片 11

空心

paint.setStyle(Paint.Style.STROKE);

图片 12

图片裁剪

以地点的真切五角星为例,以五角星的中坚为圆心,裁剪出叁个半径为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);

图片 13

Region.Op 描述 样式
INTERSECT 裁剪内部交集 图片 13
DIFFERENCE 外部

发表评论

电子邮件地址不会被公开。 必填项已用*标注