实现滑动的七种方法
- 实现效果图如下:
- 1.layout方法
注: 这里在计算偏移量的时候,可以使用getX(),也可以使用getRawX(),不同的是,使用getRawX()在ACTION_MOVE的逻辑后,要重新设置初始坐标。
>public
class
DrawView1 extends View {
private int lastX;
private int lastY;
private void intiView() {
//给view设置背景颜色,便于观察
setBackgroundColor(Color.YELLOW);
}
public DrawView1(Context context) {
super(context);
intiView();
}
public DrawView1(Context context, AttributeSet attrs) {
super(context, attrs);
intiView();
}
public DrawView1(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
intiView();
}
//视图坐标方式
@Override
public boolean onTouchEvent(MotionEvent event) {
int x = (int) event.getX();
int y = (int) event.getY();
// int x = (int) (event.getRawX());
// int y = (int) (event.getRawY());
Log.d("Lch", "onTouchEvent x: "+x+"y: "+y);
Log.d("Lch", "onTouchEvent RawX: " + event.getRawX() + " RawY: " + event.getRawY());
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
//记录触摸点的坐标
lastX = x;
lastY = y;
Log.d("Lch", "ACTION_DOWN");
break;
case MotionEvent.ACTION_MOVE:
//计算偏移量
int offsetx = x - lastX;
int offsety = y - lastY;
//调用layout方法在当前的left、top、right、bottom的基础上那个加上偏移量;
layout(getLeft() + offsetx,
getTop() + offsety,
getRight() + offsetx,
getBottom() + offsety);
Log.d("Lch", "ACTION_MOVE");
//使用绝对坐标系时需重新设置初始坐标
// lastX = x;
// lastY = y;
break;
}
return true;
}
}
-
2.offsetLeftAndRight()和offsetTopAndBottom()方法
这两个方法是系统提供的一个对于左右、上下移动的API,当计算出偏移量后,只需使用以下代码就可完成View的重新布局。
>//同时对left和right进行偏移
offsetLeftAndRight(offsetx);
//同时对top和bottom进行偏移
offsetTopAndBottom(offsety);
-
3.使用LayoutParams
LaoutParams保存了一个View的布局参数,因此可以在程序中,通过改变LayoutParams来动态修改一个布局的位置参数,从而达到改变View位置的效果。
LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams)getLayoutParams();
//ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) getLayoutParams();
layoutParams.leftMargin = getLeft() + offsetx;
layoutParams.topMargin = getTop() + offsety; setLayoutParams(layoutParams);
-
4.scrollTo和scrollBy
在一个View中,系统提供了scrollTo、scrollBy两种方式来改变一个View的位置,scrollTo和scrollBy的区别在于,to是移动到指定的坐标,而by是移动指定的增量。
与前面几种方式不同,在获取偏移量后使用scrollBy来移动View,代码如下:
int offsetX = x - lastX;
int offsetY = y - lastY;
scrollBy(offsetX, offsetY);
但是这样设置之后,移动的只是View中的所有子view,比如TextView的话,移动的就是它的文本;ImageView,移动的就是它的drawable对象。所以如果想通过这种方式实现前面的效果,使用Scroller需三步:
- 初始化Scroller
- 重写computeScroller()方法,实现模拟滑动
- 使用startScroll开启模拟过程
- 具体代码如下。
int offsetX = x - lastX;
int offsetY = y - lastY;
((View)getParent()).scrollBy(-offsetX, -offsetY);
-
5.Scroller
Scroller可以在view的layout改变时实现平滑移动的效果,而不实瞬间完成的效果。
使用Scroller的具体代码如下
public
class
DrawView3 extends View {
private int lastX;
private int lastY;
private Scroller mScroller;
public void initView(Context context) {
setBackgroundColor(Color.YELLOW);
mScroller = new Scroller(context);
}
public DrawView3(Context context) {
super(context);
initView(context);
}
public DrawView3(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView(context);
}
public DrawView3(Context context, AttributeSet attrs) {
super(context,attrs);
initView(context);
}
@Override
public void computeScroll() {
super.computeScroll();
//判断Scroller是否执行完毕
if (mScroller.computeScrollOffset()) {
((View) getParent()).scrollTo(
mScroller.getCurrX(),
mScroller.getCurrY());
//通过重绘来不断调用computeScroller
invalidate();
}
}
@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:
int offsetX = x - lastX;
int offsetY = y - lastY;
((View) getParent()).scrollBy(-offsetX, -offsetY);
break;
case MotionEvent.ACTION_UP:
//手指离开时,执行滑动过程
View viewGroup = ((View) getParent());
mScroller.startScroll(
viewGroup.getScrollX(),
viewGroup.getScrollY(),
-viewGroup.getScrollX(),
-viewGroup.getScrollY(),
3000);
invalidate();
break;
}
return true;
}
}
6.使用属性动画
使用属性动画也可以控制一个view的移动效果。
7. 使用ViewDragHelper
public
class
DragViewGroup extends FrameLayout{
private ViewDragHelper mViewDragHelper;
private View mMenuview, mMainView;
private int mWidth;
public DragViewGroup(Context context) {
super(context);
initView();
}
public DragViewGroup(Context context, AttributeSet attrs) {
super(context, attrs);
initView();
}
public DragViewGroup(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView();
}
private void initView() {
//初始化ViewDragHelper,第一个参数为要监听的view, 第二个参数是一个callback回调
//这个回调是整个ViewDragHelper的逻辑核心
mViewDragHelper = ViewDragHelper.create(this, callback);
}
//从xml加载组件后的回调
@Override
protected void onFinishInflate() {
super.onFinishInflate();
mMenuview = getChildAt(0);
mMainView = getChildAt(1);
}
//组件大小改变后的回调
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mWidth = mMenuview.getMeasuredWidth();
}
//重写事件拦截方法
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
return mViewDragHelper.shouldInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
//将触摸事件传递给ViewDragViewHelper,此操作必不可少
mViewDragHelper.processTouchEvent(event);
return true;
}
private ViewDragHelper.Callback callback = new ViewDragHelper.Callback() {
//何时开始检测触摸事件
@Override
public boolean tryCaptureView(View child, int pointerId) {
//如果当前触摸的child是mMainView时开始检测
return mMainView == child;
}
//触摸到view后回调
@Override
public void onViewCaptured(View capturedChild, int activePointerId) {
super.onViewCaptured(capturedChild, activePointerId);
}
//当拖拽状态改变,比如idle, dragging
@Override
public void onViewDragStateChanged(int state) {
super.onViewDragStateChanged(state);
}
//当位置改变时调用,常用来与滑动时更改scale等
@Override
public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
super.onViewPositionChanged(changedView, left, top, dx, dy);
}
//处理垂直滑动
@Override
public int clampViewPositionVertical(View child, int top, int dy) {
return 0;
}
//处理水平滑动
@Override
public int clampViewPositionHorizontal(View child, int left, int dx) {
return left;
}
//拖动结束后回调
@Override
public void onViewReleased(View releasedChild, float xvel, float yvel) {
super.onViewReleased(releasedChild, xvel, yvel);
//手指抬起后缓慢移动到指定位置
if (mMainView.getLeft() < 500) {
//关闭菜单
//相当于Scroller的startScroll方法 mViewDragHelper.smoothSlideViewTo(mMainView, 0, 0);
ViewCompat.postInvalidateOnAnimation(DragViewGroup.this);
} else {
//打开菜单
mViewDragHelper.smoothSlideViewTo(mMainView, 300, 0);
ViewCompat.postInvalidateOnAnimation(DragViewGroup.this);
}
}
};
//重写computeScroll()方法,因为ViewDragHelper内部也是通过Scroller来实现平滑移动的
//通常情况下可以使用以下所示模板代码
@Override
public void computeScroll() {
if (mViewDragHelper.continueSettling(true)) {
ViewCompat.postInvalidateOnAnimation(this);
}
}
}