《Android1群英传》——个人读书笔记
5.1 滑动效果是如何产生的
- 要实现View的滑动,就必须监听的触摸事件,并根据事件传入的坐标,动态且不断的改变坐标,从而实现View跟随用户触摸的滑动而滑动
5.1.1 Android 坐标系
- 坐标系原点:屏幕最左上角的顶点
- 获取坐标系中点坐标: getLocationOnScreen(intlocation[])
- 获取X轴坐标: getRowX()
- 获取Y轴坐标: getRowY()
5.1.2 视图坐标系
- 原点:父视图左上角为坐标原点
- 它描述了子视图在父视图中的位置
- 两种坐标系相辅相成,并不冲突
- 视图坐标系的获取:getX()、getY()
5.1.3 触控事件——MotionEvent
- 1)MotionEvent中封装的一些常用的事件常量:
// 单点触摸按下动作
public static final int ACTION_DOWN = 0;
// 单点触摸离开动作
public static final int ACTION_UP = 1;
// 单点触摸移动动作
public static final int ACTION_MOVE = 2;
// 触摸动作取消
public static final int ACTION_CANCEL = 3;
// 触摸动作超出边界
public static final int ACTION_OUTSIDE = 4;
// 多点触摸按下动作
public static final int ACTION_POINTER_DOWN = 5;
// 多点触摸离开动作
public static final int ACTION_POINTER_UP = 6;
- 2)View提供的获取坐标的方法:
- getTop(): 获取到的是View 自身的顶边 到其 父布局顶边 的距离
-
getLeft(): <font color=#ED1A3D>左边————>左边</font>
- getRight(): 右边————>左边
-
getBottom(): <font color=#ED1A3D>底边————>顶边</font>
补充:
这边需要注意:这四个方法是相互对相应的,一组是到父控件左边的距离(getLeft()、getRight()),一组是到父控件顶部的距离(getTop()、getBottom()),每一组的对应点是相同的。
- 根据上图我们可获取View的大小:
- View的Height值 = view.getBottom() - view.getTop();
View的Width值 = view.getRight() - view.getLeft();
- 3)MotionEvent提供的方法:
- getX(): 获取点击事件距离控件左边的距离,即视图坐标
- getY(): 顶边
- getRawX(): 获取点击事件距离整个屏幕左边的距离,即绝对坐标
-
getRawY(): 顶边
5.2 实现滑动的其中方法
5.2.1 Layout方法
5.2.2 offsetLeftAndRight() 与 offsetTopAndBottom()
- 这两个方法相当于系统提供的一个对左右、上下移动的API的封装。当计算出偏移量之后,只需要使用如下代码就可以完成View的重新布局,效果与使用Layout方法一样。
//同时对left和right进行偏移
offsetLeftAndRight(offsetX);
//同时对top和bottom进行偏移
offsetTopAndBottom(offsetY)
5.2.3 LayoutParams
5.2.4 scrollTo 与 scrollBy
- 改变View的位置
- scrollTo(x,y) 表示移动到一个具体的坐标点(x,y)
- scrollBy(dx,dy) 表示移动的增量为dx,dy
- 获取偏移量后,使用 scrollBy 来移动View
int offsetX = x - lastX;
int offsetY = y = lastY;
scrollBy(offsetX,offsetY);
- 注意:scrollTo 与 scrollBy 移动的是View的content,即移动的是view的内容。当然如果是在ViewGroup中使用这两个方法,那么移动的将是所有的子View
int offsetX = x - lastX;
int offsetY = y = lastY;
((View)getParent()).scrollBy(offsetX, offsetY)
- 按照上述方法进行设置,视图不会跟随手指的移动而移动,而是反方向的移动。要想跟随手指移动需要将偏移量设置为负数
((View)getParent()).scrollBy(-offsetX, -offsetY)
5.2.5 Scroller类
- Scroller类可以实现平滑移动的效果,而非瞬间完成的移动
- 使用Scroll类的步骤:
- 1)初始化Scroller
//初始化Scroller
mScroller = new Scroller(context);
- 2)重写computeScroll()方法,实现模拟滑动
它是使用Scroller类的核心,系统会在绘制View的时候在draw()方法中调用这个方法。
@Override
public void commputeScroll()
{
super.commputeScroll();
//判断 Scroll 是否执行完毕
if(mScroller.commputeScrollOffset())
{
((View)getParent()).scrollTo(
mScroller.getCurrX(),
mScroller.getCurrY());
//通过重绘来不断调用 commputeScroll
invalidate();
}
}
- Scroller类提供了 commputeScrollOffset() 方法来判断是否完成了整个滑动,同时也提供了 getCurrX 、 getCurrY 来获得当前的滑动坐标。
- 注意: invalidate()方法只能在 computeScroll() 方法中获取模拟过程中的 scrollX 和 scrollY 坐标。但是 computeScroll() 是不会自动调用的,只能通过 invalidate()——>draw()——>computeScroll() 来间接调用 computeScroll() 方法,所以需要在模板代码中调用 invalidate() 方法,实现循环获取 scrollX 和 scrollY 的目的。而当模拟过程结束后,scroller.commputeScrollOffset() 方法会返回 false ,从而中断循环,完成整个平滑移动过程。
- 3)startScroll 开启模拟过程
- 调用 Scroller 类中的starScroll()方法来开启平滑移动过程
- 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)
5.2.6 属性动画
- 与第七章重复,这里不做介绍
5.2.7 ViewDragHelper
- Google在support库中提供了 DrawLayout 和 SlidingPaneLayout 用于实现侧边栏滑动效果
- 实现方式:
- 1)初始化ViewDragHelper
private void initView()
{
mViewDragHelper = ViewDragHelper.create(this, callback);
}
- 传入的两个参数:第一个是要监听的View,通常是ViewGroup,即ParentView;第二个是CallBack回调, 这个回调就是整个ViewDragHelper 的逻辑核心。
- 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()
- ViewDragHelper 的内部也是通过Scroller来实现滑动的。
@Override
public void computeScroll() {
if (mViewDragHelper.continueSettling(true)) {
ViewCompat.postInvalidateOnAnimation(this);
}
}
- IDE 自动重写的 tryCaptureView() ,可以指定在创建ViewDragHelper时,参数ParentView中的哪个子View可以被移动。
// 何时开始检测触摸事件
@Override
public boolean tryCaptureView(View child, int pointerId) {
//如果当前触摸的child是mMainView时开始检测
return mMainView == child;
}
- 以上代码就是指定了 MainView 是可以被移动的
- 具体实现滑动的方法 :
// 处理垂直滑动
@Override
public int clampViewPositionVertical(View child, int top, int dy) {
return top;
}
// 处理水平滑动
@Override
public int clampViewPositionHorizontal(View child, int left, int dx) {
return left;
}
- 重写 onViewReleased() 方法,可以非常简单的实现当是指离开屏幕后实现的操作,其内部也是通过 Scroller 类实现的
// 拖动结束后调用
@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);
}
}
- 系统定义了大量监听事件来帮助我们处理各种事情:
1)onViewCaptured(): 这个事件在用户触摸到View之后回调;
2)onViewDragStateChanged(): 这个事件在拖拽状态改变时回调
3)onViewPositionChanged(): 这个事件在位置改变时回调,常用于滑动时更改scale进行缩放等效果。
r
网友评论