自定义View相关案例
点击目标区域显示高亮
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;
}
}