【Android】这效果,我没法描述

作者: 带心情去旅行 | 来源:发表于2017-08-05 22:19 被阅读3015次

    前言

    最近接到一个需求,这需求让我表示很尴尬。(下面是一些废话)
    要求的效果是这样的,顶部有部分悬浮,接着是一些布局,在下面是几个可切换的Tab页面,然后滚动的时候~~吧啦吧啦吧啦吧啦~~ 还是直接看图吧

    效果
    主要就是顶部和Tab的悬浮,还有就是被顶掉的那个效果。

    听到要实现这样的效果,我抽屉那把砍产品专用菜刀已经蠢蠢欲动了。

    思路

    先说说实现的思路吧,上面的效果大致可以分成两个部分:

    • 1、Tab向上滚动到顶部时悬浮
      Tab滚动后悬浮在顶部嘛~~ 这效果使用CoordinatorLayout + AppBarLayout就能轻松实现。(什么?你还不懂这两个控件怎么使用?额,应该可以勉强看懂后面的内容)
    • 2、顶部悬浮以及“被顶走”的效果
      只要在CoordinatorLayout外面套一层FrameLayout,然后把这个顶部的布局改在上面。接着监听AppBarLayout的滚动,利用topMargin实现被“顶上去”的效果

    拆分完毕,接下来就是实现了

    实现

    • Tab的悬浮效果
      利用CoordinatorLayoutAppBarLayoutTabLayoutViewPager来实现Tab的悬浮效果
    <?xml version="1.0" encoding="utf-8"?>
    <android.support.design.widget.CoordinatorLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="#FFF">
    
        <android.support.design.widget.AppBarLayout
            android:id="@+id/app_bar"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="#FFF">
    
            <LinearLayout
                android:id="@+id/ll"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="vertical"
                app:layout_scrollFlags="scroll|exitUntilCollapsed">
    
                <ImageView
                    android:id="@+id/header"
                    android:layout_width="match_parent"
                    android:layout_height="260dp"
                    android:scaleType="centerCrop"
                    android:src="@drawable/bg_header"/>
            </LinearLayout>
    
            <android.support.design.widget.TabLayout
                android:id="@+id/table_layout"
                android:layout_width="match_parent"
                android:layout_height="50dp"
                android:background="#FBDD9C"
                app:tabGravity="fill"
                app:tabIndicatorColor="#5f00"
                app:tabIndicatorHeight="4dp"
                app:tabMode="fixed"
                app:tabSelectedTextColor="#FFFFFF"
                app:tabTextColor="#FFFFFF"/>
        </android.support.design.widget.AppBarLayout>
    
        <android.support.v4.view.ViewPager
            android:id="@+id/vp"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:layout_behavior="@string/appbar_scrolling_view_behavior"/>
    </android.support.design.widget.CoordinatorLayout>
    
    • LinearLayout中设置app:layout_scrollFlags="scroll|exitUntilCollapsed"TabLayout 不设置app:layout_scrollFlags属性
    • ViewPager中使用app:layout_behavior="@string/appbar_scrolling_view_behavior"

    layout_scrollFlags:AppBarLayout供Children View使用的属性,一共有五种值:scroll,enterAlways,enterAlwaysCollapsed,snap,exitUntilCollapsed。具体的使用可以参考Android 详细分析AppBarLayout的五种ScrollFlags
    (CoordinatorLayout、AppBarLayout的详细用法我就不多说了)

    然后,只要在Java代码中为ViewPager添加几个列表Fragment就能看到以下的效果(注意:列表不可以是ListView,需要用RecyclerView

    效果

    到目前为止,效果已经实现了大半。最后值需要实现“被顶走”的效果就好了。

    • 顶部“被顶走”的效果
      这时候,布局稍微改变下。
    <?xml version="1.0" encoding="utf-8"?>
    <FrameLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        xmlns:app="http://schemas.android.com/apk/res-auto">
    
        <android.support.design.widget.CoordinatorLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:background="#FFF">
    
            <android.support.design.widget.AppBarLayout
                android:id="@+id/app_bar"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:background="#FFF">
    
                <LinearLayout
                    android:id="@+id/ll"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:orientation="vertical"
                    app:layout_scrollFlags="scroll|exitUntilCollapsed">
    
                    <View
                        android:layout_width="match_parent"
                        android:layout_height="50dp"/>
    
                    <ImageView
                        android:id="@+id/header"
                        android:layout_width="match_parent"
                        android:layout_height="260dp"
                        android:scaleType="centerCrop"
                        android:src="@drawable/bg_header"/>
                </LinearLayout>
    
                <android.support.design.widget.TabLayout
                    android:id="@+id/table_layout"
                    android:layout_width="match_parent"
                    android:layout_height="50dp"
                    android:background="#FBDD9C"
                    app:tabGravity="fill"
                    app:tabIndicatorColor="#5f00"
                    app:tabIndicatorHeight="4dp"
                    app:tabMode="fixed"
                    app:tabSelectedTextColor="#FFFFFF"
                    app:tabTextColor="#FFFFFF"/>
            </android.support.design.widget.AppBarLayout>
    
            <android.support.v4.view.ViewPager
                android:id="@+id/vp"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                app:layout_behavior="@string/appbar_scrolling_view_behavior"/>
        </android.support.design.widget.CoordinatorLayout>
    
        <TextView
            android:id="@+id/sticky_view"
            android:layout_width="match_parent"
            android:layout_height="50dp"
            android:textColor="#FFFFFF"
            android:text="除了悬浮 我还会动"
            android:gravity="center"
            android:textSize="16sp"
            android:background="#39b9e9"/>
    </FrameLayout>
    

    在原有的基础上,套了个FrameLayout,顶部的悬浮部分可以通过FrameLayout来实现。这样也导致下面的布局被盖住了一部分,因此在LinearLayout中加了与悬浮部分相同高度的空View

    布局是完成了,那个“被顶走”的效果怎么实现呢?这时候只要在MainActivity中对AppBarLayout的滚动进行监听即可。

        @BindView(R.id.app_bar)
        AppBarLayout mAppBar;
        @BindView(R.id.sticky_view)
        View mStickyView;
        @BindView(R.id.header)
        View mHeader;
    
        private void setAppBarListener() {
            mAppBar.addOnOffsetChangedListener(new AppBarLayout.OnOffsetChangedListener() {
                @Override
                public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) {
                    //头部高度(除去被顶部覆盖的部分)
                    int minScrollHeight = mHeader.getMeasuredHeight();
                    int margin = minScrollHeight + verticalOffset;
                    margin = margin > 0 ? 0 : margin;
                    FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) mStickyView.getLayoutParams();
                    layoutParams.topMargin = margin;
                    mStickyView.setLayoutParams(layoutParams);
                }
            });
    }
    

    这里通过AppBarLayout滚动的进行监听(向上滚动时,verticalOffset值的变化为:0、-1 、-2 ... -n-1、-n)来计算margin值。通过改变topMargin,实现“被顶走”的效果。
    再看一眼效果:

    效果

    敲到这里,我才默默的收起了那把砍产品专用菜刀。

    Tips

    • 问题:使用CoordinatorLayout时,滚动不流畅问题
      解决方案:可以写个Behavior添加到AppBarLayout中。具体的解决方案
    • 问题:如果你在想要刷新功能,在CoordinatorLayout外面套了一个SwipeRefreshLayout,一不小心就触发了刷新~~(自己体会)
      解决方案:这个问题可以通过对AppBarLayout的监听,设置swipeLayout的Enabled来处理
    mAblAppBar.addOnOffsetChangedListener((appBarLayout, verticalOffset) -> {
       if (verticalOffset == 0) {
          swipeLayout.setEnabled(true);
       } else {
           if (!swipeLayout.isRefreshing()) {
              swipeLayout.setEnabled(false);
           }
       }
    });
    

    源码地址

    Github

    以上有错误之处,感谢指出

    相关文章

      网友评论

      • 余金山_02d8:啊哈哈哈哈,太好了。还有源码呢
      • 萌萌的白天:NestedScrolling .子类和父类 的接口实现就行了 可以抽离出来做成自定义组件
      • 敲代码的小新:这个效果官方就有demo啊 老哥
      • d499c115489d:tablayout滑倒顶部后,快速下滑的话,顶部的布局会一下子被拉出来,有没有什么办法可以让vp中rv的数据回到第一条数据的时候,再让顶部的布局满满随着下滑出现
      • 爱下雪的孩子:大佬:joy: 学习了
      • Android小人:为什么要使用RecyclerView呢?是因为它实现了NestedScrollingChild接口如果说直接在activity的布局里面放RecyclerView的话一点问题没有,但是如果像这样嵌套的话,CoordinatorLayout是怎么得到RecyclerView滑动的动作呢?因为CoordinatorLayout的子view是ViewPager而不是RecyclerView
        带心情去旅行:这个嘛,我也不清楚。
        Android小人: @Android小人 @带心情去旅行
      • hui朝晖:佩服你这种遇到需求还能思考的,我就是思考了也想不到具体实现😂
      • 笨鸟不乖在简书:官方demo就有:sweat:
      • 奈文摩尔定律:这都可以上首页
      • 5ef947bf2bd1:大概是去年初的时候被要求实现这种效果,多次尝试无果,最后怼产改了需求
        5ef947bf2bd1: @E_Sun伊森 顶部抽屉还多一个要求,阻尼效果,拉不到一半弹回去,超过一半就打开
      • 明明_白_白:这算什么?MD设计的demo就有。
        明明_白_白:@带心情去旅行 能不能自力更生啊?我说的事demo啊,官方的demo,这你斗找不到还学什么编程?
        一夜胀木:@带心情去旅行 https://github.com/yanzhenjie/SwipeRecyclerView这个和效果很像,提供给楼主参考。
        带心情去旅行:如果有,请你附上链接,方便大伙学习。而不是一副不屑的样子,屁都没留下。
        如果你还能详细到具体的Activity,那就非常感谢了。
      • 紫豪:又一个轮子~
      • 涛涛吃肉肉:可以 谢谢 学习了
      • 砺雪凝霜:我们APP中就有这个效果
      • null_null_:说没技术含量的傻逼吧。
        下位子:没错
      • H_Cynic:敲oc代码的表示那三个你们眼里的Tab切换比顶部那效果还难:persevere:
        请输入账号名:swift也没压力
        H_Cynic:@hehtao 低调点。。我怕直接评论很简单会被骂:unamused:
        hehtao:@H_Cynic 表示OC无压力
      • 下位子:我上个星期跟接了同样的需求....很是尴尬‘
      • 4fc0238b5269:新的V7包里 有控件支持
      • WUREN_刘徵:666+999这个效果能死N多脑细胞,幸好我只是一个设计
      • 撸代码的周先生:大牛.最近也在做这个,炒鸡麻烦!!
      • 61218f30e53d:产品说:我就知道你行,嘻嘻!
      • 其实你懂De:说没有技术含量的你自己可以去试试.....
      • 爱言语论:what? 看见效果图我已经不想怎么实现,看见我旁边的刀子没????
      • 4f39f425d487:没啥技术含量
      • 楷桐:棒棒的
      • 工藤一号:666很棒

      本文标题:【Android】这效果,我没法描述

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