自定义View相关案例

博客介绍了点击目标区域显示高亮及橡皮擦效果案例。还对Path进行简单介绍,给出Path常用方法表,为保证兼容性去除API21以上才添加的方法,同时说明了reset和rewind方法的特点,以及矩阵操作中的矩阵变换。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

点击目标区域显示高亮

Path简单介绍

Path常用方法表
为了兼容性(偷懒) 本表格中去除了在API21(即安卓版本5.0)以上才添加的方法。

作用相关方法
移动起点moveTo 移动下一次操作的起点位置
设置终点setLastPoint 重置当前path中最后一个点位置,如果在绘制之前调用,效果和moveTo相同
连接直线lineTo 添加上一个点到当前点之间的直线到Path
闭合路径close 连接第一个点连接到最后一个点,形成一个闭合区域
添加内容addRect, addRoundRect, addOval, addCircle, addPath, addArc, arcTo 添加(矩形, 圆角矩形, 椭圆, 圆, 路径, 圆弧) 到当前Path (注意addArc和arcTo的区别)
是否为空isEmpty 判断Path是否为空
是否为矩形isRect 判断path是否是一个矩形
替换路径set 用新的路径替换到当前路径所有内容
偏移路径offset 对当前路径之前的操作进行偏移(不会影响之后的操作)
贝塞尔曲线quadTo, cubicTo 分别为二次和三次贝塞尔曲线的方法
rXxx方法rMoveTo, rLineTo, rQuadTo, rCubicTo 不带r的方法是基于原点的坐标系(偏移量), rXxx方法是基于当前点坐标系(偏移量)
填充模式setFillType, getFillType, isInverseFillType, toggleInverseFillType 设置,获取,判断和切换填充模式
提示方法incReserve 提示Path还有多少个点等待加入**(这个方法貌似会让Path优化存储结构)**
布尔操作(API19)op 对两个Path进行布尔运算(即取交集、并集等操作)
计算边界computeBounds 计算Path的边界
重置路径reset, rewind 清除Path中的内容

reset不保留内部数据结构,但会保留FillType.
rewind会保留内部的数据结构,但不保留FillType |
|矩阵操作 | transform 矩阵变换 |

在这里插入图片描述

在这里插入图片描述

package com.example.day04;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Point;
import android.graphics.RectF;
import android.graphics.Region;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;

public class MyAttrsview extends View {
    Paint paint = new Paint();
    Paint paint2 = new Paint();
    Paint paint3 = new Paint();
    Paint paint4 = new Paint();
    int count=0;
    int count2=0;
    int count3=0;
    int count4=0;
    int width;
    int height;
    //圆点
    Point point = new Point();
    //区域
    Region region = new Region();
    Region region2 = new Region();
    Region region3 = new Region();
    Region region4 = new Region();
    //path
    Path path = new Path();
    Path path2 = new Path();
    Path path3 = new Path();
    Path path4 = new Path();
    Context context;

    float Pix;
    float PiY;

    /**
     * 用xml方式配置自定义属性 然后在自定义构造中拿到xml属性 拿到属性后去实现要干的事情
     *
     * @param context
     * @param attrs
     */
    public MyAttrsview(Context context, AttributeSet attrs) {
        super(context, attrs);
        this.context = context;
        // AttributeSet 对应的Xml属性的集合
//        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.MyAttrsview);
//        width = typedArray.getInteger(R.styleable.MyAttrsview_mywidth, 0);
//        height = typedArray.getInteger(R.styleable.MyAttrsview_myheight, 0);


    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        this.height = h;
        this.width = w;
        initpath();
    }

    private void initpath() {
        WindowManager manager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);

        DisplayMetrics displayMetrics = new DisplayMetrics();
        manager.getDefaultDisplay().getMetrics(displayMetrics);


        paint.setAntiAlias(true);
        paint.setStrokeWidth(2);
        paint.setStyle(Paint.Style.STROKE);
        paint.setColor(Color.RED);

        paint2.setAntiAlias(true);
        paint2.setStrokeWidth(2);
        paint2.setStyle(Paint.Style.STROKE);
        paint2.setColor(Color.BLACK);

        paint3.setAntiAlias(true);
        paint3.setStrokeWidth(2);
        paint3.setStyle(Paint.Style.STROKE);
        paint3.setColor(Color.YELLOW);

        paint4.setAntiAlias(true);
        paint4.setStrokeWidth(2);
        paint4.setStyle(Paint.Style.STROKE);
        paint4.setColor(Color.BLUE);


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

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

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

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

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);


        canvas.drawPath(path, paint);
        canvas.drawPath(path2, paint2);
        canvas.drawPath(path3, paint3);
        canvas.drawPath(path4, paint4);

