Android UI绘制 -- 滑动

基础

scrollBy()、scrollTo()的本质都是修改View中的mScrollY、mScrollX;而修改这两个参数的效果就是View在绘制的时候会将整个View的坐标进行平移。
详见 不再迷惑,也许之前你从未真正懂得 Scroller 及滑动机制

Scroller

Scroller 只是一个普通的类,它封装了滚动事件,可用于View的平滑滚动效果。但是,它只是提供滚动时的数据变化,它本身不控制对于 View 的滚动动画。如何制作的平滑的滚动效果,这个责任在于开发者自己,Scroller 能做的就是提供数值及时间在一个滚动动画周期中的值。所以它只是一个辅助类。
当我们利用scrollBy()、scrollTo()来使得view移动时因为是瞬移会有迟钝感,Scroller可以类似属性动画一样让滑动变得平滑,同时提供快速滑动(即ListView用力滑动时会自动滚动一段距离后停下,即有一个初速度)。

public class CustomView extends LinearLayout {  

    private static final String TAG = "Scroller";  

    private Scroller mScroller;  

    public CustomView(Context context, AttributeSet attrs) {  
        super(context, attrs);  
        //Step 1. 创建一个Scroller
        mScroller = new Scroller(context);  
    }  

    //调用此方法滚动到目标位置  
    public void smoothScrollTo(int fx, int fy) {  
        int dx = fx - mScroller.getFinalX();  
        int dy = fy - mScroller.getFinalY();  
        smoothScrollBy(dx, dy);  
    }  

    //调用此方法设置滚动的相对偏移  
    public void smoothScrollBy(int dx, int dy) {  

        //Step 2.设置要滑动的距离
        mScroller.startScroll(mScroller.getFinalX(), mScroller.getFinalY(), dx, dy);  
        invalidate();//这里必须调用invalidate()才能保证computeScroll()会被调用,否则不一定会刷新界面,看不到滚动效果  
    }  

    //Step 3.重写computeScroll,Scroller只是为我们计算了下一帧应该移动到哪里(这样才平滑,且可设置插值器),实际的移动需要我们在这里设置scrollTo
    @Override  
    public void computeScroll() {  

        //先判断mScroller滚动是否完成  
        if (mScroller.computeScrollOffset()) {  

            //这里调用View的scrollTo()完成实际的滚动  
            scrollTo(mScroller.getCurrX(), mScroller.getCurrY());  

            //必须调用该方法,否则不一定能看到滚动效果  
            postInvalidate();  
        }  
        super.computeScroll();  
    }  
}  

同时Scroller提供fling来支持快速滚动

//startX 开始滚动时 X 坐标
//startY 开始滚动时 Y 坐标
//velocityX 开始滚动时 X 方向的初始速度
//velocityY 开始滚动时 Y 方向的初始速度
//minX 滚动过程,X 坐标不能小于这个数值
//maxX 滚动过程,X 坐标不能大于这个值
//minY 滚动过程,Y 坐标不能小于这个数值
//maxY 滚动过程,Y 坐标不能大于这个数值
public void fling(int startX, int startY, int velocityX, int velocityY,
            int minX, int maxX, int minY, int maxY) {}

ViewDragHelper

适合实现View的拖拽效果

public class VDHLayout extends LinearLayout
{
    private ViewDragHelper mDragger;

