Android-CoordinatorLayout.……动画效果

简单实现一个多元素互动的AppBarLayout头部滑动效果

2018-05-08  本文已影响137人  留给时光吧

效果如下:



在没有AppBarLayout之前,这种效果实现起来应该是挺复杂的,但是引入AppBarLayout后就很随意了,先看布局文件:

<android.support.design.widget.CoordinatorLayout
    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.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="250dp"
        android:background="@color/colorTransparent"
        android:id="@+id/app_bar"
        app:elevation="0dp"
        >
        <include layout="@layout/layout_weather_head"/>
    </android.support.design.widget.AppBarLayout>
    <android.support.v4.widget.NestedScrollView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/ns"
        app:layout_behavior="@string/appbar_scrolling_view_behavior">
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical"
            android:descendantFocusability="blocksDescendants"
            >
            <com.app.chenyang.sweather.ui.widget.WeatherChartView
                android:id="@+id/daily_chart"
                android:layout_width="match_parent"
                android:layout_height="230dp"
                android:layout_marginLeft="15dp"
                android:layout_marginRight="15dp"/>
            <com.app.chenyang.sweather.ui.widget.MyGridView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:id="@+id/gv_living_details"
                android:numColumns="2"
                >
            </com.app.chenyang.sweather.ui.widget.MyGridView>
        </LinearLayout>
    </android.support.v4.widget.NestedScrollView>
</android.support.design.widget.CoordinatorLayout>

我们那些随着滑动互动的元素就主要就是放在AppBarLayout中。这个布局有几个关键点。

接下来看java代码。我们的效果是随着滑动来改变各个元素的位置,所以就要监听AppBarLayout的滑动事件,由于一些控件可能会未加载完成,导致测量出一些数据为0,我们首先添加一个布局监听,可以任意找一个AppBarLayout内的控件添加:

tvCity.getViewTreeObserver().addOnGlobalLayoutListener(this);

在布局建立后我们就能拿到每个控件的真实数据,然后根据滑动的距离来动态的改变每个控件位置及其他属性,逻辑不复杂,只不过要有比较缜密的计算,具体实现如下,主要部分有注释:

    @Override
    public void onGlobalLayout() {
        tvCity.getViewTreeObserver().removeOnGlobalLayoutListener(this);
        nestedScrollView.fullScroll(View.FOCUS_UP);
        int ivArrowDistance = ivPageBack.getRight();
        //获取AppBarLayout最大滑动距离
        final int scrollRange = appBar.getTotalScrollRange();
        //预先计算各个部分要移动的距离
        final double tvCityLeftTranslationDistance = tvCity.getLeft() + (tvCity.getWidth() * ZOOM_RATIO * 0.5) - ivArrowDistance;
        final double tvTimeLeftTranslationDistance = tvTime.getLeft() + (tvTime.getWidth() * ZOOM_RATIO * 0.5) - ivArrowDistance;
        final double llWeatherRightTranslationDistance = (BaseUtils.SCREEN_WIDTH - llWeather.getRight()) + (llWeather.getWidth() * ZOOM_RATIO * 0.5) - ivArrowDistance;
        final double llWeatherTopTranslationDistance = ivPageForwardTopMargin + scrollRange/2 + ivPageBack.getHeight() * 0.5 - llWeather.getHeight() * 0.5 - llWeatherTopMargin;

        appBar.addOnOffsetChangedListener(new AppBarLayout.OnOffsetChangedListener() {
            @Override
            public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) {
                //获得滑动具体,计算比例
                int scrollDistance = Math.abs(verticalOffset);
                float scrollPercentage = (float) scrollDistance/scrollRange;
                
                //两端箭头处理,主要是随着滑动上下移动
                ivPageBackParams.topMargin = ivPageBackTopMargin + scrollDistance/2;
                ivPageForwardParams.topMargin = ivPageForwardTopMargin + scrollDistance/2;
                ivPageBack.setLayoutParams(ivPageBackParams);
                ivPageForward.setLayoutParams(ivPageForwardParams);

                //底部AQI和天气文字的处理,只要是设置透明度
                if(scrollDistance > ALPHA_DISTANCE){
                    tvAQI.setAlpha(0);
                    tvWeather.setAlpha(0);
                }else{
                    tvAQI.setAlpha(1-scrollDistance/(float)ALPHA_DISTANCE);
                    tvWeather.setAlpha(1-scrollDistance/(float)ALPHA_DISTANCE);
                }

                //第一行城市名称及时间的处理
                //向上滑动时文字向右移动且适当缩小
                tvCityParams.topMargin = tvCityTopMargin + scrollDistance;
                tvCityParams.leftMargin = -(int) (tvCityLeftTranslationDistance * scrollPercentage);

                tvTimeParams.topMargin = tvTimeTopMargin + scrollDistance;
                tvTimeParams.leftMargin = -(int) (tvTimeLeftTranslationDistance * scrollPercentage);

                tvCity.setLayoutParams(tvCityParams);
                tvCity.setScaleX(1 - ZOOM_RATIO * scrollPercentage);
                tvCity.setScaleY(1 - ZOOM_RATIO * scrollPercentage);

                tvTime.setLayoutParams(tvTimeParams);
                tvTime.setScaleX(1 -ZOOM_RATIO * scrollPercentage);
                tvTime.setScaleY(1 -ZOOM_RATIO * scrollPercentage);

                //这里将天气图标和温度作为一整块处理
                //向上滑动时整体右移且缩小
                llWeatherParams.topMargin = (int) (llWeatherTopTranslationDistance * scrollPercentage) + llWeatherTopMargin;
                llWeatherParams.rightMargin = -(int) (llWeatherRightTranslationDistance * scrollPercentage);
                llWeather.setScaleX(1 - ZOOM_RATIO * scrollPercentage);
                llWeather.setScaleY(1 - ZOOM_RATIO * scrollPercentage);
            }
        });

    }

起始这种方法是比较直接原始的,也很简单,就是实现起来比较繁琐,以后遇到更简便的方法在更新ヾ(o・ω・)ノ

上一篇下一篇

猜你喜欢

热点阅读