day05-自定义View

MeasureSpec的使用

  1. 获取测量模式(Mode) int specMode = MeasureSpec.getMode(measureSpec)
  2. 获取测量大小(Size) int specSize = MeasureSpec.getSize(measureSpec)
  3. 在这里插入图片描述

自定义View属性:

自定义View的自定义属性,为了能让自定义View在xml文件中编写时可以设置自己特有的属性值

创建attrs.xml文件
在res/values/中创建一个attrs.xml。
创建declare-styleable节点
在根节点resources内添加declare-styleable节点,declare-styleable节点只有一个name属性且为必填,name可以自由定义。
在这里插入图片描述

View生命周期

onWindowVisibilityChanged(改变可见性)——》创建(构造方法)——》onFinishInflate(初始化xml【当所有子控件全部完成xml映射是触发】})——》onAttachedToWindow(当view附着一个窗口时触发)——》onMeasure(确定自己的大小)——》onSizeChenged(当view大小变化是触发)——》onLayout(确定所有子控件的大小和位置)——》onDeaw(view绘制)——》onDetackedFromWindow(当View离开附着的窗口时触发,比如在Activity调用onDestroy方法时View就会离开窗口。和一开始的AttachedToWindow相对,都只会被调用一次)

view的onMeasure和onLayout问什么会执行两次?

api25-24:执行2次onMeasure、2次onLayout、1次onDraw,理论上执行三次测量,但由于测量优化策略,第三次不会执行onMeasure。

api23-21:执行3次onMeasure、2次onLayout、1次onDraw,forceLayout标志位,离奇被置为true,导致无测量优化。

api19-16:执行2次onMeasure、2次onLayout、1次onDraw,原因第一次performTranversals中只会执行measureHierarchy中的performMeasure,forceLayout标志位,离奇被置位true,导致无测量优化。

总之,造成这个现象的根本原因是performTranversal函数在View的测量流程中会执行2次。
遍历Activity的根布局DecorView里(或者其它窗口比如Dialog)的每一个View

画笔属性:

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

案例

填充View效果

在这里插入图片描述
在这里插入图片描述

代码

public class MyView extends View {

private Context context;

private int myWidth,myHeight;

private int width;
private int height;

private Paint p1,p2,p3,p4;
private Path path1,path2,path3,path4;

public MyView(Context context, AttributeSet attrs) {
    super(context, attrs);
    this.context = context;

    TypedArray typedArray = context.obtainStyledAttributes(attrs,R.styleable.MyView);
    myWidth = typedArray.getInteger(R.styleable.MyView_myWidth,0);
    myHeight = typedArray.getInteger(R.styleable.MyView_myHeight,0);

    initPaint();
}

private void initPaint() {
    WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
    DisplayMetrics displayMetrics = new DisplayMetrics();
    windowManager.getDefaultDisplay().getMetrics(displayMetrics);
    width = displayMetrics.widthPixels;
    height = displayMetrics.heightPixels;

    p1 = new Paint();
    p2 = new Paint();
    p3 = new Paint();
    p4 = new Paint();

    p1.setStrokeWidth(5);
    p1.setColor(Color.RED);
    p1.setAntiAlias(true);
    p1.setStyle(Paint.Style.STROKE);

    p2.setStrokeWidth(5);
    p2.setColor(Color.BLUE);
    p2.setAntiAlias(true);
    p2.setStyle(Paint.Style.STROKE);

    p3.setStrokeWidth(5);
    p3.setColor(Color.GREEN);
    p3.setAntiAlias(true);
    p3.setStyle(Paint.Style.STROKE);

    p4.setStrokeWidth(5);
    p4.setColor(Color.YELLOW);
    p4.setAntiAlias(true);
    p4.setStyle(Paint.Style.STROKE);


    path1 = new Path();
    path2 = new Path();
    path3 = new Path();
    path4 = new Path();

    path1.moveTo(0,0);
    path1.lineTo(width/2,height/2);
    path1.lineTo(width,0);

    path2.moveTo(0,0);
    path2.lineTo(width/2,height/2);
    path2.lineTo(0,height);

    path3.moveTo(0,height);
    path3.lineTo(width/2,height/2);
    path3.lineTo(width,height);

    path4.moveTo(width,0);
    path4.lineTo(width/2,height/2);
    path4.lineTo(width,height);

}

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    canvas.drawPath(path1,p1);
    canvas.drawPath(path2,p2);
    canvas.drawPath(path3,p3);
    canvas.drawPath(path4,p4);
}

@Override
public boolean onTouchEvent(MotionEvent event) {
    float x = event.getX();
    float y = event.getY();
    switch (event.getAction()){
        case MotionEvent.ACTION_DOWN:
            fill(x,y);
            fill1(x,y);
            fill2(x,y);
            fill3(x,y);
            break;
    }
    invalidate();
    return true;
}

