自定义View之大风车系列demo(三)

大风车系列版本1.0到版本3.0的体验有很大的不足:风车旋转的弧度都是手动写死的,不会根据手指移动的快慢而快慢。版本4.0将解决这个问题,思路如下:

1)以风车图片的中心为坐标原点简历一个直角坐标系,捕获手指按下事件也即是MotionEvent.ACTION_DOWN事件,记录此时手指的坐标点与直角坐标系x正坐标轴的夹角

2)获取手指移动的时候当前手指坐标点与直角坐标系x正轴的夹角。

3)计算步骤1和步骤2的两个夹角的差,就是手指此时移动的弧度。而不像版本3.0之前的那样写死了

具体代码设计如下:提供了速度控制器类SpeedControl类,在里面主要提供了获取当前坐标点与x正轴的夹角的方法,该类的的代码如下:

package rotation.demo.bean;
public class SpeedControl {

	/** 手指按下时候的弧度 **/
	private float down_rad;

	/** 手指move时当前坐标的弧度 **/
	private float current_move_rad;

	/** 弧度增量,它的值等于current_move_rad - down_rad**/
	private float Δrad;

	/** 图片的中心原点 **/
	private float x0, y0;

	public SpeedControl(float x0, float y0) {
		this.x0 = x0;
		this.y0 = y0;
	}

	/***
	 * 计算当前坐标点与x轴的夹角所代表的弧度,弧度计算公式为 1rad = 180/Math.PI,<br>
	 * 需要注意的是直角坐标系分四个象限,每个象限的坐标点与x轴的夹角计算时需要计算一下
	 *
	 * @param current_x
	 *            当前坐标点的横坐标点
	 * @param current_y
	 *            当前坐标点的纵坐标点
	 * @return
	 */
	public float computeRad(float current_x, float current_y) {
		final float Δx = current_x - x0;
		final float Δy = current_y - y0;

		double θ = 0f;// 夹角
		// 求夹角的正切的绝对值
		float tanθ = Math.abs(Δy / Δx);

		if (Δx > 0) {// 当坐标点在1或者4象限的情况
			if (Δy >= 0) {// 坐标点位于第一象限
				θ = Math.atan(tanθ);
			} else {// 当坐标点位于第四象限
				θ = 2 * Math.PI - Math.atan(tanθ);
			}
		} else {// 当坐标点位于2或3象限
			if (Δy >= 0) {// 位于第二象限

				θ = Math.PI - Math.atan(tanθ);
			} else {// 位于第三象限
				θ = Math.PI + Math.atan(tanθ);
			}
		}
                //注意一弧度 = 180/Math.PI
		float result = (float) ((180 * θ) / Math.PI);
		return result;
	}

	public float getDown_rad() {
		return down_rad;
	}

	public void setDown_rad(float down_rad) {
		this.down_rad = down_rad;
	}

	public float getCurrent_move_rad() {
		return current_move_rad;
	}

	public void setCurrent_move_rad(float current_move_rad) {
		this.current_move_rad = current_move_rad;
	}
        //该方法工Matrix.preRotate使用
	public float getΔrad() {
		return Δrad;
	}

	public void setΔrad(float δrad) {
		Δrad = δrad;
	}

}

进而RotationView的代码做了如下改变,主要体现在onTouchEvent和onDraw上:

package rotation.demo.view;

import rotation.demo.bean.SpeedControl;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;

/**
 * verson3.0 前两个版本都是手指离开屏幕的时候就会立即停止转动,现在这个版本让当手指抬起的时候,由于惯性让风车继续转动一点时间。
 * 思路:监听手指抬起事件,然后重绘
 *
 * @author YanQiu
 *
 */
public class RotationView extends View {

	/** 要转动的图片 **/
	private Bitmap bitMap;
	/** 风车每次转动的弧度 **/
	private int degree = 0;
	/** 图片的宽度:在这里提供的是正方形的图片,所以宽度和高度是一样的 **/
	private int width = 0;
	/*** 图片的高度:在这里提供的是正方形的图片,所以宽度和高度是一样的 **/
	private int height = 0;
	/** 定义一个画笔 **/
	private Paint paint = new Paint();
	/**手指抬起的时间**/
	private long upTime = 0;
	/**手指抬起的时候风车持续转动的时间**/
	private final long stopTimeDuration = 5000;
	/**速度控制器**/
	private SpeedControl speedControl;
	public RotationView(Context context, AttributeSet attrs) {
		super(context, attrs);
	}

