上文首要写了自定义View的一些根底,这篇文章首要自定义了SVGView,也算是对上篇文章的稳固,事情的起因是开发APP的时分有一个人体图,能够标示出各个区域的疼痛程度,所以第一时间想到了运用SVG,所以查询了网上不少的样例,可是或多或少都有一些问题,这儿自己写了一个,支撑Raw文件和File加载,支撑区别SVG不同区域点击,支撑移动,缩放,翻转
源码
源码点击这儿,点这儿……
引入
implementation 'io.github.zhaojfGithub.SVGView:SVGView:0.0.3
运用
SVGId | integer | 运用svg的文件id |
---|---|---|
SVGScale | float | 设置初始缩放倍数 |
SVGBackground | color | 设置控件布景 |
SVGColor | color | 设置区域默许颜色 |
SVGLineColor | color | 设置切割线默许颜色 |
SVGIsMove | boolean | 是否启用滑动动能 |
SVGMoveSpeed | float | 设置滑动的速度,越小越快 |
SVGIsZoom | boolean | 是否启用缩放功能 |
SVGZoomSpeed | float | 设置缩放的速度,越小越快 |
尽管我也觉得越小越快是反人类设计,这个以后再改
Java调用
val svgView = findViewById<SVGView>(R.id.SVGView)
svgView.zoomSpeed = 1F
svgView.moveSpeed = 3F
svgView.setOnClickListener(SVGView.OnSVGClickListener {
if (it.select) {
it.color = Color.RED
} else {
it.color = Color.BLUE;
}
})
仍是挺简单的,这儿的select是点击修改后才传递进来的,所以只需要操作设置选中时什么姿态和未选中是什么姿态就行
完成效果
这儿本来想放那个人体图了,可是公司的东西,考虑一下,自己随意画了一个SVG图,
整体概览
在考虑把这个View上传上去的时分,就很充沛的考虑了扩展的状况,谁也确保不了用户想区别区域一定要加一个TAG,万一想加一个XXOO呢,所以在设计之初,就把文件的解析,实体类的制作给了一个单独的类,这儿有一个SVGHelpInterface
的接口SVGHelpImpl
完成类,一般来说有自己独特的主意只需要继承SVGHelpImpl
就能够了,来看一眼接口内容
List<PathBean> deCodeSVG(Context context, Integer SVGId, File file, @ColorInt Integer color) throws IOException;
RectF getSVGRecF(List<PathBean> list);
PathBean getPathBean(Integer id, String tag, Path path, Integer color);
void onDraw(PathBean pathBean, Canvas canvas, Paint paint, @ColorInt Integer LineColor);
Boolean isClick(PathBean pathBean, Float x, Float y);
能够看到,持续把一切能摘出来的逻辑都拿出来了,什么还不行你玩?主张去继承View,然后悉数重写
- deCodeSVG:这儿首要担任解析SVG图片,然后生成对应的实体
- getSVGRecF:这儿便是算这个图片的实际大小的,然后配合在View
onMeasure
方法中定义View的大小 - getPathBean:这个呢生成一个实体,假如你有自己的实体,不想运用自带的,那么就重写这个方法
- onDraw:把内容画出来,和View的onDraw一个意思,不过它对应仅仅一个区域。即一个PathBean
- isClick:判别点击区域是否在对应区域
具体怎样完成的能够看源码 上面很多地方都运用到了PathBean,ok,PathBean是什么勒,即存储SVG path切割的信息,先看SVG的信息
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="800dp"
android:height="600dp"
android:viewportWidth="800"
android:viewportHeight="600">
<path
android:strokeWidth="1"
android:pathData="M93,158h55v266h-55z"
android:fillColor="#fff"
android:strokeColor="#000"/>
</vector>
PathBean对应便是SVG内的path标签,然后依据pathData去画出来,对吧很简单,看一下结构函数
public PathBean(Integer id, String tag, Path path, @ColorInt Integer color, Boolean isSelect) {
RectF rectF = new RectF();
path.computeBounds(rectF, true);
Region region = new Region();
Rect rect = new Rect((int) rectF.left, (int) rectF.top, (int) rectF.right, (int) rectF.bottom);
region.setPath(path, new Region(rect));
this.id = id;
this.tag = tag;
this.path = path;
this.rectF = rectF;
this.region = region;
this.color = color;
this.isSelect = isSelect;
}
这儿首要解说一下Region
的效果和Path
的效果和区别,为什么在这儿解说,由于我在写View的时分我也不知道
-
Region
类用于表示一个二维平面上的矩形区域。它能够用来描绘一个区域的边界、形状或方位,并进行相应的操作,如兼并、交集、差集、补集等。Region
类的首要效果是进行图形的区域运算和判别。 -
Path
类用于描绘和绘制图形的途径。它能够包括直线、曲线、圆弧等不同的线段和曲线段,然后构成杂乱的形状和轮廓。 - OK 明白,Path便是画出这个区域出来的,Region便是判别是不是在点击区域内的
View设计
这儿呢就需要上文View根底的内容了
获取XML信息 计算View大小:
onDraw:
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (PathList.isEmpty() || canvas == null || originRecF == null) {
return;
}
canvas.save();
canvas.drawColor(svgBackground);
//默许先移动到中心
canvas.translate((getWidth() - originRecF.width() * scale) / 2, (getHeight() - originRecF.height() * scale) / 2);
canvas.translate(lastMoveX, lastMoveY);
//处理手势位移
canvas.translate(moveX, moveY);
canvas.scale(scale, scale);
for (int i = 0; i < PathList.size(); i++) {
svgHelp.onDraw(PathList.get(i), canvas, paint, lineColor);
}
canvas.restore();
}
首要是位移到View的中心,为什么,由于你写个View仍是,然后处理手势的位移操作,然后再处理缩放,最终通过SVGHelp的onDraw一个一个画出来,至此,一个根本的显现就完成了,接下来处理事情分发。
事情分发
说到事情分发,那便是需要在onTouchEvent来做操作了,
这儿运用的是官方GestureDetector.OnGestureListener
完成的点击 estureDetector.SimpleOnGestureListener
完成的位移,至于为什么不在GestureDetector.OnGestureListener
把位移一并处理了,由于要区别点击,位移操作,这儿分隔写。ScaleGestureDetector.SimpleOnScaleGestureListener
完成的缩放
看一下如何完成:
@Override
public boolean onTouchEvent(MotionEvent event) {
gestureClick.onTouchEvent(event);
int pointerCount = event.getPointerCount();
if (pointerCount == 1) {
gestureMove.onTouchEvent(event);
} else {
gestureZoom.onTouchEvent(event);
}
if (event.getAction() == MotionEvent.ACTION_UP) {
if (moveNumber > MAX_MOVE_NUMBER) {
//只要经历了滑动才记载
lastMoveX += moveX;
lastMoveY += moveY;
moveX = 0F;
moveY = 0F;
}
moveNumber = 0;
}
return true;
}
第一步注册点击事情,不做区别,第二步获取了接触屏幕的手指数量,假如只一根手指,那么就事情就给到位移操作,假如是多根就给到位移操作,再往下是记载之前方位的间隔,以方便在持续方位,不处理就会每次从中心开始,值得注意的是有一个moveNumber > MAX_MOVE_NUMBER
的阈值判别,在实际测试的过程中,发现缩放操作中心会断触,导致忽然跳到了位移,以至于发生了大批量的位移操作,这儿设置了一个阈值,首要便是为了防止这类事情的发生,return true
说明这个事情我现已处理过了,不用管了
点击处理
@Override
public boolean onSingleTapUp(@NonNull MotionEvent e) {
boolean result = false;
for (PathBean pathBean : PathList) {
float x = (e.getX() - (getWidth() - originRecF.width() * scale) / 2 - lastMoveX) / scale;
float y = (e.getY() - (getHeight() - originRecF.height() * scale) / 2 - lastMoveY) / scale;
if (svgHelp.isClick(pathBean, x, y)) {
pathBean.setSelect(!pathBean.getSelect());
if (onClickListener != null) {
onClickListener.onClick(pathBean);
}
result = true;
invalidate();
}
}
return result;
}
此方法为点击处理,这儿值得重视的只要当时坐标转换为本来坐标的计算,由于我找不到更好的方法,只能这样咯 首要减去了位移到居中的间隔,然后再减去手指位移的间隔最终除以缩放倍数,计算出本来对应坐标,然后判别在不在这个区域内
位移操作
@Override
public boolean onScroll(@NonNull MotionEvent e1, @NonNull MotionEvent e2, float distanceX, float distanceY) {
if (!isMove) {
return false;
}
if (moveNumber <= MAX_MOVE_NUMBER) {
moveNumber++;
return true;
}
float deltaX = (e2.getX() - e1.getX()) / moveSpeed;
float deltaY = (e2.getY() - e1.getY()) / moveSpeed;
moveX = deltaX;
moveY = deltaY;
invalidate();
return true;
}
额,如同没什么能够解说的们首要是仍是这个阈值,对应onTouchEvent方法的处理,然后通过手指位移间隔和设置的位移速度,得出需要的位移间隔,嗯 简单
缩放操作
@Override
public boolean onScale(@NonNull ScaleGestureDetector detector) {
if (!isZoom) {
return false;
}
float scaleGap = (detector.getScaleFactor() - lastScale) / zoomSpeed;
//由于假如scale为负数,会造成图画倒转,到0会看不到,故设置为0.1
if (scale + scaleGap > 0.1) {
scale += scaleGap;
}
invalidate();
return true;
}
在实际的一直有一次,缩放缩多了,天啊撸,图画居然翻转了过来,原因是缩放scale变成了辅助,造成了画布翻转,然后就变成倒着显现了,这儿首要做一下约束,最小倍数为1。
完毕
至此根本方法就悉数完毕了,怎样说呢,仍是第一次写一个比较完整的View,由于之前写的没有这么规范,有兴趣能够看一下源码,其中有不合适的地方,期望我们批评指正
想要上传到maven过程能够看下一篇文章# 写一个SVGView,并上传到Maven(下)