CoordinatorLayout+ViewPager+Behavior仿喜马拉雅FM首页

效果

5d0e4d8cbdf3221132.gif

实现方案

  • ViewPager
  • CoordinatorLayout
  • Behavior

Behavior设置在哪里?

  • 设置在 你要根据这个View的位置变化而做处理的View上,比如A发生变化,能触发B的变化,所以你要设置在A上,然后在自定义的Behavior处理,这样能通过A的变化来改变其他View。(个人比较喜欢这种方式处理)
  • 设置在B上,然后在自定义的Behavior通过layoutDependsOn来指定B要根据哪些View来的变化来变化,这样可以指定多个变化来改变B。 这个也是官方的,在layoutDependsOn的方法入参child就是B,而dependency是parent下的其他View,总之,你依赖的那个dependency发生了变化才会触发Behavior的方法回调。

ViewPager样式修改

ViewPager切换Item间隔处理

ViewPageronPageScroll监听

@Override
public void onPageScroll(int enterPosition, int leavePosition, float percent) {
                try {
                    String picUrl = mBanners.get(enterPosition).getBackground();
                    if (noEverLoad(picUrl)) {
                        vArcTarget.setTag(R.id.banner_tag, picUrl);
                        MMCImageLoader.getInstance().loadUrlImage(getActivity(),
                                picUrl, vArcTarget, R.color.transparent);
                    }
                    vArcTarget.setAlpha(percent);
                    //当超过0.96的时候设置当前的背景图,
                    // 如果在onPageSelect或者state等于SCROLL_STATE_IDLE设置太晚了
                    if (percent >= 0.96F) {
                        MMCImageLoader.getInstance().loadUrlImage(getActivity(),
                                picUrl, vArcCurrent, R.drawable.holder);
                    }
                } catch (Exception e) {
                    //下标异常,空指针异常
                }
            }

渐变效果处理

一张图的渐变方案(自定义View绘制)例如

public class ArcView extends android.support.v7.widget.AppCompatImageView {
    /**
     * rgb渐变
     *  argbEvaluator.evaluate(int )
     */
    ArgbEvaluator argbEvaluator = new ArgbEvaluator();
    Paint mPaint = new Paint();
    RectF vRectF = new RectF();

    {
        mPaint.setColor(ContextCompat.getColor(getContext(), R.color.base_color_primary));
        mPaint.setStyle(Paint.Style.FILL);
        mPaint.setAntiAlias(true);
    }
    public ArcView(Context context) {
        this(context, null, 0);
    }

    public ArcView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public ArcView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

 @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        float v = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 200, getContext().getResources().getDisplayMetrics());
        vRectF.set(-v, -getHeight(), getWidth() + v, getHeight());
        canvas.drawArc(vRectF, 0F, 180, true, mPaint);
    }
    
    public void setPaintColor(int color) {
        mPaint.setColor(color);
        invalidate();
    }
}

两张图片的渐变方案,适合图片,预缓存效果更好

vArcCurrent在下方,vArcTarget在上方,通过加载上面那张之后,根据滑动进度修改透明度,当超过96%透明度的时候将底部的vArcCurrent加载了,即达到掩饰效果,但是首次加载效果依赖网速,本地有缓存之后就完美了。如果想要完美效果需要将图片先预加载Bitmap。这里预加载可以单独判断或者先缓存。

三张图片的方案,提前设置好前后两张图片

在两张图的基础上多一些代码。。。

CoordinatorLayout,Behavior流程,源码简单分析

通过获取CoordinatorLayout的子View的LayoutParms反射解析并创建Behavior实例
在这里插入图片描述在这里插入图片描述
在这里插入图片描述
然后将CoordinatorLayout.Behavior的位置变化关联给Behavior,这样CoordinatorLayout的一些变化就调用Behavior相应的方法,然后你去Behavior处理方法就行了~
具体可以查看CoordinatorLayout下NestedScrollingParent2接口方法的相关实现
https://developer.android.com/reference/android/support/v4/view/NestedScrollingParent2

它把dependency加在节点Node,而child加在了Edge上

