Scroll滑动分析-《Android群英传》第五章
来源:互联网 发布:人际网络网上商城 编辑:程序博客网 时间:2024/05/17 09:16
本文选自《android群英传》第五章。主要是滑动方面知识,讲解了坐标系、MotionEvent剄七种滑动的实现方法。
- 1-滑动的产生
- 1-Android坐标系
- 2-视图坐标系
- 3-MotionEvent
- View提供的获取坐标的方法
- MotionEvent提供的方法
- 2-实现滑动
- 1-Layout实现滑动
- 2-offsetLeftAndRight和offsetTopAndBottom
- 3-LayoutParams
- 4-scrollToscrollBy
- 5-Scroller
- 6-属性动画
- 7-ViewDragHelper
- QQ侧滑菜单
- 1-滑动的产生
1-滑动的产生
滑动一个View本质就是移动View,通过改变View的坐标去实现这个效果。这里就需要监听用户触摸的事件。下面包含两个部分:Android窗口坐标系和屏幕触控事件MotionEvent
1-Android坐标系
以屏幕左上角为原点,向右为X正方向,向下为Y轴正方向。
* Android中通过getLocationOnScreen(intlocation[])
能获得当前视图的左上角在Andriod坐标系中的坐标。
* 触控事件中,getRawX()和getRawY(),获得的同样是android坐标系中的坐标
2-视图坐标系
是当前视图以父视图左上角为原点建立的坐标系,用于描述当前视图在父视图中的坐标。
* 触控事件中通过getX()、getY()
获得在视图坐标系中的坐标。
3-MotionEvent
一共有几种常用事件常量:ACTION_DOWN单点按下\UP单点离开\MOVE单点移动\CANCEL动作取消\OUTSIDE动作超出边界\POINTER_DOWN多点触摸按下\POINTER_UP多点触摸离开
一般情况通过onTouchEvent中的event.getAction()来获取事件类型的常量。
系统有很多获取坐标值和相对距离的方法。主要分为两类。
View提供的获取坐标的方法
getTop:View的顶部到父控件顶边的距离。
getLeft/Right/Bottom:对应View的左边/右边/底部分别到父控件左边/右边/底部的距离。
MotionEvent提供的方法
getX()获得点击事件event距离控件左边的距离。视图坐标。
getY()获得点击事件event距离控件顶部的距离。视图坐标。
getRawX()/RawY()获得点击事件距离整个屏幕左边/顶部的距离。绝对坐标。
2-实现滑动
1-Layout实现滑动
View控件有layout决定View的位置。在View控件的onTouchEvent()方法中去获得控件滑动前后的偏移。通过layout方法去重新设置。
自定义View
public class ScrollByLayoutView extends AppCompatImageView{ float downX; float downY; /** * 三个构造函数千万不能少 */ public ScrollByLayoutView(Context context) { super(context); } public ScrollByLayoutView(Context context, AttributeSet attrs) { super(context, attrs); } public ScrollByLayoutView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } /** * 进行偏移计算,之后调用layout */ public boolean onTouchEvent(MotionEvent event) { float curX = event.getX(); //手指实时位置的X float curY = event.getY(); //Y switch(event.getAction()){ case MotionEvent.ACTION_DOWN: downX = curX; //按下时的坐标 downY = curY; Log.i("ScrollByLayoutView", "onTouchEvent: ACTION_DOWN"); break; case MotionEvent.ACTION_MOVE: int offsetX = (int)(curX - downX); //X偏移 int offsetY = (int)(curY - downY); //Y偏移 //用getLeft得到当前控件距离父控件左边的距离+偏移量,得到变化后的距离,然后调用layout layout(getLeft() + offsetX, getTop() + offsetY, getRight() + offsetX, getBottom() + offsetY); Log.i("ScrollByLayoutView", "onTouchEvent: ACTION_MOVE"); break; } return true; }}
使用控件:
<com.example.xxx.ScrollByLayoutView android:id="@+id/scroll_way1_imageview" android:layout_width="100dp" android:layout_height="100dp" android:src="@drawable/jide"/>
这样就实现了滑动,对于offset偏移值的计算,也可以使用event的getRawX/Y来计算。
* 特别注意:使用绝对坐标需要在每次调用layout之后重新设置初始值(downX = curX)
2-offsetLeftAndRight和offsetTopAndBottom
直接替换layout
//对left和right, top和bottom同时偏移offsetLeftAndRight(offsetX);offsetTopAndBottom(offsetY);
3-LayoutParams
利用布局的参数来移动View控件。
//方法三:通过布局设置在父控件的位置。但是必须要有父控件, 而且要指定父布局的类型,不好的方法。RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) getLayoutParams();layoutParams.leftMargin = getLeft() + offsetX;layoutParams.topMargin = getTop() + offsetY;setLayoutParams(layoutParams);
//方法四:用ViewGroup的MarginLayoutParams的方法去设置marign// 相比于上面方法, 就不需要知道父布局的类型。// 缺点:滑动到右侧控件会缩小ViewGroup.MarginLayoutParams mlayoutParams = (ViewGroup.MarginLayoutParams) getLayoutParams();mlayoutParams.leftMargin = getLeft() + offsetX;mlayoutParams.topMargin = getTop() + offsetY;setLayoutParams(mlayoutParams);
4-scrollTo\scrollBy
scrollTo去移动到制定坐标
scrollBy表示移动的增量
* 这两个方法是移动View的内容,因此需要在View的父控件中调用。
//方法五:scrollTo/scrollBy,在父控件中调用来操作父控件内部的控件 ((View)getParent()).scrollBy(offsetX, offsetY);
然而结果是错误的,因为滑动的参考物并不是之前的。
这里的移动类似于移动了玻璃,所以View控件移向了完全相反的地方。需要取反。
((View)getParent()).scrollBy(-offsetX, -offsetY);
5-Scroller
和scrollTo/By比较类似,但是去别在于scrollTo/By的位移是瞬间完成的。而Scroller却是平滑移动的。减少了突兀感。
这里实现一个效果,就是滑动后,控件自动返回最初始位置,这里在ACTION_UP中实现。
三个步骤:
1.初始化scroller
自定义View构造中初始化。
Scroller mScroller;public ScrollByLayoutView(Context context) { super(context); mScroller = new Scroller(context);}
2.重载自定义View的computeScroll
public void computeScroll() { super.computeScroll(); //判断scroller是否执行完毕。 if(mScroller.computeScrollOffset()){ ((View)getParent()).scrollTo(mScroller.getCurrX(), mScroller.getCurrY()); //通过重绘来不断调用 computeScroll invalidate(); }}
判断是否执行完毕
3.ACTION_UP中将View滑动回初始位置
case MotionEvent.ACTION_UP: View viewGroup = (View) getParent(); mScroller.startScroll(viewGroup.getScrollX(), viewGroup.getScrollY(), -viewGroup.getScrollX(), -viewGroup.getScrollY()); invalidate(); break;
6-属性动画
参考动画章节
7-ViewDragHelper
Google在其support库中为我们提供了一个DrawerLayout和SlidingPaneLayout两个布局来帮助开发者实现侧滑效果,这两个布局,大大的方便了我们自己创建自己的滑动布局,然而,这两个强大的布局背后,却隐藏着一个鲜为人知,却功能强大的类——ViewDragHelper,通过ViewDragHelper,基本可以实现各种不同的侧滑,拖放需求,因此这个方法也是各种滑动解决方案的终极绝招。
QQ侧滑菜单
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(); } /**------------------------------------------- * 1、初始化数据:调用ViewDragHelper.create方法 * ------------------------------------------*/ private void initView() { mViewDragHelper = ViewDragHelper.create(this,callback); //需要监听的View和回调callback } /**------------------------------- * 2、事件拦截和触摸事件全部交给ViewDragHelper进行处理 * ------------------------------*/ //事件拦截 @Override public boolean onInterceptTouchEvent(MotionEvent ev) { return mViewDragHelper.shouldInterceptTouchEvent(ev); } //触摸事件 @Override public boolean onTouchEvent(MotionEvent event) { //将触摸事件传递给ViewDragHelper mViewDragHelper.processTouchEvent(event); return true; } /**-------------------------------------------- * 3、也需要重写computeScroll() * 内部也是通过scroller来进行平移滑动, 这个模板可以照搬 * -------------------------------------------*/ @Override public void computeScroll() { if(mViewDragHelper.continueSettling(true)){ ViewCompat.postInvalidateOnAnimation(this); } } /**------------------------------ * 4、处理的回调:侧滑回调 * ----------------------------*/ private ViewDragHelper.Callback callback = new ViewDragHelper.Callback() { /*------------------------------- * 何时开始触摸: * 1.指定哪一个子View可以被移动. * 2.如果直接返回true,在该布局之内的所有子View都可以随意划动 * ------------------------------*/ @Override public boolean tryCaptureView(View child, int pointerId) { //如果当前触摸的child是mMainView开始检测 return mMainView == child; } /*------------------------------- * 处理水平滑动: * 1. 返回值默认为0,如果为0则不处理该方向的滑动。 * 2. 一般直接返回left,当需要精准计算pading等值时,可以先对left处理再返回 * ------------------------------*/ @Override public int clampViewPositionHorizontal(View child, int left, int dx) { return left; } /*------------------------------- * 处理垂直滑动: * 1. 返回值默认为0,如果为0则不处理该方向的滑动。 * 2. 一般直接返回top,,当需要精准计算pading等值时,可以先对left处理再返回 * ------------------------------*/ @Override public int clampViewPositionVertical(View child, int top, int dy) { return 0; } /*--------------------------------------------- * 拖动结束后调用,类似ACTION_UP。 * 这里是实现侧滑菜单,一般滑动可以不用这段代码 * ---------------------------------------------*/ @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); } } }; /**--------------------------------------------------- * 5、获取子控件用于处理 * 1. 上面完成了滑动功能,这里简单的按照第1、2的顺序指定子控件View的内容 * 2. onSizeChanged能够获得menu等子控件的宽度等信息,有需求可以后续处理 * ----------------------------------------------*/ //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(); }}
使用(作为父控件,里面依次放menu和main):
<com.example.xxxx.DragViewGroup android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/colorAccent"/> <TextView android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/colorPrimary"/> </com.example.xxxx.DragViewGroup>
- Scroll滑动分析-《Android群英传》第五章
- Android群英传第五章Scroll分析读书笔记
- 《Android群英传》读书笔记(4)第五章:Android Scroll分析
- Android群英传笔记——第五章:Android Scroll分析
- Android群英传第五章笔记·Android Scroll分析
- Android群英传笔记——第五章:Android Scroll分析
- 第五章Android Scroll分析(Android群英传)
- Android群英传学习——第五章、Android Scroll分析
- Android群英传知识点回顾——第五章:Android Scroll分析
- Android群英传之Android Scroll分析
- 《Android群英传》读书笔记5.Android Scroll分析
- Andriod群英传-Android Scroll 分析学习笔记
- Android Scroll 滑动分析
- 读Android群英传第五章
- Android群英传学习笔记——Android Scroll 分析
- Android群英传之Android 滑动分析
- Android群英转读书笔记第五章(Android Scroll分析)
- android 群英传笔记----Android scroll
- MongoDB的安装
- SSM(SpringMVC+Spring+Mybatis)框架搭建
- 选择框付默认值
- node.js事件循环,事件驱动程序。
- boost::async_read_some连续接收数据
- Scroll滑动分析-《Android群英传》第五章
- Computer Vision阅读文章总结纪要
- xiunobbs回帖时间排序修改
- 将文件夹下的多个文件的内容合并到一个文件中
- GCC 关于禁用编译Kernel 是warning as a error
- 广告轮播
- 实验
- hdu 1231 最大连续子序列 ,1003 Max Sum;
- 51Nod 1062 序列中最大的数 打表