Android Design Library(二)使用CoordinatorLayout布局实现联动效果

使用CoordinatorLayout布局实现联动效果,下面先看下效果图,开始、滑动中和滑动完成效果图如下:

        

接下来我们具体来看下怎样实现这种效果
首先在项目的build.gradle文件中, 引入头像控件库和CardView库, 在本例中会使用
compile 'com.android.support:cardview-v7:23.3.0'//卡片布局
compile 'com.jakewharton:butterknife:7.0.1'//免去findViewById
compile 'de.hdodenhof:circleimageview:2.1.0'//圆形图片

 

1、外层布局

android.support.design.widget.CoordinatorLayout
android:fitsSystemWindows="false"//默认false,这个属性会导致与最上面的状态栏(status bar)重叠
android.support.design.widget.AppBarLayout

2、在AppBarLayout中, 添加CollapsingToolbarLayout控件, CollapsingToolbar会滚动消失, 被ToolBar替换, 实现滚动动画.

<android.support.design.widget.CollapsingToolbarLayout
    android:layout_width="match_parent"
    android:layout_height="450dp"
    app:layout_scrollFlags="scroll|exitUntilCollapsed|snap">
</android.support.design.widget.CollapsingToolbarLayout>

app:layout_scrollFlags="scroll|exitUntilCollapsed|snap"//的属性, scroll滑动, exitUntilCollapsed退出到最小, snap自动滑动动画, 定义CollapsingToolbarLayout的滑动属性.

CollapsingToolbarLayout划分两部分, 一部分大图, 一部分文字.

<ImageView
    android:id="@+id/img_placeholder"
    android:layout_width="match_parent"
    android:layout_height="300dp"
    android:scaleType="centerCrop"
    android:src="@mipmap/pic"
    app:layout_collapseMode="parallax"/>//折叠时的视差效果, 自动滑动.
<FrameLayout
    android:id="@+id/main_fl_title"
    android:layout_width="match_parent"
    android:layout_height="150dp"
    android:layout_gravity="bottom|center_horizontal"
    android:background="@color/colorPrimaryDark1"
    app:layout_collapseMode="parallax">//折叠时的视差效果, 自动滑动.
    <LinearLayout
        android:id="@+id/ll_titleContainer"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:layout_gravity="center">
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:gravity="bottom|center"
            android:text="@string/person_name"
            android:textColor="@android:color/white"
            android:textSize="30sp" />
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:layout_marginTop="4dp"
            android:text="演员"
            android:textColor="@android:color/white"/>
    </LinearLayout>
</FrameLayout>

3、添加NestedScrollView, 文字的滚动视图.

<android.support.v4.widget.NestedScrollView
    android:id="@+id/nestedScrollView"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:scrollbars="none"
    android:layout_marginTop="50dp"
    app:behavior_overlapTop="80dp"
    app:layout_behavior="@string/appbar_scrolling_view_behavior">
    <android.support.v7.widget.CardView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="8dp"
        app:cardElevation="8dp"//阴影大小
        app:contentPadding="16dp">
        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:lineSpacingExtra="8dp"
            android:text="@string/person_introduce"/>
    </android.support.v7.widget.CardView>
</android.support.v4.widget.NestedScrollView>

app:behavior_overlapTop="80dp", The amount that the scrolling view should overlap the bottom ofany AppBarLayout. 使ScrollView压在AppBarLayout上面一段长度.

4、添加ToolBar, 滚动结束的效果图.

<android.support.v7.widget.Toolbar
    android:id="@+id/main_tb_toolbar"
    android:layout_width="match_parent"
    android:layout_height="46dp"
    android:background="@color/colorPrimaryDark1"
    app:layout_anchor="@id/main_fl_title"
    app:theme="@style/ThemeOverlay.AppCompat.Dark">
    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:orientation="horizontal">
        <Space
            android:layout_width="40dp"
            android:layout_height="40dp" />
        <TextView
            android:id="@+id/main_tv_title"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:layout_marginLeft="8dp"
            android:gravity="center_vertical"
            android:text="@string/person_name"
            android:textColor="@android:color/white"
            android:textSize="20sp"
            android:visibility="invisible"/>
    </LinearLayout>
</android.support.v7.widget.Toolbar>

ToolBar是工具栏, 滑动完成时, 在最上部显示. Space预留头像的位置.

5、圆形头像CircleImageView显示(使用第三方库)

<de.hdodenhof.circleimageview.CircleImageView
    android:id="@+id/circleImageView"
    android:layout_width="100dp"
    android:layout_height="100dp"
    android:layout_gravity="center"
    android:layout_marginBottom="40dp"
    android:src="@mipmap/small"
    app:civ_border_color="@color/white"
    app:civ_border_width="2dp"
    app:layout_behavior="com.test.wjy.statusbartest.CoordinatorLayout.AvatarImageBehavior"/>