而这个方法来收集所有的child到mDependencySortedChildren这个List,mChildDag是DirectedAcyclicGraph的实例,这个东西叫做“有向无环图”,即DAG,这里用到的就是树

在这里插入图片描述

如果没有解析到behavior就设置一个默认的behavior

通过两个for循环比较 onChildViewsChanged这个方法会回调

在这里插入图片描述

选取两张图片方案的实操过程


  • 继承 CoordinatorLayout.Behavior编写 Behavior的全路径名,例如com.xxx.xxx.base.lib.view.banner.BannerBehavior或者用".类名"来指定
  • 编写Behavior有两种思路
    1 在CoordinatorLayout层级下设置在你想要改变的那个View上,然后通过重写Behavior的layoutDependsOn方法找到CoordinatorLayout节点下的某个你需要跟随变化的View A,然后你可以在onDependentViewChanged来根据dependency的变化来改变child;(如果发现无效果,请看方法2)
    2 滚动类型的,比如我们的滚动改变透明度,可以两种方式
    • child与dependency有间距,移动后位于denpency边界处,例如上方,下方,那么我们可以用默认的@string/appbar_scrolling_view_behavior来处理
    • 相对于紧贴着控件,比如child上方贴着dependency下方的控件,继续往上滑动之后,并不会回调onDependentViewChanged方法
      如果你第一次使用,建议默认创建ScrollingActivity看看代码和效果
      布局会有
<?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"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:fitsSystemWindows="true"
        tools:context=".ScrollingActivity">

    <android.support.design.widget.AppBarLayout
            android:id="@+id/app_bar"
            android:fitsSystemWindows="true"
            android:layout_height="@dimen/app_bar_height"
            android:layout_width="match_parent"
            android:theme="@style/AppTheme.AppBarOverlay">

        <android.support.design.widget.CollapsingToolbarLayout
                android:id="@+id/toolbar_layout"
                android:fitsSystemWindows="true"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                app:toolbarId="@+id/toolbar"
                app:layout_scrollFlags="scroll|exitUntilCollapsed"
                app:contentScrim="?attr/colorPrimary">

            <android.support.v7.widget.Toolbar
                    android:id="@+id/toolbar"
                    android:layout_height="?attr/actionBarSize"
                    android:layout_width="match_parent"
                    app:layout_collapseMode="pin"
                    app:popupTheme="@style/AppTheme.PopupOverlay"/>

        </android.support.design.widget.CollapsingToolbarLayout>
    </android.support.design.widget.AppBarLayout>


    <android.support.v4.widget.NestedScrollView
            app:layout_behavior="@string/appbar_scrolling_view_behavior"
            android:layout_width="match_parent"
            android:layout_height="match_parent">

        <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_margin="@dimen/text_margin"
                android:text="@string/large_text"/>

    </android.support.v4.widget.NestedScrollView>

</android.support.design.widget.CoordinatorLayout>

而自带的behavior有这几个
在这里插入图片描述

定义Behavior

public class BannerBehavior extends CoordinatorLayout.Behavior<View> {
    private int value;
    public BannerBehavior() {}
    public BannerBehavior(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
    @Override
    public boolean onStartNestedScroll(
    @NonNull CoordinatorLayout coordinatorLayout,
    @NonNull View child,
     @NonNull View directTargetChild,
     @NonNull View target, int axes, int type) {
     //竖直滑动的才拦截
        return axes == ViewCompat.SCROLL_AXIS_VERTICAL;
    }

    @Override
    public void onNestedScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull View child,
                               @NonNull View target, int dxConsumed, int dyConsumed,
                               int dxUnconsumed, int dyUnconsumed, int type) {
        super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, type);
        value += dyConsumed;
        //这里用getRootView()是因为topbar_title_layout和top_bar_bg不在当前的layout上。而为了方便找arc_bg我也这样获取了,你也可以设置成dependencyView
        View titleBar = coordinatorLayout.getRootView().findViewById(R.id.topbar_title_layout);
        View topBarBg = coordinatorLayout.getRootView().findViewById(R.id.top_bar_bg);

