一.如何创建一个自定义View?
- 首先继承View,然后写入到布局中
public class CircleLoadingView extends View {
private Paint paint;
public CircleLoadingView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
paint = new Paint();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int radius = DpPxUtils.dp2px(80);
canvas.drawCircle(0,0,radius,paint);
}
}
2.然后写入到布局中
<LinearLayout
android:gravity="center"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<com.smallcake.template.custom.CircleLoadingView
android:background="@color/red"
android:layout_width="100dp"
android:layout_height="100dp"/>
</LinearLayout>
3.运行,效果就出来了
注意: 1.自定义的View构造方法是
两个参数的构造方法,不写比报错 2.默认画笔paint最好写出来,因为onDraw可能会多次执行,画笔默认黑色
二.如何获取自定义View的宽高?
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
}
或者:onDraw()方法中通过getWidth()和getHeight()
注意:
- 1.
onMeasure方法会执行三次 - 2.首先要明白
MeasureSpec中三种模式:UNSPECIFIED,AT_MOST,EXACTLY - 3.
int型的MeasureSpec来表示一个组件的大小,这个变量里面不仅有组件的尺寸大小,还有大小的模式,一共32位,前两位标示模式,后面30位标示空间大小 - 4.通常来说,我们在自定义 View 的时候会经常地接触到
AT_MOST和EXACTLY,UNSPECIFIED这个模式被应用在系统源码中。如NestedScrollView和ScrollView - 5.如何获取对应的模式 :
int heightMode = MeasureSpec.getMode(heightMeasureSpec);参考:www.cnblogs.com/liushilin/p…
| 模式 | 描述 | 对应 |
|---|---|---|
精确模式01(MeasureSpec.EXACTLY) | 在这种模式下,尺寸的值是多少,那么这个组件的长或宽就是多少。 | 对应 MATCH_PARENT 和确定的值。 |
最大模式11(MeasureSpec.AT_MOST) | 这个也就是父组件,能够给出的最大的空间,当前组件的长或宽最大只能为这么大,当然也可以比这个小。 | 对应 WRAP_CONETNT |
未指定模式00(MeasureSpec.UNSPECIFIED) | 这个就是说,当前组件,可以随便用空间,不受限制。 | 所有 |
三.一些样式设置?
| 效果 | 代码 |
|---|---|
| 抗锯齿 | paint.setAntiAlias(true); |
| 空心 | paint.setStyle(Paint.Style.STROKE); |
| 线宽 | paint.setStrokeWidth(8); |
四.RectF和Rect区别?
- 1.它们都是用于构造一个矩形区域
- 2.精度不一样,Rect是使用int类型作为数值,RectF是使用float类型作为数值
- 3.两个类型提供的方法也不是完全一致,比如 RectF(Rect r)根据给定的Rect对象来构造一个RectF对象, 进而扩展Rect不具备的功能
五.画圆
canvas.drawCircle(float cx, float cy, float radius, @NonNull Paint paint)
- 1.绘制都是从画笔中心点开始画,画笔粗细设置过宽,会出现上下左右
少一块,我们应该设置半径radius-画笔组粗/2
六.画圆弧
RectF rectF = new RectF(0, 0, width, height);
canvas.drawArc(rectF,0,30,false,paint);
drawArc参数:1.绘制区域,2.开始角度,3.结束角度,4.是否连接中心点,5,画笔
-
1.如果你想绘制的是一段圆弧,不是一段圆弧切面,可以设置画笔样式为空心的
paint.style = Paint.Style.STROKE -
Demo:绘制一个加载进度的圆弧,并结合动画,让其动起来
class CircleLoaindView : View {
private val paint = Paint()
private val paint2 = Paint()
private var rectF: RectF? = null
private var position = 0//从0到100的进度位置
private val paintWidth = 4f//画笔粗细
constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) {
paint.setColor(Color.parseColor("#DDDDDD"))
paint.isAntiAlias = true
paint.style = Paint.Style.STROKE
paint.strokeWidth = paintWidth
paint2.setColor(Color.parseColor("#535353"))
paint2.isAntiAlias = true
paint2.style = Paint.Style.STROKE
paint2.strokeWidth = paintWidth
paint2.strokeCap = Paint.Cap.ROUND
}
fun setPosition(position: Int) {
this.position = position;
invalidate()
}
override fun onDraw(canvas: Canvas?) {
super.onDraw(canvas)
val centerX = width / 2
val strokeWidth = centerX / radius
canvas?.let {
//绘制灰色背景圆环
canvas.drawCircle(
(width / 2).toFloat(), (height / 2).toFloat(),
(width / 2).toFloat() - paintWidth / 2, paint
)
//设置圆弧段绘制区域
if (rectF == null) rectF = RectF(
(strokeWidth / 2).toFloat() + paintWidth / 2,
(strokeWidth / 2).toFloat() + paintWidth / 2,
(2 * centerX - strokeWidth / 2).toFloat() - paintWidth / 2,
(2 * centerX - strokeWidth / 2).toFloat() - paintWidth / 2
)
//绘制圆弧段
canvas.drawArc(rectF!!, 3.6f * position, 360f / 30, false, paint2)
}
}
}
<com.smallcake.test.CircleLoaindView
android:id="@+id/circleLoading"
android:layout_width="200dp"
android:layout_height="200dp"
/>
val animator: ObjectAnimator = ObjectAnimator.ofInt(circleLoading, "position", 0, 100)
animator.duration = 1000
animator.repeatCount = Animation.INFINITE
animator.interpolator = LinearInterpolator()
animator.start()