头像控件的滑动行为是自定义的类, 具体行为参考注释. 判断起始和终止位置, child和dependency视图联动, 这里是图像视图和Toolbar联动.

package com.test.wjy.statusbartest.CoordinatorLayout;
import android.content.Context;
import android.support.design.widget.CoordinatorLayout;
import android.support.v7.widget.Toolbar;
import android.util.AttributeSet;
import android.view.View;
import com.test.wjy.statusbartest.R;
import de.hdodenhof.circleimageview.CircleImageView;

/**
 * Created by wjy on 2017/5/4.
 * 图片控件位置动画
 */
public class AvatarImageBehavior extends CoordinatorLayout.Behavior<CircleImageView> {
    private final static float MIN_AVATAR_PERCENTAGE_SIZE = 0.3f;
    private final static int EXTRA_FINAL_AVATAR_PADDING = 80;
    private int mStartYPosition; // 起始的Y轴位置
    private int mFinalYPosition; // 结束的Y轴位置
    private int mStartHeight; // 开始的图片高度
    private int mFinalHeight; // 结束的图片高度
    private int mStartXPosition; // 起始的X轴高度
    private int mFinalXPosition; // 结束的X轴高度
    private float mStartToolbarPosition; // Toolbar的起始位置
    private final Context mContext;
    private float mAvatarMaxSize;
    public AvatarImageBehavior(Context context, AttributeSet attrs) {
        mContext = context;
        init();
    }
    private void init() {
        bindDimensions();
    }
    private void bindDimensions() {
        mAvatarMaxSize = mContext.getResources().getDimension(R.dimen.image_width);
    }

    @Override
    public boolean layoutDependsOn(CoordinatorLayout parent, CircleImageView child, View dependency) {
        // 依赖Toolbar控件
        return dependency instanceof Toolbar;
    }
    @Override
    public boolean onDependentViewChanged(CoordinatorLayout parent, CircleImageView child, View dependency) {
        // 初始化属性
        shouldInitProperties(child, dependency);
        // 最大滑动距离: 起始位置-状态栏高度
        final int maxScrollDistance = (int) (mStartToolbarPosition - getStatusBarHeight());
        // 滑动的百分比
        float expandedPercentageFactor = dependency.getY() / maxScrollDistance;
        // Y轴距离
        float distanceYToSubtract = ((mStartYPosition - mFinalYPosition)
                * (1f - expandedPercentageFactor)) + (child.getHeight() / 2);
        // X轴距离
        float distanceXToSubtract = ((mStartXPosition - mFinalXPosition)
               * (1f - expandedPercentageFactor)) + (child.getWidth() / 2);
        // 高度减小
        float heightToSubtract = ((mStartHeight - mFinalHeight) * (1f - expandedPercentageFactor));
        // 图片位置
        child.setY(mStartYPosition - distanceYToSubtract);
        child.setX(mStartXPosition - distanceXToSubtract);
        // 图片大小
        CoordinatorLayout.LayoutParams lp = (CoordinatorLayout.LayoutParams) child.getLayoutParams();
        lp.width = (int) (mStartHeight - heightToSubtract);
        lp.height = (int) (mStartHeight - heightToSubtract);
        child.setLayoutParams(lp);
        return true;
    }
    /**
     * 初始化动画值
     *
     * @param child      图片控件
     * @param dependency ToolBar
     */
    private void shouldInitProperties(CircleImageView child, View dependency) {
        // 图片控件中心
        if (mStartYPosition == 0)
            mStartYPosition = (int) (child.getY() + (child.getHeight() / 2));
        // Toolbar中心
        if (mFinalYPosition == 0)
            mFinalYPosition = (dependency.getHeight() / 2);
        // 图片高度
        if (mStartHeight == 0)
            mStartHeight = child.getHeight();
        // Toolbar缩略图高度
        if (mFinalHeight == 0)
            mFinalHeight = mContext.getResources().getDimensionPixelOffset(R.dimen.image_final_width);
        // 图片控件水平中心
        if (mStartXPosition == 0)
            mStartXPosition = (int) (child.getX() + (child.getWidth() / 2));
        // 边缘+缩略图宽度的一半
        if (mFinalXPosition == 0)
            mFinalXPosition = mContext.getResources().getDimensionPixelOffset(R.dimen.abc_action_bar_content_inset_material) + (mFinalHeight / 2);
        // Toolbar的起始位置
        if (mStartToolbarPosition == 0)
            mStartToolbarPosition = dependency.getY() + (dependency.getHeight() / 2);
    }
    // 获取状态栏高度
    public int getStatusBarHeight() {
        int result = 0;
        int resourceId = mContext.getResources().getIdentifier("status_bar_height", "dimen", "android");

        if (resourceId > 0) {
            result = mContext.getResources().getDimensionPixelSize(resourceId);
        }
        return result;
    }
}

