自定义View
自定义属性
- values 下建立attrs.xml文件
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="MyButton">//声明的自定义属性集合
<attr name="title" format="string" />
<attr name="textSize" format="dimension" />
<!--Reference可以是引用(的资源如@color或者@drawable)-->
<attr name="background" format="reference|color" />
<attr name="fraction" format="fraction" />
</declare-styleable>
</resources>
- 在View的构造方法中获取 :尺寸得到的是px值整数值(四舍五入后的)
public MyView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
if (attrs != null) {
// TypedArray 这种数据结构 存储了我们在xml中获取的值
// 从context通过获得 R.styleable.MyButton是我们定义的属性集合
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.MyButton);
// 获取键值:自定义Styleable的名字然后下划线属性
background = typedArray.getDrawable(R.styleable.MyButton_background);
textSize = typedArray.getDimension(R.styleable.MyButton_textSize, 0);
title = typedArray.getString(R.styleable.MyButton_title);
fraction = typedArray.getFraction(R.styleable.MyButton_fraction, 0, 0, 0);
typedArray.recycle(); //记得回收
}
}
- 关与OnMeasure :从MeasureSpec中获得测量模式,然后根据测量模式根据自己的需求设置宽高(得到的都是px) 设置宽高的函数是setMeasureDimension();
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout //这里的布局不能是ConstrainLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="net.hongqianfly.myview.MainActivity">
<net.hongqianfly.myview.MyView
android:layout_width="match_parent"
android:layout_height="50dp"
app:title="自定义view"
app:gBackground="@color/colorAccent"
app:textSize="50sp"
android:background="@color/colorAccent"/>
</RelativeLayout>
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
widthSize = MeasureSpec.getSize(widthMeasureSpec);
heightSize = MeasureSpec.getSize(heightMeasureSpec);
switch (widthMode) {
case MeasureSpec.AT_MOST:
break;
case MeasureSpec.EXACTLY:
widthSize = UiUtils.dip2px(mContext, 50);
break;
case MeasureSpec.UNSPECIFIED:
break;
}
setMeasuredDimension(widthSize, heightSize);
}
onDraw() 方法
画文字
- canvas.drawText("你好,中国!",100,60,mPaint);//以View的左上点为原点为,X轴向右为正,Y轴像下为正建立坐标系 (100,60) 指的是文字的左下角点的坐标
- canvas.drawText("你好,中国!",2,4,80,60,mPaint);//文字的开始坐标,结束坐标,以及左下角的坐标
- canvas.drawTextOnPath("1234567897545985445",path,0,0,mPaint);
参数 路径 ,距离路径开始点 x,y轴偏移量
画圆
canvas.drawCircle(100,100,30,mPaint); 以View的左上点为原点为,X轴向右为正,Y轴像下为正建立坐标系 (100,100) 指的是圆心坐标,30指的是半径
画线
canvas.drawLine(20,50,40,90,mPaint);//开始点坐标(20,50),结束点坐标(40,90)
canvas.drawLines(new float[]{100,100,200,200,200,100,300,100}, p);
同时绘制多条线。 参数1:float数组:每四个一组为一条线。
画虚线要用path画
Path path = new Path();
path.moveTo(0, 0);
path.lineTo(500, 500);
mPaint.setPathEffect(new DashPathEffect(new float[]{10, 15, 20, 25}, 0));
canvas.drawPath(path, mPaint);
画矩形
RectF rectF = new RectF(0, 10, 400, 480); 前两个参数是左上角的坐标,后两个参数是右下角的坐标
Rect 的参数为int ,RectF的参数为float,RectF的精度更高一些,他们都是通过四个坐标参数来确定一个矩形的区域
RectF(float left,float top,fLoat right,float bottom)构造一个指定了4个参数的矩形.
RectF rectF = new RectF(0, 10, 400, 480);
canvas.drawRect(rectF,mPaint);
//画圆角矩形 20,20为在X和Y轴上的半径 一般相等就好
canvas.drawRoundRect(rectF,20,20,mPaint);
画椭圆
RectF rectF = new RectF(0, 10, 400, 480);
canvas.drawOval(rectF,mPaint);
画弧度
RectF rectF = new RectF(0, 10, 400, 480); //先创建一个矩形,扇形与其相切,中心为矩形的中心点
20 初始角度 水平向右为0度,逆时针为正
720 扫过的角度
true 是否和中心点相连,true画出一个扇形 false 画出一个直线与圆弧相组成的图形
canvas.drawArc(rectF,20,60,true,mPaint);
多边形
用 Path来画
path.addCircle(100,100,50, Path.Direction.CW);为 Path 中新增一个圆之后,调用 canvas.drawPath(path, paint) ,就能画一个圆出来。最后一个参数 dir 是画圆的路径的方向。路径方向有两种:顺时针 (CW clockwise) 和逆时针 (CCW counter-clockwise) 。对于普通情况,这个参数填 CW 还是填 CCW 没有影响。它只是在需要填充图形 (Paint.Style 为 FILL 或 FILL_AND_STROKE) ,并且图形出现自相交时,用于判断填充范围的。
path.lineTo(100, 100); //参数是绝对坐标
path.rLineTo(30,40);//参数是相对当前位置的相对坐标 (前缀 r 指的就是 relatively 「相对地」)。当前位置:所谓当前位置,即最后一次调用画 Path 的方法的终点位置。初始值为原点 (0, 0)
path.arcTo(100, 100, 300, 300, -90, 90, false);
path.arcTo() 只用来画弧形而不画扇形,所以不再需要 useCenter 参数;而多出来的这个 forceMoveTo 参数的意思是,绘制是要「抬一下笔移动过去」,还是「直接拖着笔过去」,区别在于是否留下移动的痕迹。是否与之前连线
path.addArc() 只是一个直接使用了 forceMoveTo = true 的简化版 arcTo() ;
path.setFillType(Path.FillType.WINDING);//两个相交的圆设置全填充
path.setFillType(Path.FillType.EVEN_ODD);//两圆相交不填充 两圆其余部分填充
even-odd rule (奇偶原则):对于平面中的任意一点,向任意方向射出一条射线,这条射线和图形相交的次数(相交才算,相切不算哦)如果是奇数,则这个点被认为在图形内部,是要被涂色的区域;如果是偶数,则这个点被认为在图形外部,是不被涂色的区域。还以左右相交的双圆为例
non-zero winding rule (非零环绕数原则):首先,它需要你图形中的所有线条都是有绘制方向的:同样是从平面中的点向任意方向射出一条射线,但计算规则不一样:以 0 为初始值,对于射线和图形的所有交点,遇到每个顺时针的交点(图形从射线的左边向右穿过)把结果加 1,遇到每个逆时针的交点(图形从射线的右边向左穿过)把结果减 1,最终把所有的交点都算上,得到的结果如果不是 0,则认为这个点在图形内部,是要被涂色的区域;如果是 0,则认为这个点在图形外部,是不被涂色的区域。
画点
canvas.drawPoint(60, 390, p);//画一个点
canvas.drawPoints(new float[]{60,400,65,400,70,400}, p);//画多个点
画图片
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_arrow);
RectF rectF = new RectF(0, 0, 80, 80);
//第一个Rect为矩形的图片的显示大小,若为null,则显示整个大小
//第二个Rect为矩形的显示区域
canvas.drawBitmap(bitmap,null,rectF,mPaint);
canvas.drawBitmap(bitmap,0,20,mpaint); //0,20图片左上角的坐标
drawColor()
类似的方法还有 drawRGB(int r, int g, int b) 和 drawARGB(int a, int r, int g, int b) ,它们和 drawColor(color) 只是使用方式不同,作用都是一样的
这类颜色填充方法一般用于在绘制之前设置底色,或者在绘制之后为界面设置半透明蒙版。
canvas
canvas .translate(100,50) //画布向X轴方向移动100,Y轴方向移动50
canvas.scale(2, 4);//画布x轴放大2倍,Y轴放大四倍 默认原点为左上角
canvas.scale(2, 4,100,100); 100,100为自定义的原点坐标
canvas.rotate(30);//左上角为中心点 顺时针为正
canvas.rotate(30,100,100); 以(100,100)为中心,旋转30度,顺时针方向为正方向
Canvas保存和还原
Canvas提供了几个方法,让我们可以方便的对Canvas的状态进行更改和还原。
这些方法是:save()、restore()、restoreToCount(int saveCount)。
我们在对Canvas进行平移、旋转、放大等操作时候,可以调用save()方法,将当前修改过的Canvas状态进行保存,调用restore() 方法后,会将Canvas还原成最近的一个save() 的状态。
save()方法还会有一个返回值,我们也可以调用restoreToCount(int saveCount)方法,将这个返回值作为参数传递进去,就可以将Canvas还原成某一个特定的save()状态。
关于Paint
setAntiAlias(); //设置画笔的锯齿效果
setColor(); //设置画笔的颜色
setARGB(); //设置画笔的A、R、G、B值
setAlpha(); //设置画笔的Alpha值
setTextSize(); //设置字体的尺寸
setDither(true); //设置防抖动
setStrokeWidth(float width) 设置线条宽度
setStyle(); //设置画笔的风格(空心或实心)
- Paint.Style.FILL:填充内部
- Paint.Style.FILL_AND_STROKE :填充内部和描边
- Paint.Style.STROKE :描边
setStrokeCap( Paint.Cap.BUTT ); // 线帽,即画的线条两端是否带有圆角,butt,无圆角
setStrokeWidth(); //设置空心边框的宽度 画笔的线宽
getColor(); //获取画笔的颜色
获取res下面的文字和颜色
String string = mContext.getString(R.string.app_name);
int color = mContext.getResources().getColor(R.color.colorAccent);