美文网首页Android 知识程序员Android 进阶技术篇专题
Android 仿IOS拖拽回弹之进阶ReboundFrameL

Android 仿IOS拖拽回弹之进阶ReboundFrameL

作者: SwitchLife | 来源:发表于2018-03-07 18:20 被阅读111次

前言

  IOS拖拽回弹给用户的体验不得不赞。然后Android原生的API各种不支持,于是乎出现的很多仿IOS拖拽回弹的Android控件ReboundScrollView,比起原生的ScrollView,体验上好了很多,但是还是不尽人意:RebounScrollView里的内容超出屏幕时,你想一次性在显示完ScrollView里的内容紧跟着拽出屏幕,你发现你做不到,臣妾也做不到!于是乎,我想我是不是可以试试?下面分享一个让你也可以有IOS拖拽回弹般体验的ReboundFrameLayout。既然Android原生的ScrollView做不到,那我干脆舍弃不用,我们自己做类似ScrollView的滑动,这样方便我们在滑动到顶部(或底部)时可以继续向下(或向上滑动)。

这里链接两个大神写的拖拽回弹控件:
1、ReboundScrollView 仿IOS 拖拽回弹
2、 Android上实现仿IOS弹性ScrollView

我的思路

思路图

截屏效果

立即体验

扫描以下二维码下载体验App(从0.2.3版本开始,体验App内嵌版本更新检测功能):


JSCKit库传送门:https://github.com/JustinRoom/JSCKit

源码简析

利用Scroller方式实现滚动。
ReboundFrameLayout.java关键逻辑代码(请参阅代码里的详细注释):

private void executeScroll(View target, int mYOffset) {
        //为了方便下面的描述,我们用content代表target的内容,即:content=target的内容。
        //content下滑的高度
        int childScrollY = target.getScrollY();
        //content在target中的可见区域
        Rect rect = new Rect();
        target.getLocalVisibleRect(rect);
        //content的实际高度
        int realHeight = target.getMeasuredHeight();

        //content底部与target底部对齐时需要向上滑动的距离。
        // 如果target底部已向上经拉出屏幕外,则认为distanceFromBottom为0
        int distanceFromBottom = rect.bottom < 0 ? 0 : realHeight - rect.bottom;

        int scrollY = getScrollY();
        Log.i(TAG, "executeScroll: scrollY = " + scrollY);
        int childScrolledY = Math.abs(childScrollY);
        int scrolledY = Math.abs(scrollY);
        int distance = Math.abs(mYOffset);
        if (mYOffset < 0){//向下滑动
            //向下滑动target与ReboundFrameLayout底部对齐
            scrollBy(0, -Math.min(distance, scrolledY));
            distance = distance - scrolledY;
            if (distance <= 0){
                return;
            }

            //向下滑动content与target顶部对齐
            target.scrollBy(0, -Math.min(distance, childScrolledY));
            distance = distance - childScrolledY;
            if (distance <= 0){
                return;
            }

            //向下滑动target
            scrollBy(0, -distance);
        } else if (mYOffset > 0) {//向上滑动
            //向上滑动target与ReboundFrameLayout顶部对齐
            scrollBy(0, Math.min(distance, scrolledY));
            distance = distance - scrolledY;
            if (distance <= 0){
                return;
            }

            //向上滑动content与target底部对齐
            target.scrollBy(0, Math.min(distance, distanceFromBottom));
            distance = distance - distanceFromBottom;
            if (distance <= 0){
                return;
            }

            //向上滑动target
            scrollBy(0, distance);
        }
    }

我们来看看下面几处重要的地方:

int realHeight = target.getMeasuredHeight();

  • 关于获取target的内容高度问题。
    1、如果target是从layout布局文件中解释出来的,我们可以通过target.measure(0, 0)测量出来
    2、如果targetnew出来的,通过target.measure(0, 0)测量不出来的。
    为了考虑到上面两种情况,所以我们在protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)方法中预先测量target
  • 在测量之前,我们先看看ViewGroup的解析流程,根据测试结果,流程图如下:
    ViewGroup解析流程图.jpg
    根据上图,不管是从layout布局文件中解释出来的还是new出来的,最终都会走测量方法onMeasure(int widthMeasureSpec, int heightMeasureSpec),那么接下来我们就在这里测量好target就ok了
  • 测量
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        //先调用super方法固定本身的高度
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        //测量target的实际内容高度
        if (getChildCount() > 0) {
            View target = getChildAt(0);
            ViewGroup.LayoutParams params = target.getLayoutParams();
            int newHeightMeasureSpec = MeasureSpec.makeMeasureSpec(params.height, MeasureSpec.AT_MOST);
            target.measure(widthMeasureSpec, newHeightMeasureSpec);
        }
    }

公共方法

结尾

  不求打赏,点个赞加个关注吧!我是JustinEoy,QQ:1006368252

相关文章

网友评论

  • SwitchLife:抽了点时间对此控件做了大量的优化,用到的童鞋请及时更新!
  • SwitchLife:刚刚做了下优化,修复了滑动的bug。用到的童鞋们请及时更新。关注我,欢迎提问!:yum:

本文标题:Android 仿IOS拖拽回弹之进阶ReboundFrameL

本文链接:https://www.haomeiwen.com/subject/pwfsfftx.html