6、小圆圈(加号)

android.support.design.widget.FloatingActionButton

app:layout_anchorGravity="bottom|right|end"//在布局的哪个位置,次设置为右下角
app:backgroundTint="@color/green"//背景色
app:rippleColor="@color/yellow"//点击后颜色
app:elevation="10dp"//海拔高(阴影大小)
app:layout_anchor="@id/nestedScrollView"//相对于哪个布局  以哪个布局为锚
app:pressedTranslationZ="20dp"//按压后Z轴高度
android:clickable="true"//可点击

整个页面的布局main.xml文件内容如下:

<?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:id="@+id/ctl_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="false">
    <android.support.design.widget.AppBarLayout
        android:id="@+id/abl_appBar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/AppTheme.AppBarOverlay">
        <android.support.design.widget.CollapsingToolbarLayout
            android:layout_width="match_parent"
            android:layout_height="450dp"
            app:layout_scrollFlags="scroll|exitUntilCollapsed|snap">
            <ImageView
                android:id="@+id/img_placeholder"
                android:layout_width="match_parent"
                android:layout_height="300dp"
                android:scaleType="centerCrop"
                android:src="@mipmap/chexiao_big"
                app:layout_collapseMode="parallax" />
            <FrameLayout
                android:id="@+id/main_fl_title"
                android:layout_width="match_parent"
                android:layout_height="150dp"
                android:layout_gravity="bottom|center_horizontal"
                android:background="@color/colorPrimaryDark1"
                app:layout_collapseMode="parallax">
                <LinearLayout
                    android:id="@+id/ll_titleContainer"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_gravity="center"
                    android:orientation="vertical">
                    <TextView
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_gravity="center_horizontal"
                        android:gravity="bottom|center"
                        android:text="@string/chexiao"
                        android:textColor="@android:color/white"
                        android:textSize="30sp" />
                    <TextView
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_gravity="center_horizontal"
                        android:layout_marginTop="4dp"
                        android:text="演员"
                        android:textColor="@android:color/white" />
                </LinearLayout>
            </FrameLayout>
        </android.support.design.widget.CollapsingToolbarLayout>
    </android.support.design.widget.AppBarLayout>
    <android.support.v4.widget.NestedScrollView
        android:id="@+id/nestedScrollView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_marginTop="50dp"
        android:scrollbars="none"
        app:behavior_overlapTop="80dp"
        app:layout_behavior="@string/appbar_scrolling_view_behavior">
        <android.support.v7.widget.CardView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_margin="8dp"
            app:cardElevation="8dp"
            app:contentPadding="16dp">
            <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:lineSpacingExtra="8dp"
                android:text="@string/chexiao_introduce" />
        </android.support.v7.widget.CardView>
    </android.support.v4.widget.NestedScrollView>
    <android.support.v7.widget.Toolbar
        android:id="@+id/main_tb_toolbar"
        android:layout_width="match_parent"
        android:layout_height="46dp"
        android:background="@color/colorPrimaryDark1"
        app:layout_anchor="@id/main_fl_title"
        app:theme="@style/ThemeOverlay.AppCompat.Dark">
        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:orientation="horizontal">
            <Space
                android:layout_width="40dp"
                android:layout_height="40dp" />
            <TextView
                android:id="@+id/main_tv_title"
                android:layout_width="wrap_content"
                android:layout_height="match_parent"
                android:layout_marginLeft="8dp"
                android:gravity="center_vertical"
                android:text="@string/chexiao"
                android:textColor="@android:color/white"
                android:textSize="20sp"
                android:visibility="invisible" />
        </LinearLayout>
    </android.support.v7.widget.Toolbar>
    <de.hdodenhof.circleimageview.CircleImageView
        android:id="@+id/circleImageView"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:layout_gravity="center"
        android:layout_marginBottom="40dp"
        android:src="@mipmap/chexiao_small"
        app:civ_border_color="@color/white"
        app:civ_border_width="2dp"
        app:layout_behavior="com.test.wjy.statusbartest.CoordinatorLayout.AvatarImageBehavior" />
    <android.support.design.widget.FloatingActionButton
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="15dp"
        android:clickable="true"
        android:src="@mipmap/add_black"
        app:backgroundTint="@color/green"
        app:elevation="10dp"
        app:layout_anchor="@id/nestedScrollView"
        app:layout_anchorGravity="bottom|right|end"
        app:pressedTranslationZ="20dp"
        app:rippleColor="@color/yellow" />
</android.support.design.widget.CoordinatorLayout>

逻辑部分的Activity

