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

UI效果图

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

功能

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

实现

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

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

由上分析

创建一个继承于ImageView的控件

public class PausePressView extends AppCompatImageView {
    public PausePressView(@NonNull Context context) {
        this(context, null);
    }

    public PausePressView(@NonNull Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public PausePressView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }
}

初始化画笔

private void initPaint() {
	paint = new Paint();
	paint.setColor(getContext().getResources().getColor(R.color.c_FF6E66));
	paint.setStyle(Paint.Style.STROKE);
	paint.setAntiAlias(true);
	paint.setStrokeCap(Paint.Cap.ROUND);
	paint.setStrokeWidth(10);
}
  • 颜色
  • 类型(stroke,只有外框,fill则填充,也有fill_and_stroke)
  • 抗锯齿
  • 线头圆角帽
  • 线的宽度

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

public PausePressView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
	super(context, attrs, defStyleAttr);
	initPaint();
	setPadding(20, 20, 20, 20);
}

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

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

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

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
	ViewGroup.LayoutParams layoutParams = this.getLayoutParams();
	if (layoutParams != null) {
		defaultWidth = layoutParams.width;
		defaultHeight = layoutParams.height;
	}
	//获取到图片资源的大小,然后设置大小
	setMeasuredDimension(defaultWidth, defaultHeight);
}

设置多少就用多少吧

绘制

@Override
protected void onDraw(Canvas canvas) {
	super.onDraw(canvas);
	int measuredWidth = getMeasuredWidth();
	int measuredHeight = getMeasuredHeight();
	if (rect == null) {
		rect = new RectF(10, 10, measuredWidth - 10, measuredHeight - 10);
	}
	paint.setColor(getContext().getResources().getColor(R.color.c_FFE7E6));
	//绘制底线
	canvas.drawArc(rect, -90, 360, false, paint);
	paint.setColor(getContext().getResources().getColor(R.color.c_FF6E66));
	//绘制前景线
	canvas.drawArc(rect, -90, 180, false, paint);
}
  • super.onDraw(canvas); 先绘制图片
  • 在图片绘制好以后,我们绘制扇形外圈

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

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

事件处理

@Override
public boolean onTouchEvent(MotionEvent event) {
	int action = event.getAction();
	switch (action) {
		case MotionEvent.ACTION_DOWN:
			//开始倒计时
			this.isRelease = false;
			startCountDown();
			break;
		case MotionEvent.ACTION_UP:
		case MotionEvent.ACTION_CANCEL:
			//结束倒计时
			this.isRelease = true;
			break;
	}
	/*消费事件*/
	return true;
}

开始倒计时


//倒计时的时长
private int countDownDuration = 2;

//当前的进度
private float currentProgress = 0;

//手是否有释放
private boolean isRelease = false;


/**
 * 开始倒计时
 */
private void startCountDown() {
	if (this.currentProgress >= 360) {
		this.currentProgress = 0;
	}
	//换算成毫秒
	int duration;
	if (!isRelease) {
		duration = countDownDuration * 1000;
	} else {
		//往回倒时只要1秒的动画即可
		duration = 1000;
	}
	//第20毫秒绘制一次进度
	int rate = duration / 20;
	//求度数
	float perDegree = 360 * 1.0f / rate;
	post(new Runnable() {
		@Override
		public void run() {
			if (!isRelease) {
				currentProgress += perDegree;
			} else {
				currentProgress -= perDegree;
			}
			if (currentProgress < 0) {
				currentProgress = 0;
			}
			if (currentProgress >= 360) {
				//结束了
				currentProgress = 360;
				//TODO:告诉外部
			}
			//重新绘制
			invalidate();
			if (currentProgress > 0 && currentProgress < 360) {
				postDelayed(this, 20);
			}
		}
	});
}

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

@Override
protected void onDraw(Canvas canvas) {
	super.onDraw(canvas);
	int measuredWidth = getMeasuredWidth();
	int measuredHeight = getMeasuredHeight();
	if (rect == null) {
		rect = new RectF(10, 10, measuredWidth - 10, measuredHeight - 10);
	}
	paint.setColor(getContext().getResources().getColor(R.color.c_FFE7E6));
	//绘制底线
	canvas.drawArc(rect, -90, 360, false, paint);
	paint.setColor(getContext().getResources().getColor(R.color.c_FF6E66));
	//绘制前景线
	canvas.drawArc(rect, -90, currentProgress, false, paint);
}

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

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

告诉外部当前状态

定义接口

public interface OnCountDownStateChangeListener {
	//倒计时结束
	void onCountDownEnd();

	//用户取消倒计时
	void onCountDownCancel();
}

设置接口

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

当倒计时结束时调用接口

/**
 * 开始倒计时
 */
private void startCountDown() {
	if (this.currentProgress >= 360) {
		this.currentProgress = 0;
	}
	//换算成毫秒
	int duration;
	if (!isRelease) {
		duration = countDownDuration * 1000;
	} else {
		//往回倒时只要1秒的动画即可
		duration = 1000;
	}
	//第20毫秒绘制一次进度
	int rate = duration / 20;
	//求度数
	float perDegree = 360 * 1.0f / rate;
	post(new Runnable() {
		@Override
		public void run() {
			if (!isRelease) {
				currentProgress += perDegree;
			} else {
				currentProgress -= perDegree;
			}
			if (currentProgress < 0) {
				currentProgress = 0;
			}
			if (currentProgress >= 360) {
				//结束了
				currentProgress = 360;
				//告诉外部
				if (mOnCountDownStateChangeListener != null) {
					mOnCountDownStateChangeListener.onCountDownEnd();
				}
			}
			//重新绘制
			invalidate();
			if (currentProgress > 0 && currentProgress < 360) {
				postDelayed(this, 20);
			}
		}
	});
}

当用户手释放的时候:

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

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

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

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

Android自定义控件课程

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

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

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

发表评论
留言与评论(共有 0 条评论) “”
   
验证码:

相关文章

推荐文章