        View arcBg = coordinatorLayout.getRootView().findViewById(R.id.arc_bg);
        int height = titleBar.getHeight();

//        titleBar高度的时候全部显示
//        float abs =Math.min(height, Math.max(value, 0F)) / height;
//        titleBar高度一半的时候全部显示,但是最大透明度值不能超过1
        float abs = Math.min(1, Math.min(height, Math.max(value, 0F)) / height * 2);
        arcBg.setAlpha(abs);
        topBarBg.setAlpha(abs);
    }

}

设置behavior

然后我这边的布局是这样的将base_banner_behavior设置在StatusView上,只能设置在CoordinatorLayout节点下

<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">

    <com.linghit.lingjidashi.base.lib.view.StatusView
        android:id="@+id/base_state_container"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/base_banner_behavior">
        <com.scwang.smartrefresh.layout.SmartRefreshLayout
            android:id="@+id/base_refresh_layout"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:srlDisableContentWhenRefresh="true">

            <android.support.v7.widget.RecyclerView
                android:id="@+id/base_refresh_list"
                android:layout_width="match_parent"
                android:layout_height="wrap_content" />
        </com.scwang.smartrefresh.layout.SmartRefreshLayout>
    </com.linghit.lingjidashi.base.lib.view.StatusView>
</android.support.design.widget.CoordinatorLayout>

onStartNestedScroll onNestedScroll的回调流程

在CoordinatorLayout的onStartNestedScroll方法是这样的
在这里插入图片描述
而setNestedScrollAccepted只是设置下这两种状态下是否接收,然后通过isNestedScrollAccepted获取状态,如果accepted就回调onNestedScrollAccepted给Behavior,所以相应的其他方法在一些相应的状态会回调,而你在你想要的方法回调处理就行,比如我的需要根据滑动状态即时改变状态,所以我选择在onNestedScroll处理。
在这里插入图片描述
Demo:Gayhub直连
番外篇:喜马拉雅FM首页源码剖析实现过程
PS:

  • TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 100, getContext().getResources().getDisplayMetrics());
  • getContext().getResources().getIdentifier(“login_xx_” + code,
    “string”, getActivity().getPackageName());

其他收获

CoordinatorLayout分析参考

作者HansRove,源码XiMaLaYa-by-HansRove-,仿喜马拉雅, 对AVFoundation框架的一次尝试 软件环境: iOS9.1硬件环境: Mac OS X 10.11开发工具: Xcode7.1 项目描述: 模板是“喜马拉雅FM”,因为很喜欢这个软件的风格和内容,当时这款软件参杂着太多的广告以及推广。所以打算通过抓包,并Json解析出数据,进而使用自己搭建的界面完成视听播放功能, UI也算是高仿喜马拉雅FM”.但部分内容在原来基础上做了相应的修改。 功能概述: 0、音频播放:这是最基础的模块,提供用户收听各类声音、专辑及电台主播。 1、发现听:实时动态的信息展示页,每天的音频热门信息和音频专辑集数的更新;展示给用户声音分类和电台及网络主播相应的推荐页供用户选择交互。 2、定制听:记录用户关注的声音(专辑)、主播和电台以及收听历史,并推荐近几天热门声音。 3、下载听:记录用户下载记录并对用户下载过的声音进行分类展示。 4、搜索功能:根据热词提供展示,用户可以搜索主播、电台、声音或专辑。 项目所用技术及框架:【纯代码+故事板】项目使用MVVM模式搭建 1、二次封装AFNetworking与MJExtension进行数据请求与解析; 2、使用Masonry实现纯代码布局,使用MJRefresh实现下拉刷新上拉加载; 3、大量使用自定义Cell、自定义Button、自定义View来实现多控件封装方便布局使用; 4、使用AVFundation实现在线音频播放。并自定义播放器外观以及进度条配置; 项目收获: 1、对MVVM模式的使用更加熟练,设计模式的了解及掌握为未来开发少走了许多弯路; 2、熟悉JSON数据解析,对数据处理有一定的了解,特别是掌握了MJExtension框架的原理及写法; 3、通知中心及单例模式的使用,减低了代码的耦合性; 4、纯代码跳转Storyboard或者Xib使用更加熟练;封装了好多自定义视图,方便布局。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值