网络知识 娱乐 (Android)写个小控件吧:长按结束运动

(Android)写个小控件吧:长按结束运动

UI效果图

(Android)写个小控件吧:长按结束运动

功能

  • 长按进度条增加
  • 进度条满一周时通知外部倒计时结束
  • 松开时则会自动取消进度,并且通知外部
  • 可以自定义开始的角度
  • 可以自定义倒计时

实现

中间一张图,外面绘制个底圈,再绘制一个上层的圈即可。

当用户按下时,则开始增加角度。当用户手松开时进度归零。

由上分析

创建一个继承于ImageView的控件

public class PausePressView extends AppCompatImageView {n public PausePressView(@NonNull Context context) {n this(context, null);n }nn public PausePressView(@NonNull Context context, @Nullable AttributeSet attrs) {n this(context, attrs, 0);n }nn public PausePressView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {n super(context, attrs, defStyleAttr);n }n}

初始化画笔

private void initPaint() {ntpaint = new Paint();ntpaint.setColor(getContext().getResources().getColor(R.color.c_FF6E66));ntpaint.setStyle(Paint.Style.STROKE);ntpaint.setAntiAlias(true);ntpaint.setStrokeCap(Paint.Cap.ROUND);ntpaint.setStrokeWidth(10);n}

  • 颜色
  • 类型(stroke,只有外框,fill则填充,也有fill_and_stroke)
  • 抗锯齿
  • 线头圆角帽
  • 线的宽度

设置外边距,并且初始化画笔

public PausePressView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {ntsuper(context, attrs, defStyleAttr);ntinitPaint();ntsetPadding(20, 20, 20, 20);n}

也就是让图片收缩20个px,这个大小同学们根据自己的需求进行调整

(Android)写个小控件吧:长按结束运动

也就是让图片收缩20个px,这个大小同学们根据自己的需求进行调整

@Overridenprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {ntViewGroup.LayoutParams layoutParams = this.getLayoutParams();ntif (layoutParams != null) {nttdefaultWidth = layoutParams.width;nttdefaultHeight = layoutParams.height;nt}nt//获取到图片资源的大小,然后设置大小ntsetMeasuredDimension(defaultWidth, defaultHeight);n}

设置多少就用多少吧

绘制

@Overridenprotected void onDraw(Canvas canvas) {ntsuper.onDraw(canvas);ntint measuredWidth = getMeasuredWidth();ntint measuredHeight = getMeasuredHeight();ntif (rect == null) {nttrect = new RectF(10, 10, measuredWidth - 10, measuredHeight - 10);nt}ntpaint.setColor(getContext().getResources().getColor(R.color.c_FFE7E6));nt//绘制底线ntcanvas.drawArc(rect, -90, 360, false, paint);ntpaint.setColor(getContext().getResources().getColor(R.color.c_FF6E66));nt//绘制前景线ntcanvas.drawArc(rect, -90, 180, false, paint);n}

  • super.onDraw(canvas); 先绘制图片
  • 在图片绘制好以后,我们绘制扇形外圈

到这里,我们的静态UI就绘制好了。

(Android)写个小控件吧:长按结束运动

事件处理

@Overridenpublic boolean onTouchEvent(MotionEvent event) {ntint action = event.getAction();ntswitch (action) {nttcase MotionEvent.ACTION_DOWN:nttt//开始倒计时ntttthis.isRelease = false;ntttstartCountDown();ntttbreak;nttcase MotionEvent.ACTION_UP:nttcase MotionEvent.ACTION_CANCEL:nttt//结束倒计时ntttthis.isRelease = true;ntttbreak;nt}nt/*消费事件*/ntreturn true;n}

开始倒计时

n//倒计时的时长nprivate int countDownDuration = 2;nn//当前的进度nprivate float currentProgress = 0;nn//手是否有释放nprivate boolean isRelease = false;nnn/**n * 开始倒计时n */nprivate void startCountDown() {ntif (this.currentProgress >= 360) {nttthis.currentProgress = 0;nt}nt//换算成毫秒ntint duration;ntif (!isRelease) {nttduration = countDownDuration * 1000;nt} else {ntt//往回倒时只要1秒的动画即可nttduration = 1000;nt}nt//第20毫秒绘制一次进度ntint rate = duration / 20;nt//求度数ntfloat perDegree = 360 * 1.0f / rate;ntpost(new Runnable() {ntt@Overridenttpublic void run() {ntttif (!isRelease) {nttttcurrentProgress += perDegree;nttt} else {nttttcurrentProgress -= perDegree;nttt}ntttif (currentProgress < 0) {nttttcurrentProgress = 0;nttt}ntttif (currentProgress >= 360) {ntttt//结束了nttttcurrentProgress = 360;ntttt//TODO:告诉外部nttt}nttt//重新绘制ntttinvalidate();ntttif (currentProgress > 0 && currentProgress < 360) {nttttpostDelayed(this, 20);nttt}ntt}nt});n}

当然,绘制的地方字段要修改了

@Overridenprotected void onDraw(Canvas canvas) {ntsuper.onDraw(canvas);ntint measuredWidth = getMeasuredWidth();ntint measuredHeight = getMeasuredHeight();ntif (rect == null) {nttrect = new RectF(10, 10, measuredWidth - 10, measuredHeight - 10);nt}ntpaint.setColor(getContext().getResources().getColor(R.color.c_FFE7E6));nt//绘制底线ntcanvas.drawArc(rect, -90, 360, false, paint);ntpaint.setColor(getContext().getResources().getColor(R.color.c_FF6E66));nt//绘制前景线ntcanvas.drawArc(rect, -90, currentProgress, false, paint);n}

到这一步,动起来的效果就有了

(Android)写个小控件吧:长按结束运动

告诉外部当前状态

定义接口

public interface OnCountDownStateChangeListener {nt//倒计时结束ntvoid onCountDownEnd();nnt//用户取消倒计时ntvoid onCountDownCancel();n}

设置接口

public void setOnCountDownStateChangeListener(OnCountDownStateChangeListener listener) {ntthis.mOnCountDownStateChangeListener = listener;n}

当倒计时结束时调用接口

/**n * 开始倒计时n */nprivate void startCountDown() {ntif (this.currentProgress >= 360) {nttthis.currentProgress = 0;nt}nt//换算成毫秒ntint duration;ntif (!isRelease) {nttduration = countDownDuration * 1000;nt} else {ntt//往回倒时只要1秒的动画即可nttduration = 1000;nt}nt//第20毫秒绘制一次进度ntint rate = duration / 20;nt//求度数ntfloat perDegree = 360 * 1.0f / rate;ntpost(new Runnable() {ntt@Overridenttpublic void run() {ntttif (!isRelease) {nttttcurrentProgress += perDegree;nttt} else {nttttcurrentProgress -= perDegree;nttt}ntttif (currentProgress < 0) {nttttcurrentProgress = 0;nttt}ntttif (currentProgress >= 360) {ntttt//结束了nttttcurrentProgress = 360;ntttt//告诉外部nttttif (mOnCountDownStateChangeListener != null) {ntttttmOnCountDownStateChangeListener.onCountDownEnd();ntttt}nttt}nttt//重新绘制ntttinvalidate();ntttif (currentProgress > 0 && currentProgress < 360) {nttttpostDelayed(this, 20);nttt}ntt}nt});n}

当用户手释放的时候:

@Overridenpublic boolean onTouchEvent(MotionEvent event) {n int action = event.getAction();n switch (action) {n case MotionEvent.ACTION_DOWN:n //开始倒计时n this.isRelease = false;n startCountDown();n break;n case MotionEvent.ACTION_UP:n case MotionEvent.ACTION_CANCEL:n //结束倒计时n this.isRelease = true;n //告诉外部n if (currentProgress < 360 && mOnCountDownStateChangeListener != null) {n mOnCountDownStateChangeListener.onCountDownCancel();n }n break;n }n /*消费事件*/n return true;n}

到这里,基本功能就完成了。

还需要暴露设置属性方法,以及定义自定义属性的配置。

这个很简单,详情就不多说了,可以去参考一下我们的自定义控件课程。

Android自定义控件课程

【完结】Android开发自定义组合控件-轮播图

欢迎大家关注我们的公众号:阳光沙滩官网

(Android)写个小控件吧:长按结束运动