    public VDHLayout(Context context, AttributeSet attrs)
    {
        super(context, attrs);
        //Step 1.创建一个ViewDragHelper
        mDragger = ViewDragHelper.create(this, 1.0f, new ViewDragHelper.Callback()
        {
            //如何返回ture则表示可以捕获该view,根据传入的第一个view参数决定哪些可以捕获
            @Override
            public boolean tryCaptureView(View child, int pointerId)
            {
                return true;
            }
            //可以在该方法中对child移动的边界进行控制,left为即将移动到的位置
            @Override
            public int clampViewPositionHorizontal(View child, int left, int dx)
            {
                return left;
            }
            //可以在该方法中对child移动的边界进行控制,top为即将移动到的位置
            @Override
            public int clampViewPositionVertical(View child, int top, int dy)
            {
                return top;
            }
            //手指释放回调,可以做回滚
            @Override
            public void onViewReleased(View releasedChild, float xvel, float yvel)
            {
                //手指释放时可以自动回去
                if (releasedChild == XXXView)
                {
                    mDragger.settleCapturedViewAt(初始位置X,初始位置Y);
                    invalidate();
                }
            }
            //在边界拖动时回调,
            @Override
            public void onEdgeDragStarted(int edgeFlags, int pointerId)
            {
                mDragger.captureChildView(XXXView, pointerId);
            }

        });
        //边界回调要生效需要设置边界
        mDragger.setEdgeTrackingEnabled(ViewDragHelper.EDGE_LEFT);
    }

   @Override
    public boolean onInterceptTouchEvent(MotionEvent event)
    {
        //Step 2.将onInterceptTouchEvent是否拦截交给ViewDragHelper
        return mDragger.shouldInterceptTouchEvent(event);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event)
    {
        //Step 3.将touch事件传递给ViewDragHelper
        mDragger.processTouchEvent(event);
        return true;
    }
}

这样VDHLayout里的子View就可以随意的拖拽了
Callback中未用到的方法

内部View可点击需重写下面两个方法

@Override
public int getViewHorizontalDragRange(View child)
{
     return getMeasuredWidth()-child.getMeasuredWidth();
}

@Override
public int getViewVerticalDragRange(View child)
{
     return getMeasuredHeight()-child.getMeasuredHeight();
}

onViewDragStateChanged

当ViewDragHelper状态发生变化时回调(IDLE,DRAGGING,SETTING[自动滚动时])

onViewPositionChanged

当captureview的位置发生改变时回调

onViewCaptured

当captureview被捕获时回调

GestureDetector

用来辅助处理触摸事件,可以理解为是onTouchEvent的增强,为我们识别了各种手势事件:单击、双击、滑动、快速滑动、长按等等…

GestureDetectorm_gestur eDetector = new GestureDetector(context, onGestureListener);
m_gestureDetector.setOnDoubleTapListener(onDoubleTapListener);

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        //将touch事件交给gesture处理
        m_gestureDetector.onTouchEvent(event);
        return super.onTouchEvent(event);
    }

GestureDetector包括的监听有
GesturetDetector.OnGestureListener 接口
GesttureDetector.OnDoubleTapListener 接口
GesttureDetector.SimpleOnGestureListener 类

ItemTouchHelper

ItemTouchHelper是一个强大的工具,它处理好了关于在RecyclerView上添加拖动排序与滑动删除的所有事情。它是RecyclerView.ItemDecoration的子类,也就是说它可以轻易的添加到几乎所有的LayoutManager和Adapter中。它还可以和现有的item动画一起工作,提供受类型限制的拖放动画等等
创建一个ItemTouchHelper.Callback实现下面几个方法

getMovementFlags

@Override
public int getMovementFlags(RecyclerView recyclerView, 
        RecyclerView.ViewHolder viewHolder) {
    int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
    int swipeFlags = ItemTouchHelper.START | ItemTouchHelper.END;
    return makeMovementFlags(dragFlags, swipeFlags);
}

ItemTouchHelper可以让你轻易得到一个事件的方向。你需要重写getMovementFlags()方法来指定可以支持的拖放和滑动的方向。使用helperItemTouchHelper.makeMovementFlags(int, int)来构造返回的flag。这里我们启用了上下左右两种方向。注:上下为拖动(drag),左右为滑动(swipe)。

isLongPressDragEnabled是否允许长按拖动 isItemViewSwipeEnabled是否允许滑动

接下来是拖动/滑动结束后的回调,需要在这里处理数据

onMove onSwiped

@Override
public boolean onMove(RecyclerView recyclerView, 
        RecyclerView.ViewHolder viewHolder, 
        RecyclerView.ViewHolder target) {
    return true;
}
@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, 
        int direction) {

}