//        canvas.drawCircle(width, height, 100, paint);
    }

    //判断点击事件
    @Override
    public boolean onTouchEvent(MotionEvent event) {

        float x = event.getX();
        float y = event.getY();
        Pix = x;
        PiY = y;
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                //画一个矩形
                RectF rectF = new RectF();
                RectF rectF2 = new RectF();
                RectF rectF3 = new RectF();
                RectF rectF4 = new RectF();
                //用path 分割成多个矩形
                path.computeBounds(rectF, true);
                path2.computeBounds(rectF2, true);
                path3.computeBounds(rectF3, true);
                path4.computeBounds(rectF4, true);
                //表示一个区域
                region.setPath(path, new Region((int) rectF.left, (int) rectF.top, (int) rectF.right, (int) rectF.bottom));
                region2.setPath(path2, new Region((int) rectF2.left, (int) rectF2.top, (int) rectF2.right, (int) rectF2.bottom));
                region3.setPath(path3, new Region((int) rectF3.left, (int) rectF3.top, (int) rectF3.right, (int) rectF3.bottom));
                region4.setPath(path4, new Region((int) rectF4.left, (int) rectF4.top, (int) rectF4.right, (int) rectF4.bottom));
                //判断这个点是否在这个区域内 在就返回true 否则返回false
                boolean contains = region.contains((int) Pix, (int) PiY);
                boolean contains2 = region2.contains((int) Pix, (int) PiY);
                boolean contains3 = region3.contains((int) Pix, (int) PiY);
                boolean contains4 = region4.contains((int) Pix, (int) PiY);
                if (contains == true) {
                    count++;
                    if(count%2==1){
                        paint.setStyle(Paint.Style.FILL);
                    }else{
                        paint.setStyle(Paint.Style.STROKE);
                    }
                    if(count>10){
                        count=0;
                    }
                }
                if (contains2 == true) {
                    count2++;
                    if(count2%2==1){
                        paint2.setStyle(Paint.Style.FILL);
                    }else{
                        paint2.setStyle(Paint.Style.STROKE);
                    }
                    if(count2>10){
                        count2=0;
                    }
                }
                if (contains3 == true) {
                    count3++;
                    if(count3%2==1){
                        paint3.setStyle(Paint.Style.FILL);
                    }else{
                        paint3.setStyle(Paint.Style.STROKE);
                    }
                    if(count3>10){
                        count3=0;
                    }
                }
                if (contains4 == true) {
                    count4++;
                    if(count4%2==1){
                        paint4.setStyle(Paint.Style.FILL);
                    }else{
                        paint4.setStyle(Paint.Style.STROKE);
                    }
                    if(count4>10){
                        count4=0;
                    }
                }

                break;
            case MotionEvent.ACTION_MOVE:
                break;
            case MotionEvent.ACTION_UP:
                break;
            default:
                break;

        }
        postInvalidate();


        return true;
    }
}

橡皮擦效果案例

在这里插入图片描述

在这里插入图片描述

package com.example.myview.FuGai;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;

import com.example.myview.R;

public class Eraseer extends View {


    private Context context;
    private Paint paint = new Paint();
    private int width;
    private int height;
    private Bitmap fgbitmap;
    //灰色前景画布的Canvas
    private Canvas mycanvas;
    //背景图片的Bitmap
    private Bitmap bgbitmap;

    private float picx;
    private float picy;

    private float Min_Path = 10;

    //储存的是我们手指移动的轨迹
    private Path path = new Path();

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

    private void initPaint() {
        WindowManager manager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);

        DisplayMetrics displayMetrics = new DisplayMetrics();
        manager.getDefaultDisplay().getMetrics(displayMetrics);
        height = displayMetrics.heightPixels;
        width = displayMetrics.widthPixels;


        //设置透明度
        paint.setARGB(128, 255, 0, 0);
        //设置抗抖动 和抗锯齿
        paint.setDither(true);
        paint.setAntiAlias(true);
        //设置混合模式 DST_IN
        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 并且设置图片的质量参数
        fgbitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);

        //将BitMap注入画
        mycanvas = new Canvas(fgbitmap);

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

        //生成背景画布
        bgbitmap = BitmapFactory.decodeResource(getResources(), R.drawable.timg);

        bgbitmap = Bitmap.createScaledBitmap(bgbitmap, width, height, true);


    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //设置背景
        canvas.drawBitmap(bgbitmap, 0, 0, null);
        //设置前景
        canvas.drawBitmap(fgbitmap, 0, 0, null);

        mycanvas.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.reset();
                path.moveTo(x, y);
                picx = x;
                picy = y;
                break;
            case MotionEvent.ACTION_MOVE:
                path.lineTo(x, y);
//                path.quadTo(picx, picy, x, y);
                //为什么要在赋值一遍  因为移动的时候坐标点一直在变 path.quadTo的起点和终点是一直变得
                // 要不就是画直线了  所以要赋值到起点

                float absx = Math.abs(x - picx);
                float absy = Math.abs(y - picy);
                if (absx > Min_Path || absy > Min_Path) {
                    //储存路径曲线
                    path.quadTo(picx, picy, x , y );
                }
                picx = x;
                picy = y;

                break;
            case MotionEvent.ACTION_UP:
                break;
            default:
                break;
        }

        postInvalidate();
        return true;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值