	public RotationView(Context context, AttributeSet attrs, int defStyleAttr) {
		super(context, attrs, defStyleAttr);
	}

	public RotationView(Context context) {
		super(context);
	}

	private String tag = "";

	/**
	 * 计算图片的圆心
	 */
	public void initSize() {
		width = bitMap.getWidth();
		height = bitMap.getHeight();
		speedControl = new SpeedControl(width/2,height/2);
		postInvalidate();
	}

	public void setBitMap(Bitmap bitMap) {
		this.bitMap = bitMap;
	}

	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		// TODO Auto-generated method stub
		super.onMeasure(widthMeasureSpec, heightMeasureSpec);
		setMeasuredDimension(width, width);
	}

	@Override
	protected void onDraw(Canvas canvas) {

		Matrix matrix = new Matrix();
		// 设置转轴位置
		matrix.setTranslate((float) width / 2, (float) height / 2);

		// 开始转,为手指转过的弧度
		matrix.preRotate(speedControl.getΔrad());
		// 转轴还原
		matrix.preTranslate(-(float) width / 2, -(float) height / 2);
		canvas.drawBitmap(bitMap, matrix, paint);

		super.onDraw(canvas);
	}

	@Override
	public boolean onTouchEvent(MotionEvent event) {
		int action = event.getAction();
		switch (action) {
		case MotionEvent.ACTION_DOWN://获取手指按下的坐标点与x轴夹角的弧度
                        final float down_rad = speedControl.computeRad(event.getX(), event.getY());
			speedControl.setDown_rad(down_rad);
			break;
		case MotionEvent.ACTION_MOVE:// 随着手指的move而不断进行重绘
			final float current_move_rad = speedControl.computeRad(event.getX(), event.getY());
			final float Δrad = current_move_rad - speedControl.getDown_rad();//计算手指滑过的弧度差
			speedControl.setΔrad(Δrad);
			//该方法在UI线程自身中使用
			postInvalidate();
			break;
		case MotionEvent.ACTION_UP:// 随着手指的move而不断进行重绘
			upTime = System.currentTimeMillis();
			post(new Runnable() {
				@Override
				public void run() {
					long duration = System.currentTimeMillis()-upTime;
					if(duration ==stopTimeDuration ) {
						return;
					}else if(duration<stopTimeDuration) {
						post(this);
					}
					//在非UI线程中使用。
					invalidate();
				}
			});

			break;
		}
		return true;
	}

}

经过测试这个版本可以让风车随着手指移动速度的变化而变化,但是还是有个问题,就是手指抬起的时候同样的风车会停止转动,这个将在最后的版本来解决

时间: 2024-10-19 16:09:01

自定义View之大风车系列demo(三)的相关文章

自定义View之大风车系列demo(一)

每次写博客最讨厌写博客的开头,不知道该写些什么,现在也是,感觉跟写八百字作文似的.之所以会写这一系列的demo是因为前天看网上的一个图片转圈的源码的时候 突发奇想要不要自己也弄个耍耍,顺便学习下view的相关知识!说干就干,当然自己写的时候也不免参照写别人得到代码,毕竟自定义view与我的水平来说确实具有挑战性,通过完成这一些列的小demo确实收获颇多,虽然还有些许在我看来更牛逼的功能自己没法实现,但自己的目的也算达到了.先啰嗦了这么多,先说说大风车系列小demo的总体概况:一共五个版本,这也意

自定义View之大风车系列demo(二)

