Android——Scroll分析
一:滑动的产生原因
滑动一个View原理:就是通过不断的改变View的坐标。
实现View的滑动,必须监听用户触摸的事件,根据触摸传入的坐标,动态的改变View的坐标,从而实现滑动
Android的坐标系:Android中屏幕的最左上端的点就是原点,向右就是X坐标轴的正方向,向下就是Y坐标轴的正方向
系统提供的getLocationOnScreen(intlocation[])方法获取Android坐标系中位置 触控事件中的getRawX 和getRawY方法实 现同样效果
视图坐标系:相对于Android坐标系,视图坐标系主要描述子View在父 View中的位置,将父视图 左上角作为坐标原点。触控事件中getX getY可以获取View的视图坐标系
触控事件:
常用到的:单点触摸、单点触摸离开屏幕、触摸点移动
public static final int ACTION_DOWN = 0;public static final int ACTION_UP = 1;public static final int ACTION_MOVE = 2;
通常会在onTouchEvent(MotionEvent event)中通过event.getAction()方法获取触控事件的类型。
常用获取坐标的方法:
(1)View提供的获取坐标的方法
getTop:获取当前View自身的顶边到父布局顶边的距离
getLeft:获取当前View自身的左边到父布局左边的距离
getRight:获取当前View自身的右边到父布局右边的距离
getBottom:获取当前View自身的底边到父布局底边的距离
(2)触控事件MotionEvent中提供的方法
getX:视图坐标 点击事件到控件左边的距离
getY:视图坐标
getRawX:Android坐标 点击事件距离屏幕左端的距离
getRawY:Android坐标
二.实现滑动的七种方法
第一种:layout方法
采用getX,getY
// 视图坐标方式@Overridepublic 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);//同时对left和right进行偏移 // offsetLeftAndRight(offsetX); //同时对top还有bottom进行偏移 // offsetTopAndBottom(offsetY);break;}return true;}
采用getRawX getRawY
// 绝对坐标方式@Overridepublic 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;// 在当前left、top、right、bottom的基础上加上偏移量layout(getLeft() + offsetX,getTop() + offsetY,getRight() + offsetX,getBottom() + offsetY);// 重新设置初始坐标lastX = rawX;lastY = rawY; break;}return true;}}
执行完Action_Move后这里需要重新设置初始坐标,具体原因尚未搞懂、
第二种:offsetLeftAndRight() offsetTopAndBottom()方法 见第一段代码
第三种 LayoutParams
@Overridepublic 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;ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) getLayoutParams();//根据父布局类型的不同选择 这里父布局是LinearLayout// LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) getLayoutParams();layoutParams.leftMargin = getLeft() + offsetX;layoutParams.topMargin = getTop() + offsetY;setLayoutParams(layoutParams); break;}return true;}
LayoutParams保存一个View的布局参数,因此可以改变LayoutParams来实现View位置改变
使用此方法的前提是 此View必须有一个父布局
第四种 scrollTo scrollBy
scrollTo(x,y)移动到坐标是x,y的位置 scrollBy(x,y)表示移动的增量
这种方法需要注意两点:
第一点:这两种方法移动的是View的content,在ViewGroup中移动的是子View
第二点:将scrollBy中的参数设为正,那么content将向坐标轴负方向移动;同理,将scrollBy中的参数设为负,那么content将向坐标轴正方向移动
((View) getParent()).scrollBy(-offsetX,-offsetY);
第五种 Scroller
使用scrollTo和scrollBy方法,子View的平移是瞬时的,采用Scroller类可以实现平滑移动,而不是瞬时移动
( 1 )初始化 Scroller
( 2 )重写computeScroll方法,实现模拟滑动
(3)startScroll开启模拟过程
private void initView() { setBackgroundColor(Color.BLUE);mScroller=new Scroller(getContext());}@Overridepublic void computeScroll() {super.computeScroll();//computeScrollOffset方法判别是否完成整个滑动,未完成返回trueif(mScroller.computeScrollOffset()){//.getCurrX() getCurrY()获取当前的滑动坐标((View)getParent()).scrollTo( mScroller.getCurrX(),mScroller.getCurrY());//通过重绘(draw)不断调用computeScrolL完成滑动invalidate();}}@Overridepublic 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;((View) getParent()).scrollBy(-offsetX,-offsetY); break; case MotionEvent.ACTION_UP: View viewGroup= (View) getParent();mScroller.startScroll(viewGroup.getScrollX(),viewGroup.getScrollY(),-viewGroup.getScrollX(),-viewGroup.getScrollY());invalidate(); break;}return true;}
第六种 属性动画
第七种 ViewDragHelper
通过使用
ViewDragHelper 基本可以实现各种不同的滑动,拖放要求
(1)初始化ViewDragHelper
(2)拦截事件
(3)处理computeScroll
(4)处理回调CallBack
public class DragViewGroup extends FrameLayout {private ViewDragHelper mViewDragHelper; private View mMenuView; private View 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();}@Overrideprotected void onFinishInflate() {super.onFinishInflate();mMenuView=getChildAt(0);mMainView=getChildAt(1);}@Overrideprotected 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);}@Overridepublic boolean onTouchEvent(MotionEvent event) {mViewDragHelper.processTouchEvent(event); return true;}private void initView(){//初始化ViewDragHelper 第一个参数是要监听的View,通常是ViewGroup //第二个参数是一个CallaBack的回调 ,这个回调是整个ViewDragHelper的逻辑核心mViewDragHelper=ViewDragHelper.create(this,callback);}private ViewDragHelper.Callback callback=new ViewDragHelper.Callback() {@Override//通过此方法确定要移动的子Viewpublic boolean tryCaptureView(View child, int pointerId) {return mMainView==child;}@Override//下面两种方法分别对应垂直和水平移动 //其中top 就是垂直方向上移动的距离 left就是水平移动的距离public int clampViewPositionVertical(View child, int top, int dy) {return 0;}@Overridepublic 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){//关闭菜单mViewDragHelper.smoothSlideViewTo(mMainView,0,0);ViewCompat.postInvalidateOnAnimation(DragViewGroup.this);}else{mViewDragHelper.smoothSlideViewTo(mMainView,300,0);ViewCompat.postInvalidateOnAnimation(DragViewGroup.this);} } };@Overridepublic void computeScroll() {if(mViewDragHelper.continueSettling(true)){ ViewCompat.postInvalidateOnAnimation(this);} }}
更多相关文章
- 浅谈Java中Collections.sort对List排序的两种方法
- Python list sort方法的具体使用
- python list.sort()根据多个关键字排序的方法实现
- Android智能指针SP WP使用方法介绍
- Android地图应用新视界--mapbox的常用功能封装工具类
- android与javascript交互调用
- Android和js、H5进行交互数据(面试必问)
- 写给Android(安卓)App开发人员看的Android底层知识(1)
- 在android屏幕上 上 下 左 右 四个方向移动法拉利(Image)