搞定后记得notifyItemMoved(from, to);下 提醒控件更新。
ps:ItemTouchHelper原理:监听RecyclerView的ItemTouchListener,判断手指的滑动来为Item添加一个跟随手指移动的移动动画(补间动画),该动画会持续到手指松开;而当移动到它原位置的±1个位置的时候该位置的Item也发生一个单位位置的移动动画并调用onMove由我们主动设置两个Item的位置顺序(如果我们没有设置位置则只会有Item跟随手指移动的动画且释放后会回到原位)。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1、Android显示GIF动画 GifView GifView 是一个为了解决android中现在没有直接显示gif的view,只能通过mediaplay来显示这个问题的项目,其用法和 ImageView一样,支持gif图片 使用方法:1-把GifView.jar加入你的项目。2-在xml中配置GifView的基本属性,GifView继承自View类,和Button、ImageView一样是一个UI控件。 如: 3-在代码中配置常用属性: // 从xml中得到GifView的句柄 gf1 = (GifView) findViewById(R.id.gif1); // 设置Gif图片源 gf1.setGifImage(R.drawable.gif1); // 添加监听器 gf1.setOnClickListener(this); // 设置显示的大小,拉伸或者压缩 gf1.setShowDimension(300, 300); // 设置加载方式:先加载后显示、边加载边显示、只显示第一帧再显示 gf1.setGifImageType(GifImageType.COVER); GifView的Jar包共有四个类: GifAction.java 观察者类,监视GIF是否加载成功 GifFrame.java 里面三个成员:当前图片、延时、下张Frame的链接。 GifDecoder.java 解码线程类 GifView.java 主类,包括常用方法,如GifView构造方法、设置图片源、延迟、绘制等。 2、Calendar.v0.5.0 是 Android 平台的一个日历显示组件。 3、CWAC EndlessAdapter 是 Android 上一个可以无限往下滑进行列表数据加载的控件。 4、Android Horizontal ListView 是 Android 上一个水平滑动的 ListView 组件。 5、Android ViewBadger 视图布局。 6、滑动刷新的ListView Android PullToRefresh 为 Android 应用提供一个向下滑动即刷新列表的功能,就两个目标文件。 7、pakerfeldt-android-viewflow 是 Android 平台上一个视图切换的效果库。ViewFlow 相当于 Android UI 部件提供水平滚动的 ViewGroup,使用 Adapter 进行条目绑定。 8、Android 导航菜单 RibbonMenu 是 Android 上的一个导航菜单组件。就三个目标文件,菜单项直接在 XML 中定义,可添加文本和图标。 9、AndroidUI工具包 android-ui-utils 是一个工具包用来帮助设计和开发 Android 用户界面,包含三个单独的工具:Android Asset Studio用户界面原型模具,Android 设计预览,时常需要重复确认程序版面设计状况的 Android App 开发者,应该会爱上这个轻量级的 Java 程序:Andorid Design Preview 工具,通过 USB 连接之后,只要简单的在计算机中选取您想要显示的程序版面范围,就可将镜像结果直接显示于手机装置之上。 10、Androidui开发类库 GreenDroid 是一个Androidui开发类库,能够使你的Android开发更加简便和快捷。 11、Android滑动式菜单 SlidingMenu 是 Android 上实现类似 Facebook 和 Path 2.0 滑动式菜单的组件。 12、AsyncImageView 是 Android 上的一个异步从网络上获取图片并进行浏览的开源组件,可自动在本地进行缓存。该项目是 GreenDroid 的一部分。 13、仿Path按钮动画效果 PathButton 仿照Path应用首页左下角的Button动画效果写了个简单的Demo,由于数学不好,坐标总是和理想有出入,只是大致实现了动画效果,若果有人能把坐标算对,那么修改我的demo就能做成类似Path的那种动画效果!希望大家出点力帮着优化一下,并分享出来! 14、Android Intent开发包 OpenIntents Ope

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值