版本1.0和版本2.0的风车有个不尽人意的效果:当手指抬起的时候风车就会停止转动,现在版本3要做的就是让手指抬起的时候让风车继续转动一段时间,思路如下: 1)手指抬起的时候继续让风车转动5秒 2)需要监听MotionEvent.ACTION_UP事件并记录手指抬起的时间upTime 3)捕获MotionEvent.ACTION_UP事件并重绘五秒钟 根据上面的说明RotationView的代码做了如下的改变(在版本2的基础上主要修改了onTouchEvent事件: /**手指抬起的时间**/  

自定义View之大风车系列demo(四)

版本4.0有个问题当手指抬起的时候风车停止了,现在就解决这个问题,手指抬起的时候继续让风车由于惯性而继续旋转一段时间.思路如下 1)需要获取手指抬起时风车转动的瞬间速度,这个难度不小,在我的demo里面我只是简单的计算了手指按下到抬起经过的时间和弧度差,用弧度差除以时间来模拟下速度. 具体的还真不好用语言描述,所以偷个懒,直接上代码吧速度控制器添加了speed变量,并根据弧度差和经过的时间来计算速度 package rotation.demo.bean; import android.util.

自定义View之绘图篇(三):文字(Text)

顺境也好,逆境也好,人生就是一场对种种困难无尽无休的斗争,一场以寡敌众的战斗.--泰戈尔 相关文章: 自定义View之绘图篇(一):基础图形的绘制 自定义View之绘图篇(二):路径(Path) 一.文字 相关方法预览: //普通设置 paint.setAntiAlias(true); //指定是否使用抗锯齿功能 如果使用会使绘图速度变慢 默认false setStyle(Paint.Style.FILL);//绘图样式 对于设文字和几何图形都有效 setTextAlign(Align.LEFT

Android自定义View(四)----一步一步教你实现QQ健康界面

最近一直在学习自定义View相关的知识,今天给大家带来的是QQ健康界面的实现.先看效果图: 可以设置数字颜色,字体颜色,运动步数,运动排名,运动平均步数,虚线下方的蓝色指示条的长度会随着平均步数改变而进行变化.整体效果还是和QQ运动健康界面很像的. 自定义View四部曲,一起来看看怎么实现的. 1.自定义view的属性: <?xml version="1.0" encoding="utf-8"?> <resources> //自定义属性名,定

自定义View之绘图篇(四):baseLine和FontMetrics

乐观是一首激昂优美的进行曲,时刻鼓舞着你向事业的大路勇猛前进.--大仲马 相关文章: 自定义View之绘图篇(一):基础图形的绘制 自定义View之绘图篇(二):路径(Path) 自定义View之绘图篇(三):文字(Text) 了解baseLine和FontMetrics有助于我们理解drawText()绘制文字的原理,下面我们一起来看看呗. 一.baseLine 基线 记得小时候练习字母用的是四线格本,把字母写在四线格内,如下: 那么在canvas中drawText绘制文字时候,也是有规则的,

自定义View系列教程07--详解ViewGroup分发Touch事件

自定义View系列教程01–常用工具介绍 自定义View系列教程02–onMeasure源码详尽分析 自定义View系列教程03–onLayout源码详尽分析 自定义View系列教程04–Draw源码分析及其实践 自定义View系列教程05–示例分析 自定义View系列教程06–详解View的Touch事件处理 自定义View系列教程07–详解ViewGroup分发Touch事件 PS:如果觉得文章太长,那就直接看视频吧 在上一篇中已经分析完了View对于Touch事件的处理,在此基础上分析和理

自定义View系列教程05--示例分析

自定义View系列教程01–常用工具介绍 自定义View系列教程02–onMeasure源码详尽分析 自定义View系列教程03–onLayout源码详尽分析 自定义View系列教程04–Draw源码分析及其实践 自定义View系列教程05–示例分析 PS:如果觉得文章太长,那就直接看视频吧 之前结合源码分析完了自定义View的三个阶段:measure,layout,draw. 那么,自定义有哪几种常见的方式呢? 直接继承自View 在使用该方式实现自定义View时通常的核心操作都在onDraw

自定义View系列教程01--常用工具介绍

在自定义View的时候,常常会用到一些Android系统提供的工具.这些工具封装了我们经常会用到的方法,比如拖拽View,计算滑动速度,View的滚动,手势处理等等.如果我们自己去实现这些方法会比较繁琐,而且容易出一些bug.所以,作为自定义View系列教程的开端,先介绍一下这些常用的工具,以便在后续的学习和工作中使用. Configuration ViewConfiguration GestureDetector VelocityTracker Scroller ViewDragHelper