android进阶-Android Scroll分析
来源:互联网 发布:手机网络初始化失败 编辑:程序博客网 时间:2024/06/06 00:39
这里写链接内容1.1 Android坐标系
在android 中,将屏幕最左上角的顶点作为Android坐标系的原点,从这个点向右是X轴正方向,从这个点向下是Y轴正方向。
1.2 视图坐标系
描述子视图在父视图中的位置关系。
原点不是android坐标系中的屏幕最左上角,而是以父视图左上角为坐标原点
在触控事件中,通过getX()、getY()所获取戴尔坐标就是视图坐标系中的坐标。
1.3触控事件——MotionEvent
MotionEvent中封装的一些常用得病事件常量,它定义了触控事件的不同类型。
通常情况下我们会在onTouchEvent(MotionEvent event)方法中通过event.getAction()方法来获取触控事件的类型.
// 视图坐标方式 @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);// offsetLeftAndRight(offsetX);// offsetTopAndBottom(offsetY); break; } return true; }
获取坐标值的方法
2.实现滑动的七种方式
2.1 layout方法
// 绝对坐标方式 @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; // 在当前left、top、right、bottom的基础上加上偏移量 layout(getLeft() + offsetX, getTop() + offsetY, getRight() + offsetX, getBottom() + offsetY); // 重新设置初始坐标 lastX = rawX; lastY = rawY; break; } return true; }
2.2 offsetLeftAndRight()与offsetTopAndBottom()
这个方法相当于系统提供的一个对左右、上下移动的API的封装。
//同时对left和right进行偏移 offsetLeftAndRight(offsetX); //同时对top和bottom进行偏移 offsetTopAndBottom(offsetY);
2.3 LayoutParams
通过改变 LayoutParams来改变一个view的位置时,通常改变的是这个view的Margin属性,所以除了使用布局的LayoutParams之外,还可以使用ViewGroup.MarginLayoutParams,这个不需要考虑父布局的类型。
ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) getLayoutParams();// LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) getLayoutParams(); layoutParams.leftMargin = getLeft() + offsetX; layoutParams.topMargin = getTop() + offsetY; setLayoutParams(layoutParams);
2.4 crollTo与scrollBy
case MotionEvent.ACTION_MOVE: int offsetX = x - lastX; int offsetY = y - lastY; ((View) getParent()).scrollBy(-offsetX, -offsetY); break;
通过上面的分析可以发现,如果将scrollBy中的参数dx和dy设置为正数,那么content将向坐标轴负方向移动;如果将scrollBy中的参数dx和dy设置为负数,那么content将向坐标轴正方向移动。
2.5 Scroller
Scroller类与crollTo、crollBy方式十分相似。通过Scroller类可以实现平滑移动的效果,而不是瞬间完成动作。
使用Scroller的三个步骤
(1)初始化Scroller
通过构造方法创建一个Scroller对象
// 初始化Scroller
mScroller = new Scroller(context);
(2)重写computeScroll()方法,实现模拟滑动
// 判断Scroller是否执行完毕 if (mScroller.computeScrollOffset()) { ((View) getParent()).scrollTo( mScroller.getCurrX(), mScroller.getCurrY()); // 通过重绘来不断调用computeScroll invalidate(); }
(3)startScroll开启模拟过程
startScroll()方法具有两个重载方法
public void startScroll(int startX,int startY,int dx,int dy,int duration)
public void startScroll(int startX,int startY,int dx,int dy)
case MotionEvent.ACTION_UP: // 手指离开时,执行滑动过程 View viewGroup = ((View) getParent()); mScroller.startScroll( viewGroup.getScrollX(), viewGroup.getScrollY(), -viewGroup.getScrollX(), -viewGroup.getScrollY()); invalidate(); break;
2.6 ViewDragHelper
通过viewDragHelper,基本可以实现各种不同的滑动,拖动需求。
1 .初始化viewDragHelper
通常定义在一个ViewGroup的内部,并通过7️⃣静态工厂方法进行初始化。
private void initView() { mViewDragHelper = ViewDragHelper.create(this, callback); }
2.拦截事件
@Override public boolean onInterceptTouchEvent(MotionEvent ev) { return mViewDragHelper.shouldInterceptTouchEvent(ev); } @Override public boolean onTouchEvent(MotionEvent event) { //将触摸事件传递给ViewDragHelper,此操作必不可少 mViewDragHelper.processTouchEvent(event); return true; }
3.处理computeScroll()
@Override public void computeScroll() { if (mViewDragHelper.continueSettling(true)) { ViewCompat.postInvalidateOnAnimation(this); } }
4.处理回调Callback
private ViewDragHelper.Callback callback = new ViewDragHelper.Callback() { // 何时开始检测触摸事件 @Override public boolean tryCaptureView(View child, int pointerId) { //如果当前触摸的child是mMainView时开始检测 return mMainView == child; }
在实例中自定义了一个ViewGroup,里面定义了两个子View-MenuView和MainView,当指定如上代码时,则只有MainVierw是可以被拖动的。
下面看具体的滑动方法:
clampViewPositionVertical和clampViewPositionHorizontal
// 处理垂直滑动 @Override public int clampViewPositionVertical(View child, int top, int dy) { return 0; } // 处理水平滑动 @Override public int clampViewPositionHorizontal(View child, int left, int dx) { return left; }
当手指离开屏幕后,子View滑动回初始位置。通过onViewReleased()来实现
// 拖动结束后调用 @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); } }
在onSizeChange()方法中获取View的宽度,根据View宽度处理滑动后的效果。
@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(); }
完整实例:
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(); } @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) { //将触摸事件传递给ViewDragHelper,此操作必不可少 mViewDragHelper.processTouchEvent(event); return true; } private void initView() { mViewDragHelper = ViewDragHelper.create(this, callback); } 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); } } }; @Override public void computeScroll() { if (mViewDragHelper.continueSettling(true)) { ViewCompat.postInvalidateOnAnimation(this); } }}
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity"> <com.imooc.dragviewtest.DragViewGroup android:layout_width="match_parent" android:layout_height="match_parent" android:id="@+id/view"> <FrameLayout android:layout_width="match_parent" android:layout_height="match_parent" android:background="@android:color/holo_blue_light"> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Menu" /> </FrameLayout> <FrameLayout android:layout_width="match_parent" android:layout_height="match_parent" android:background="@android:color/holo_orange_dark"> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Main" /> </FrameLayout> </com.imooc.dragviewtest.DragViewGroup></RelativeLayout>
Demo地址:
下载地址
- android进阶-Android Scroll分析
- Android Scroll原理分析
- Android scroll 分析
- Android Scroll分析
- Android Scroll分析(一)
- Android Scroll分析
- Android Scroll分析
- Android Scroll分析
- Android Scroll 分析
- Android Scroll分析
- Chapter5-Android Scroll 分析
- android scroll分析
- Android Scroll 分析
- Android Scroll分析
- Android Scroll分析
- Android Scroll 滑动分析
- Android——Scroll分析
- Android Scroll分析(一)
- MyBatis动态sql_trim自定义字符串截取
- 机器学习中的超参数
- MOSFET管开关电路基本知识总结
- 注册系统管理员
- hadoop软件大全下载整理(更新中)
- android进阶-Android Scroll分析
- <c:out value="<b>没有进行转换<b>" escapeXml="false"><c:out>标签中的escapeXML属性
- 6.Python入门之序列
- 关于C++的翻书回顾
- 设置运动会相关模板
- BZOJ 1007 [HNOI2008]水平可见直线
- 运动员报名
- Excel在统计分析中的应用—第八章—假设检验-方差已知下总体均值之差的检验
- arduino pro mini NRF2401使用