private void fill3(float x, float y) {
    RectF rectF = new RectF();
    path4.computeBounds(rectF,true);

    Region region = new Region();
    region.setPath(path4,new Region((int)rectF.left,(int)rectF.top,(int)rectF.right,(int)rectF.bottom));
    boolean contains = region.contains((int) x, (int) y);
    if (contains){
        p4.setStyle(Paint.Style.FILL);
        invalidate();
    }
}

private void fill2(float x, float y) {
    RectF rectF = new RectF();
    path3.computeBounds(rectF,true);

    Region region = new Region();
    region.setPath(path3,new Region((int)rectF.left,(int)rectF.top,(int)rectF.right,(int)rectF.bottom));
    boolean contains = region.contains((int) x, (int) y);
    if (contains){
        p3.setStyle(Paint.Style.FILL);
        invalidate();
    }
}

private void fill1(float x, float y) {
    RectF rectF = new RectF();
    path2.computeBounds(rectF,true);

    Region region = new Region();
    region.setPath(path2,new Region((int)rectF.left,(int)rectF.top,(int)rectF.right,(int)rectF.bottom));
    boolean contains = region.contains((int) x, (int) y);
    if (contains){
        p2.setStyle(Paint.Style.FILL);
        invalidate();
    }
}

private void fill(float x, float y) {
    RectF rectF = new RectF();
    path1.computeBounds(rectF,true);

    Region region = new Region();
    region.setPath(path1,new Region((int)rectF.left,(int)rectF.top,(int)rectF.right,(int)rectF.bottom));
    boolean contains = region.contains((int) x, (int) y);
    if (contains){
        p1.setStyle(Paint.Style.FILL);
        invalidate();
    }
}}

擦除View 效果

在这里插入图片描述
在这里插入图片描述

代码

public class EraserView extends View {
private Context context;
//屏幕的宽高
private int weight;
private int height;

private Paint paint;

//前景bitmap
private Bitmap qbitmap;
//前景画布canvas
private Canvas mcanvas;
//背景bitmap
private Bitmap bbitmap;
//背景canvas
//手指移动的路径
private Path path;
//设置一个全局的起点
private float picX;
private float picY;
//移动的最小值
private float MIN_MOVE_DIS = 10;

public EraserView(Context context, AttributeSet attrs) {
    super(context, attrs);
    this.context = context;
    initPaint();
}

//初始化
private void initPaint() {
    //获取屏幕宽高
    WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
    DisplayMetrics displayMetrics = new DisplayMetrics();
    windowManager.getDefaultDisplay().getRealMetrics(displayMetrics);
    weight = displayMetrics.widthPixels;
    height = displayMetrics.heightPixels;

    paint = new Paint();
    paint.setAntiAlias(true); //抗锯齿
    paint.setDither(true); //抗抖动
//  paint.setColor(Color.GREEN); //设置画笔颜色
//  paint.setStrokeWidth(5); //设置画笔宽度
    paint.setARGB(128,255,0,0); //设置透明度
    //设置混合模式
    paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
    paint.setStyle(Paint.Style.STROKE); //设置描边
    paint.setStrokeJoin(Paint.Join.ROUND); //设置路径结合处样式
    paint.setStrokeCap(Paint.Cap.ROUND); // 设置笔触类型
    paint.setStrokeWidth(40); //设置宽度

    //生成前景的bitmap  设置图片的质量参数
    qbitmap = Bitmap.createBitmap(weight,height,Bitmap.Config.ARGB_8888);

    //将其注入画布
    mcanvas = new Canvas(qbitmap);

    //灰色
    mcanvas.drawColor(0XFF808080);

    //生成背景图片
    bbitmap = BitmapFactory.decodeResource(context.getResources(), R.mipmap.xuexxx);

    bbitmap = Bitmap.createScaledBitmap(bbitmap,weight,height,true);

    path = new Path();
}

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    //设置背景
    canvas.drawBitmap(bbitmap,0,0,null);
    //设置前景
    canvas.drawBitmap(qbitmap,0,0,null);
    //设置前景
    mcanvas.drawPath(path,paint);
}

@Override
public boolean onTouchEvent(MotionEvent event) {
    //获取当前手指所在位置的点
    float x = event.getX();
    float y = event.getY();
    switch (event.getAction()){
        //手指落下
        case MotionEvent.ACTION_DOWN:
            //清空上一次的path路径
            path.reset();
            path.moveTo(x,y);
            //赋值全局的起点
            picX = x;
            picY = y;
            break;
        case MotionEvent.ACTION_MOVE:
            float absX = Math.abs(x - picX);
            float absY = Math.abs(y - picY);
            if (absX > MIN_MOVE_DIS || absY > MIN_MOVE_DIS){
                //存储路径的线路
                path.quadTo(picX,picY,x,y);
            }
            //赋值到起点
            picX = x;
            picY = y;
            break;
    }
    invalidate();
    return true;
}}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值