package com.test.wjy.statusbartest.CoordinatorLayout;
import android.app.Activity;
import android.os.Bundle;
import android.support.design.widget.AppBarLayout;
import android.support.design.widget.CollapsingToolbarLayout;
import android.support.v7.widget.Toolbar;
import android.view.View;
import android.view.animation.AlphaAnimation;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.test.wjy.statusbartest.R;
import butterknife.Bind;
import butterknife.ButterKnife;
import de.hdodenhof.circleimageview.CircleImageView;
/**
 * Created by wjy on 2017/5/3.
 */
public class CoordinatorLayoutActivity extends Activity {
    // 控制ToolBar的变量
    private static final float PERCENTAGE_TO_SHOW_TITLE_AT_TOOLBAR = 0.9f;
    private static final float PERCENTAGE_TO_HIDE_TITLE_DETAILS = 0.3f;
    private static final int ALPHA_ANIMATIONS_DURATION = 200;
    private boolean mIsTheTitleVisible = false;
    private boolean mIsTheTitleContainerVisible = true;
    @Bind(R.id.img_placeholder)
    ImageView mIvPlaceholder; // 大图片
    @Bind(R.id.ll_titleContainer)
    LinearLayout mLlTitleContainer; // TitleLinearLayout
    @Bind(R.id.main_fl_title)
    FrameLayout mFlTitleContainer; // TitleFrameLayout
    @Bind(R.id.abl_appBar)
    AppBarLayout mAblAppBar; // 整个可以滑动的AppBar
    @Bind(R.id.main_tv_title)
    TextView mTvToolbarTitle; // 标题栏Title
    @Bind(R.id.main_tb_toolbar)
    Toolbar mTbToolbar; // 工具栏
    @Bind(R.id.circleImageView)
    CircleImageView circleImageView;//圆形头像
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activvity_coordinatorlayout);
        ButterKnife.bind(this);
        initView();
    }
    private void initView() {
        mTbToolbar.setTitle("");
        //AppBar的监听
        mAblAppBar.addOnOffsetChangedListener(new AppBarLayout.OnOffsetChangedListener() {
            @Override
            public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) {
                int maxScroll = appBarLayout.getTotalScrollRange();
                float percentage = (float) Math.abs(verticalOffset) / (float) maxScroll;
                handleAlphaOnTitle(percentage);
                handleToolbarTitleVisibility(percentage);
            }
        });
        initParallaxValues(); // 自动滑动效果
    }
    // 设置自动滑动的动画效果
    private void initParallaxValues() {
        CollapsingToolbarLayout.LayoutParams petDetailsLp =
                (CollapsingToolbarLayout.LayoutParams) mIvPlaceholder.getLayoutParams();
        CollapsingToolbarLayout.LayoutParams petBackgroundLp =
                (CollapsingToolbarLayout.LayoutParams) mFlTitleContainer.getLayoutParams();
        petDetailsLp.setParallaxMultiplier(0.9f);
        petBackgroundLp.setParallaxMultiplier(0.3f);
        mIvPlaceholder.setLayoutParams(petDetailsLp);
        mFlTitleContainer.setLayoutParams(petBackgroundLp);
    }
    // 处理ToolBar的显示
    private void handleToolbarTitleVisibility(float percentage) {
        if (percentage >= PERCENTAGE_TO_SHOW_TITLE_AT_TOOLBAR) {
            if (!mIsTheTitleVisible) {
                startAlphaAnimation(mTvToolbarTitle, ALPHA_ANIMATIONS_DURATION, View.VISIBLE);
                mIsTheTitleVisible = true;
            }
        } else {
            if (mIsTheTitleVisible) {
                startAlphaAnimation(mTvToolbarTitle, ALPHA_ANIMATIONS_DURATION, View.INVISIBLE);
                mIsTheTitleVisible = false;
            }
        }
    }
    // 控制Title的显示
    private void handleAlphaOnTitle(float percentage) {
        if (percentage >= PERCENTAGE_TO_HIDE_TITLE_DETAILS) {
            if (mIsTheTitleContainerVisible) {
                startAlphaAnimation(mLlTitleContainer, ALPHA_ANIMATIONS_DURATION, View.INVISIBLE);
                mIsTheTitleContainerVisible = false;
            }
        } else {
            if (!mIsTheTitleContainerVisible) {
                startAlphaAnimation(mLlTitleContainer, ALPHA_ANIMATIONS_DURATION, View.VISIBLE);
                mIsTheTitleContainerVisible = true;
            }
        }
    }
    // 设置渐变的动画
    public static void startAlphaAnimation(View v, long duration, int visibility) {
        AlphaAnimation alphaAnimation = (visibility == View.VISIBLE)
                ? new AlphaAnimation(0f, 1f)
                : new AlphaAnimation(1f, 0f);
        alphaAnimation.setDuration(duration);
        alphaAnimation.setFillAfter(true);
        v.startAnimation(alphaAnimation);
    }
}

这样联动效果就已经显示出来了.


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

时代新人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值