Android自定义控件系列——View与内容作滑动或动画

View与内容作滑动或动画

控件滑动

View滑动的本质就是随着手指的运动不断地改变坐标。 当触摸事件传到View时,系统记下触摸点的坐标,手指移动时系统记下移动后的触摸的坐标并算出偏移量,并通过偏移量来修改View的坐标,不断的重复这样的过程,从而实现滑动过程

方法一:layout(),控制View的坐标

private int lastX = 0;
private int lastY = 0;
@Override
public boolean onTouchEvent(MotionEvent event) {
	int x = (int) event.getX();
	int y = (int) event.getY();
	switch (event.getAction()) {
		case MotionEvent.ACTION_DOWN:
			// 记录触摸点坐标
			lastX = x;
			lastY = y;
			break;
		case MotionEvent.ACTION_MOVE:
			// 计算偏移量
			int offsetX = x - lastX;
			int offsetY = y - lastY;
			// 在当前left、top、right、bottom的基础上加上偏移量
			layout(getLeft() + offsetX,
					getTop() + offsetY,
					getRight() + offsetX,
					getBottom() + offsetY);
			//重新设置初始坐标
            x = (int)(event.getRawX());
            y = (int)(event.getRawY());
			break;
	}
	return true;
}

方法二:offsetLeftAndRight() 与 offsetTopAndroidBottom()

private int lastX = 0;
private int lastY = 0;
@Override
public boolean onTouchEvent(MotionEvent event) {
	int x = (int) event.getX();
	int y = (int) event.getY();
	switch (event.getAction()) {
		case MotionEvent.ACTION_DOWN:
			// 记录触摸点坐标
			lastX = x;
			lastY = y;
			break;
		case MotionEvent.ACTION_MOVE:
			// 计算偏移量
			int offsetX = x - lastX;
			int offsetY = y - lastY
			//同时对于left 和 right进行偏移
			offsetLeftAndRight(offsetX);
			//同时对于top 和 bottom进行偏移
			offsetTopAndBottom(offsetY);
			//重新设置初始坐标
			x = (int)(event.getRawX());
			y = (int)(event.getRawY());
			break;
	}
	return true;
}

方法三:LayoutParams,改变布局参数

private int lastX = 0;
private int lastY = 0;
@Override
public boolean onTouchEvent(MotionEvent event) {
	int x = (int) event.getX();
	int y = (int) event.getY();
	switch (event.getAction()) {
		case MotionEvent.ACTION_DOWN:
			// 记录触摸点坐标
			lastX = x;
			lastY = y;
			break;
		case MotionEvent.ACTION_MOVE:
			// 计算偏移量
			int offsetX = x - lastX;
			int offsetY = y - lastY;
            //获得 LayoutParams对象的时候,需要将其转换成直接父View(这个View的上一层布局)的类型
			LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) getLayoutParams();
            layoutParams.leftMargin = getLeft() + offsetX;
            layoutParams.topMargin = getTop() + offsetY;
            setLayoutParams(layoutParams);
			//重新设置初始坐标
            x = (int)(event.getRawX());
            y = (int)(event.getRawY());
			break;
	}
	return true;
}

方法四:scrollTo()与ScrollBy()

private int lastX = 0;
private int lastY = 0;
@Override
public boolean onTouchEvent(MotionEvent event) {
	int x = (int) event.getX();
	int y = (int) event.getY();
	switch (event.getAction()) {
		case MotionEvent.ACTION_DOWN:
			// 记录触摸点坐标
			lastX = x;
			lastY = y;
			break;
		case MotionEvent.ACTION_MOVE:
			// 计算偏移量
			int offsetX = x - lastX;
			int offsetY = y - lastY;
			//scroll方法移动的是View的父布局,因此,子View相对于父布局移动,相当于父布局取反移动
			((View)getParent()).scrollBy(-offsetX,-offsetY);
			//重新设置初始坐标
            x = (int)(event.getRawX());
            y = (int)(event.getRawY());
			break;
	}
	return true;
}

方法五:Scroller

//Scroll本身无法让View滑动,它需要和View的computeScroll方法配合才能使用
Scroller scroller =new Scroller(mContext);

private int lastX = 0;
private int lastY = 0;
@Override
public boolean onTouchEvent(MotionEvent event){
	int getX = (int) event.getX();
	int getY = (int) event.getY();
	switch (event.getAction()){
		case MotionEvent.ACTION_DOWN:
			lastX = getX;
			lastY = getY;
			break;
		case MotionEvent.ACTION_MOVE:
			int offsetX = getX - lastX;
			int offsetY = getY - lastY;
			smoothScrollTo(-offsetX, -offsetY);
			break;
		case MotionEvent.ACTION_UP:
			break;
	}
	return false;
}
@Override
public void computeScroll(){
    // 判断Scroller滑动是否执行完毕
	if (mScroller.computeScrollOffset()){
		scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
         // 通过重绘让系统调用onDraw,onDraw中又会调用computeScroll,如此不断循环,直到Scroller执行完毕
		postInvalidate();
	}
}
private void smoothScrollTo(int destX, int destY){
	int scrollX = getScrollX();
	int delta = destX = scrollX;
	mScroller.startScroll(scrollX, 0, delta, 0);
    // 必须调用该方法通知View重绘以便computeScroll方法被调用
	invalidate();
}

方法六:动画,帧动画,属性动画,LayoutAnimation

方法七:ViewDragHelper

View内容作动画

方法一:ValueAnimator,结合setInterpolator等属性

//创建一个动画过程
ValueAnimator animator = ValueAnimator.ofFloat(0, 1);
//设置过程的持续行为方式
animator.setInterpolator(new LinearInterpolator());
//设置过程的时间
animator.setDuration(1000);
//监听过程,对过程的每个细节进行更新
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
    //获取到过程的百分比(进度)
	float fraction = animation.getAnimatedFraction();
    //根据进度,建立跟view的关系,如view中的一个圆的半径变化
    radius = fraction * 100;
    //通知view刷新(重点)
  	invalidate();
}});
//开始动画过程
animator.start();

方法二:Handler消息机制等消息通知的形式。类似的如接口回调等

private float radius;
private Handler handler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
        if (msg.what == 1) {
            //更新view的参数
            radius++;
            //刷新view
            invalidate();
}}};
@Override
protected void onDraw(Canvas canvas) {
    //发送消息
	handler.sendEmptyMessage(1);
}

方法三: 死循环或postInvalidateDelayed(long delayMilliseconds)

private float radius;
@Override
protected void onDraw(Canvas canvas) {
	radius++;
	//如hanlder机制,延迟指定时间段后刷新view,从而实现不断更新view的内容
     //或使用invalidate();立即刷新view(性能消耗较高)
	postInvalidateDelayed(10);
}

方法四:利用时间,时间是平滑的变化过程

private float radius;
//第一次的时间
private long firstTime;
//是否初始化标志
private boolean isInit;
@Override
protected void onDraw(Canvas canvas) {
	if (isInit) {
        //变化状态
		isInit = false;
        //记录第一次的时间毫秒值
		firstTime = System.currentTimeMillis();
	}
    //更新view内容
	radius = (System.currentTimeMillis() - firstTime) * 2;
    //刷新view
	invalidate();
}

猜你喜欢

转载自blog.csdn.net/mLuoya/article/details/88654023