Android 跟随手指移动的圆

通过onTouchEvent实现一个简单的跟随触摸点移动的圆

public class CircleFollowView extends View {
	private static final String Tag = "CircleFollowView";

	private float currentX = 50;
	private float currentY = 50;
	private int radius = 50;
	// 控件宽度
	private int mWidth;
	// 控件高度
	private int mHeight;
	private int lastX;
	private int lastY;

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

	public CircleFollowView(Context context, AttributeSet attrs) {
		super(context, attrs);
	}

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

	private void initView() {
		// 取最小的长度的一半作为半径
		// layout中View要设定绝对大小,例如具体dp或者match_patent,否则点击屏幕其他地方也可能移动圆
		this.radius = (mWidth / 2 < mHeight / 2) ? mWidth / 2 : mHeight / 2;
		this.currentX = mWidth / 2;
		this.currentY = mHeight / 2;
	}

	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		int widthMode = MeasureSpec.getMode(widthMeasureSpec);
		int heightMode = MeasureSpec.getMode(heightMeasureSpec);
		int widthSize = MeasureSpec.getSize(widthMeasureSpec);
		int heightSize = MeasureSpec.getSize(heightMeasureSpec);

		if (widthMode == MeasureSpec.EXACTLY) {
			mWidth = widthSize;
		} else if (widthMode == MeasureSpec.AT_MOST) {
			throw new IllegalArgumentException(
					"width must be EXACTLY,you should set like android:width=\"200dp\"");
		}

		if (heightMode == MeasureSpec.EXACTLY) {
			mHeight = heightSize;
		} else if (widthMeasureSpec == MeasureSpec.AT_MOST) {

			throw new IllegalArgumentException(
					"height must be EXACTLY,you should set like android:height=\"200dp\"");
		}

		setMeasuredDimension(mWidth, mHeight);
	}

	@SuppressLint("DrawAllocation")
	@Override
	protected void onDraw(Canvas canvas) {
		super.onDraw(canvas);
		initView();
		Paint p = new Paint();
		p.setColor(Color.RED);

		canvas.drawCircle(currentX, currentY, radius, p);
	}

	@Override
	public boolean onTouchEvent(MotionEvent event) {
		// 触摸点到屏幕左上角的距离
		int rawX = (int) event.getRawX();
		int rawY = (int) event.getRawY();
		switch (event.getAction()) {
		case MotionEvent.ACTION_DOWN:
			lastX = rawX;
			lastY = rawY;
			break;
		case MotionEvent.ACTION_MOVE:
			int offsetX = rawX - lastX;
			int offsetY = rawY - lastY;
			offsetLeftAndRight(offsetX);
			offsetTopAndBottom(offsetY);
			lastX = rawX;
			lastY = rawY;
			break;
		case MotionEvent.ACTION_UP:
			lastX = rawX;
			lastY = rawY;
			break;
		}		
		return true;
	}
}

上面一种方式是通过修改View距离屏幕的位置实现滑动,下面使用Scroller(其他内容相同,只展示onTouchEvent部分):

int offsetX;
	int offsetY;

	@Override
	public boolean onTouchEvent(MotionEvent event) {
		int x = (int) event.getX();
		int y = (int) event.getY();
		switch (event.getAction()) {
		case MotionEvent.ACTION_DOWN:
			lastX = (int) event.getX();
			lastY = (int) event.getY();
			break;
		case MotionEvent.ACTION_MOVE:
			offsetX = x - lastX;
			offsetY = y - lastY;
			 ((View) getParent()).scrollBy(-offsetX, -offsetY);
			 invalidate();
			break;
		default:
			break;
		}
		return true;
	}

scroll的位移从左到右为负值,从右到左是正值,从上到下是负值,从下到上是正值,所以使用scrollBy时参数取负。

((View) getParent()).scrollBy(-offsetX, -offsetY);如果替换为

View viewGroup = ((View) getParent());
mScroller.startScroll(viewGroup.getScrollX(),  viewGroup.getScrollY(), -offsetX, -offsetY,1000);//1s完成滑动

需要配合

        public void computeScroll() {
		super.computeScroll();
		if (mScroller.computeScrollOffset()) {
			((View) getParent()).scrollTo(mScroller.getCurrX(),
					mScroller.getCurrY());
			// 通过重绘来不断调用computeScroll
			invalidate();
		}
	}

通过invalidate()->onDraw()->computeScroll()一部分一部分重绘实现滑动,但是不能实时,可以实现一种延迟的滑动效果。

scrollTo/scrollBy这种方式能比较方便的实现滑动效果并且不影响点击时间,但是他只能滑动View的内部,所以上面代码中滑动使用的是getParent(),实际运用时,滑动的部分最好单独写在一个ViewGroup中,否则父组件内的所有内容会一起滑动。

第三种修改LayoutParams,向左移动,就增加marginLeft:

	public boolean onTouchEvent(MotionEvent event) {
		// 触摸点到屏幕左上角的距离
		int rawX = (int) event.getRawX();
		int rawY = (int) event.getRawY();
		VelocityTracker velocityTracker = VelocityTracker.obtain();

		switch (event.getAction()) {
		case MotionEvent.ACTION_DOWN:
			lastX = rawX;
			lastY = rawY;
			break;
		case MotionEvent.ACTION_MOVE:
			int offsetX = rawX - lastX;
			int offsetY = rawY - lastY;			
			MarginLayoutParams layoutParams = (MarginLayoutParams) getLayoutParams();
			layoutParams.leftMargin += offsetX;
			layoutParams.topMargin += offsetY;
			requestLayout();//或者setLayoutParams(layoutParams);
			lastX = rawX;
			lastY = rawY;			
			break;
		case MotionEvent.ACTION_UP:
			lastX = rawX;
			lastY = rawY;
			break;
		}		
		return true;
	}

最后,如果滑动的效果不是实时的,也可以采用动画的方式,动画比较适合复杂的效果和没有交互的View。


  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值