我爱编程

Day30-自定义View

2018-04-10  本文已影响175人  我不是死胖子

自定义view步骤:

ps: 重绘切记开子线程
低版本的适配用 new RectF(坐标X...), 再传入使用的draw方法即可

path类简述

详细请移步HenCoder1-1

path 的方法分为两种, 直接描述路径和辅助的设置/计算, path预先画完草稿后最后用canvas.drawPath来绘制

path 在绘制 Rect 时无法设置起点, 强制默认起点左上角, 可设置绘制方向顺时针/逆时针
path 在绘制 RoundCirdle 时可以设置.

View 中方法执行顺序

invalidate 和 requestLayout 区别

invalidate(): 只调用 onDraw();
requestLayout(): 会调用 onMeasure(), onLayout(), 不一定调用onDraw()

各个Animator区别

ViewPropertyAnimator: 限制于使用View自带的属性
ObjectAnimator: 可用于自定义控件的自定义属性
Animator 中只应做取插值的操作, 绘制/getPosTan 等方法应该放在 onDraw里

何时获取宽高

View 的生命周期和 Activity 的 onCreate, onStart, onResume 不同步. 如果此时还在测量, 得到的height, width 可能为0.

画一个仿 mastodon 的转发按钮

目标

分析

获取参数

android 中 View 绘制时是设置的 View 整体的宽高

截图一桢并在 PS 下放大到 2000% 后如图获取到参数(单位: 格)

绘制 View

android 中 View 绘制通过继承 View 后重写 onDraw 方法.onDraw中, canvas 理解为画布/画纸, paint 理解为画笔

android 中画笔 paint 分为两种, STORKE 和 FILL, 直译就是一笔和画满, 具体的解释是如果指定的区域是闭合的, 用 FILL 会把这个区域填满. 而 STROKE只会画边框

canvas.translate(mWidth/2, mHeight/2);//坐标系原点切到控件1/2处

透明边

此时三角形也画出来了, 但是原效果的三角形的边好像是外边框透明了? 那好, 来从画笔 paint 里找设置外边框的笔, 找了一圈。。。。。。emmm 没有.
那换一个角度, 并没有什么三角形的外边框, 而是在三角形的上面, 有一条透明的线.
好的, 那画好了透明色的线来看, 还是原来的样子.
因为 android 的绘制原则是覆盖, 所以一个区域如果有图案了,只能去覆盖它, 但是透明色的下面是原来的颜色, 所以覆盖上去了并不能透明.
所以看似透明边. 实际是背景色边而已.
但是为了保证三角形的圆角不会因误差被擦除, 需要将三角形和背景色边两个path重叠起来, 并且重叠出保留三角形. 用到了 PorterDuffXfermodeDST_OVER, 保留了作为的DST三角形

动画 Animation

android 中动画的概念可以拆分为, 用一个插值器拿到 此刻进度 在动画总进度的百分比 + 此刻 的 canvas 上画了啥. 插值器可以理解为动画执行到了哪一刻
举个例子, 一条用 path 画的直线本身在一次 onDraw内就可以直接完成, 现在把它的 path 路径用一个 1000 毫秒的动画来完成, 过程是: 默认的每10ms获取一次 此刻 插值器的值, 同时调用 postInvalidate() 方法触发 onDraw 来绘制, onDraw里根据插值器的值获取到执行到了总进度的多少, 来决定怎么画.

问题

此时发现, 三角形一直保持着绘制时的标准坐标系, 并没有按照预想的旋转.

旋转三角形

我们需要在左侧边时的三角形逆时针旋转90度, 在有侧边时的三角形顺时针旋转90度, 也就是-90 和 90.
根据动画 Animation 中提到的 mPathMeasure.getPosTan(distance, pos[], tan[])方法, 参数依次是, 此刻的进度, 此刻的点坐标数组pos[] 以及此刻的切线值. 这里通过不详细解释, 直接通过 float degrees = (float) (Math.atan2(tan[1], tan[0]) * 180.0 / Math.PI), 拿到趋势方向与x轴的夹脚, 将画布旋转即可canvas.rotate(degrees), 同理画出右侧三角形(此处可以展开讲讲, 但考虑到篇幅, 可根据注释理解)

矩形改成圆角矩形

之前绘制的是矩形, 而圆角可以用path.arcTo(float left, float top, float right, float bottom, float startAngle, float sweepAngle, boolean forceMoveTo方法绘制. 如图的圆弧是占地 left, top 到 right, bottom 的圆的 -180 度开始划过了 90 度的部分
其中,只画left, top, right, bottom 确定的椭圆为

image
这次的view为 image
于是效果从

最终效果

image

优化

1.颜色

根据"数码去色计"的"显示原生值"设置获取按钮的各种颜色

2.透明三角替换透明边

当三角形的圆角数值固定且view很小时, 三角形会矮于view导致三角形顶部多处一个"小角"

3.添加点击逻辑

3.1.缩放

view 自带了 animate(). 直接调用来缩放整体.

3.2. 当手指滑出View时

普通的父布局不会管子view的 touch 事件, 但是 RecyclerView 会在 onInterceptTouchEvent 里判断当 event 区域超过子 view 后调用 setScrollStat(), 拦截掉接下来的 move 事件. 这样子 view 被ACTION_CANCEL, 父布局 RecyclerView 根据 move 来滑动

如果手指超出了 view 后发生位移(Recyclerview中), 可根据 Recyclerview 触发的 ACTION_CANCEL 获取到离开的标志
如果 view 无法位移(在普通 view 中), 只可用 onTouchEvent 根据 ACTION_UP 拿到手指离开的标志

优化后的效果

参考
感谢黄海奇的指点
Hencoder1-1
arcToMethod
getBackgroundColor
Path测量工具:PathMeasure
安卓自定义View进阶-PathMeasure
CSDN | 自定义view系列(3)--给自定义View添加点击事件
TODO
Android自定义View长按事件的实现

上一篇 下一篇

猜你喜欢

热点阅读