V4,V7,V13等Android开发技能知识点安卓文档

CoordinatorLayout中各种控件实现Demo

2017-03-10  本文已影响300人  黄海佳
一、简述CoordinatorLayout

原理请看这里 Android design包中CoordinatorLayout的设计原理
再看看facebook的效果

1018039-920fa0d9d86fa68b.gif
下面直接贴网上一些经典Demo
例子一:底部菜单抽屉效果
1 添加依赖
compile 'com.android.support:appcompat-v7:25.1.1'
2 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/coordinator"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true">


    <android.support.v4.widget.NestedScrollView
        android:id="@+id/nestedScrollView"
        android:layout_width="match_parent"
        android:layout_height="350dp"
        android:background="@mipmap/ic_launcher"
        app:layout_behavior="@string/bottom_sheet_behavior"
        app:behavior_peekHeight="0dp"
        app:behavior_hideable="true">
        <!--中间可以随意写自己的布局-->
        
    </android.support.v4.widget.NestedScrollView>

    <android.support.design.widget.FloatingActionButton
        android:id="@+id/fabs"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom|end"
        android:layout_margin="@dimen/fab_margin"
        app:srcCompat="@android:drawable/ic_dialog_email" />
    

</android.support.design.widget.CoordinatorLayout>
2 Activity
    @BindView(R.id.coordinator)
    CoordinatorLayout coordinator;

    @BindView(R.id.nestedScrollView)
    NestedScrollView scrollView;

    @BindView(R.id.fabs)
    FloatingActionButton fab;

    @Override
    protected int setLayoutResourceID() {
        return R.layout.activity_hellow;
    }

    @Override
    protected void setUpView() {
       fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                //点击出现底部菜单
                BottomSheetBehavior bottomSheetBehavior = BottomSheetBehavior.from(scrollView);
                bottomSheetBehavior.setState(bottomSheetBehavior.getState() == BottomSheetBehavior.STATE_COLLAPSED?BottomSheetBehavior.STATE_EXPANDED:BottomSheetBehavior.STATE_COLLAPSED);
            }
        });
    }
实现的关键点:

1 根布局为 CoordinatorLayout;
2 为底部弹出框设置app:layout_behavior="@string/bottom_sheet_behavior";
3 在代码中得到BottomSheetBehavior(底部菜单),并动态改变它的状态;
4至于FloatingActionButton跟随弹出框移动,关键点在于为FloatingActionButton设置
5app:layout_behavior="@string/bottom_sheet_behavior"appbar_scrolling_view_behavior并不是一个字符串,而是一个类,而且这个类我们可以自定义。

例子二:AppBarLayout
1 xml
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <android.support.design.widget.AppBarLayout
        android:id="@+id/main_appbarlayout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <android.support.v7.widget.Toolbar
            android:id="@+id/main_toolbar"
            app:title="@string/app_name"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"/>
    </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">
         <!--中间可以随意写自己的布局-->
    </android.support.v4.widget.NestedScrollView>

</android.support.design.widget.CoordinatorLayout>
属性介绍
layout_scrollFlags

AppBarLayout中的子控件通过layout_scrollFlags这个属性或者setScrollFlags才能展现出他们的滚动行为。它的值有:scroll、snap、enterAlways、enterAlwaysCollapsed、exitUntilCollapsed。

scrol:所有想滚动的view都需要设置这个flag, 没有设置这个flag的view将被固定在屏幕顶部。这个我们已经见识过了
snap:这个直接翻译成“折断”。就是说被"关心"的View如果显示了一半,就全显示出来,否则则隐藏。

app:layout_scrollFlags="scroll|snap"
app:layout_scrollFlags="scroll|snap"
enterAlways:往上滑动的时候被"关心"的这个View隐藏,往下滑的时候显示
app:layout_scrollFlags="scroll|enterAlways"
app:layout_scrollFlags="scroll|enterAlways"
enterAlwaysCollapsed:向上滑动的时候被"关心"的这个View隐藏,向下滑动时先展现一个最小高度,等到滑动到NestedScrollView最顶部的时候再完全展现出来。注意这边一定要有一个最小高度,即minHeight属性,并且enterAlwaysCollapsed一定要搭配enterAlways和scroll才能正常展现
<android.support.v7.widget.Toolbar 
    android:id="@+id/main_toolbar"
    app:title="@string/app_name"
    app:layout_scrollFlags="scroll|enterAlways|enterAlwaysCollapsed"
    android:layout_width="match_parent"
    android:layout_height="200dip"
    android:minHeight="?attr/actionBarSize"/>
app:layout_scrollFlags="scroll|enterAlways|enterAlwaysCollapsed"
如果你的AppBarLayout中包含其他的View,那么含有layout_scrollFlags的标签的View请布局在前面。因为AppBarLayout实际上是一个LinearLayout。
例子三:折叠的Toolbar

CollapsingToolbarLayout提供了一个可以折叠的Toolbar,它继承FrameLayout。给它设置layout_scrollFlags,它可以控制包含在其中的子控件(如:ImageView、Toolbar)。

1 xml
<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.support.design.widget.AppBarLayout
        android:id="@+id/main_appbarlayout"
        android:layout_width="match_parent"
        android:layout_height="200dip">

        <android.support.design.widget.CollapsingToolbarLayout 
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:expandedTitleMarginStart="64dp"
            app:expandedTitleMarginEnd="64dp"
            app:contentScrim="?attr/colorPrimaryDark"
            app:layout_scrollFlags="scroll|exitUntilCollapsed">
            <ImageView
                android:src="@mipmap/bg"
                app:layout_collapseParallaxMultiplier="0.5"
                app:layout_collapseMode="parallax"
                android:scaleType="centerCrop"
                android:layout_width="match_parent"
                android:layout_height="match_parent" />
            <android.support.v7.widget.Toolbar
                app:title="@string/app_name"
                app:layout_collapseMode="pin"
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize"/>
          </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">
        <!-- 自己布局 -->
    </android.support.v4.widget.NestedScrollView>

</android.support.design.widget.CoordinatorLayout>
1018039-e7f47d571d8b5bfb.gif

相关属性

layout_collapseMode

有2种模式,
pin模式:即固定模式,在折叠的时候最后固定在顶端
parallax模式:即视差模式,在折叠的时候会有个视差折叠的效果

layout_collapseParallaxMultiplier

CollapsingToolbarLayout滑动时,子视图的视觉差。这个值的范围为0.0-1.0之间。为0的时候,你可以感觉到视图完全随NestedScrollView滚动;为1的时候,似乎又是完全不滚动

contentScrim

这是ToolBar被折叠到顶部固定后的背景。

expandedTitleMarginStart/expandedTitleMarginEnd

在ToolBar被折叠前文字部分的左右间距

setExpandedTitleColor/setCollapsedTitleTextColor

设置还没收缩时状态下字体颜色与收缩后Toolbar上字体的颜色

setTitle

使用CollapsingToolbarLayout时必须把title设置到CollapsingToolbarLayout上,设置到Toolbar上不会显示

例子四:同步移动

先看效果


2800913-45ea0c7812fa7a12.gif
DodoMoveView
**
 * @创建 HaiJia
 * @时间 2017/3/10 11:21
 * @描述 可拖动的View
 */

public class DodoMoveView extends TextView{

    private int lastX;
    private int lastY;


    public DodoMoveView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        int x = (int) event.getRawX();
        int y = (int) event.getRawY();

        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                break;

            case MotionEvent.ACTION_MOVE:
                CoordinatorLayout.MarginLayoutParams layoutParams = 
                (CoordinatorLayout.MarginLayoutParams) getLayoutParams();
                int left = layoutParams.leftMargin + x - lastX;
                int top = layoutParams.topMargin + y - lastY;

                layoutParams.leftMargin = left;
                layoutParams.topMargin = top;
                setLayoutParams(layoutParams);
                requestLayout();
                break;

            case MotionEvent.ACTION_UP:
                break;
        }
        lastX = x;
        lastY = y;
        //如果返回值是true,代表事件在当前的viewGroup中会被处理,
        //向下传递之路被截断(所有子控件将没有机会参与Touch事件),
        // 同时把事件传递给当前的控件的onTouchEvent()处理。
        return true;
    }
}

然后简单修改Behavior

public class DodoBehavior0s extends CoordinatorLayout.Behavior<Button>{

    private Context mContext;

    private int width;
    private int height;

    public DodoBehavior0s(Context context, AttributeSet attrs) {
        super(context, attrs);
        this.mContext = context;
        DisplayMetrics display = context.getResources().getDisplayMetrics();
        width = display.widthPixels;
        height = display.heightPixels;
    }

    @Override
    public boolean layoutDependsOn(CoordinatorLayout parent, Button child, View dependency) {
        //如果dependency是TempView的实例,说明它就是我们所需要的Dependency
        return dependency instanceof DodoMoveView;
    }

    //每次dependency位置发生变化,都会执行onDependentViewChanged方法
    @Override
    public boolean onDependentViewChanged(CoordinatorLayout parent, Button btn, View dependency) {

        //根据dependency的位置,设置Button的位置

        int top = dependency.getTop();
        int left = dependency.getLeft();

        int x = width - left - btn.getWidth();
        int y = height - top - btn.getHeight();

        setPosition(btn, x, y);
        return true;
    }

    private void setPosition(View v, int x, int y) {
        CoordinatorLayout.MarginLayoutParams layoutParams = (CoordinatorLayout.MarginLayoutParams) v.getLayoutParams();
        layoutParams.leftMargin = x;
        layoutParams.topMargin = y;
        layoutParams.width = y/2;
        v.setLayoutParams(layoutParams);
    }
}

效果如下


2800913-9d0211e3e8802f09.gif
三、其他各种效果Demo
1、BackBehavior

快速返回效果的Behavior,根据AppBarLayout的滚动来控制自定义View的滚动


1018039-5d44d427cea2929e.gif
2、项目可用的Demo

CoordinatorLayoutSample
CoordinatorBehaviorExample

2267876-d4f42ee04a3d2760.gif

附上关键源码

xml
<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.support.design.widget.AppBarLayout
        android:id="@+id/app_bar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">

        <android.support.design.widget.CollapsingToolbarLayout
            android:id="@+id/collapsing_tool_bar"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:contentScrim="@color/colorPrimary"
            app:expandedTitleGravity="bottom|center_horizontal"
            app:layout_scrollFlags="scroll|exitUntilCollapsed|snap">

            <ImageView
                android:id="@+id/iv_head"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:scaleType="fitXY"
                android:src="@mipmap/dd"
                app:layout_collapseMode="parallax"
                app:layout_collapseParallaxMultiplier="0.6"/>

            <!-- 设置app:navigationIcon="@android:color/transparent"给头像预留位置 -->
            <android.support.v7.widget.Toolbar
                android:id="@+id/tool_bar"
                android:layout_width="match_parent"
                android:layout_height="50dp"
                app:layout_collapseMode="pin"
                app:navigationIcon="@android:color/transparent"
                app:theme="@style/ThemeOverlay.AppCompat.Dark"
                app:title="Tom Hardy"/>
        </android.support.design.widget.CollapsingToolbarLayout>
    </android.support.design.widget.AppBarLayout>

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

        <include layout="@layout/layout_content_tom"/>
    </android.support.v4.widget.NestedScrollView>

    <!-- layout_anchor属性5.0以上需要设置为CollapsingToolbarLayout,不然头像最后会被覆盖 -->
    <de.hdodenhof.circleimageview.CircleImageView
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:layout_margin="16dp"
        android:src="@mipmap/tom"
        app:border_color="@android:color/white"
        app:border_width="1dp"
        app:layout_anchor="@id/collapsing_tool_bar"
        app:layout_anchorGravity="bottom|right"
        app:layout_behavior="com.haijia.demo.behavior.AvatarBehavior"/>
</android.support.design.widget.CoordinatorLayout>
layout_content_tom.xml
<?xml version="1.0" encoding="utf-8"?>

<android.support.v7.widget.CardView
    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="wrap_content"
    android:layout_marginTop="5dp"
    app:cardCornerRadius="5dp"
    app:cardElevation="8dp"
    app:cardPreventCornerOverlap="false"
    app:cardUseCompatPadding="true">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginLeft="10dp"
            android:layout_marginRight="10dp"
            android:layout_marginTop="10dp"
            android:orientation="horizontal">

            <TextView
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="Date of Birth"
                android:textAppearance="@style/TextAppearance.AppCompat.Body2"/>

            <TextView
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="3"
                android:text="15 September 1977, Hammersmith, London, England, UK"/>
        </LinearLayout>

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginLeft="10dp"
            android:layout_marginRight="10dp"
            android:layout_marginTop="10dp"
            android:orientation="horizontal">

            <TextView
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="Birth Name"
                android:textAppearance="@style/TextAppearance.AppCompat.Body2"/>

            <TextView
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="3"
                android:text="Edward Thomas Hardy"/>
        </LinearLayout>

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginBottom="10dp"
            android:layout_marginLeft="10dp"
            android:layout_marginRight="10dp"
            android:layout_marginTop="10dp"
            android:orientation="horizontal">

            <TextView
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="Height"
                android:textAppearance="@style/TextAppearance.AppCompat.Body2"/>

            <TextView
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="3"
                android:text="(1.75 m)"/>
        </LinearLayout>

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_margin="10dp"
            android:text="黄海佳"/>
    </LinearLayout>
</android.support.v7.widget.CardView>

AvatarBehavior
public class AvatarBehavior extends CoordinatorLayout.Behavior<CircleImageView> {

    // 缩放动画变化的支点
    private static final float ANIM_CHANGE_POINT = 0.2f;

    private Context mContext;
    // 整个滚动的范围
    private int mTotalScrollRange;
    // AppBarLayout高度
    private int mAppBarHeight;
    // AppBarLayout宽度
    private int mAppBarWidth;
    // 控件原始大小
    private int mOriginalSize;
    // 控件最终大小
    private int mFinalSize;
    // 控件最终缩放的尺寸,设置坐标值需要算上该值
    private float mScaleSize;
    // 原始x坐标
    private float mOriginalX;
    // 最终x坐标
    private float mFinalX;
    // 起始y坐标
    private float mOriginalY;
    // 最终y坐标
    private float mFinalY;
    // ToolBar高度
    private int mToolBarHeight;
    // AppBar的起始Y坐标
    private float mAppBarStartY;
    // 滚动执行百分比[0~1]
    private float mPercent;
    // Y轴移动插值器
    private DecelerateInterpolator mMoveYInterpolator;
    // X轴移动插值器
    private AccelerateInterpolator mMoveXInterpolator;
    // 最终变换的视图,因为在5.0以上AppBarLayout在收缩到最终状态会覆盖变换后的视图,所以添加一个最终显示的图片
    private CircleImageView mFinalView;
    // 最终变换的视图离底部的大小
    private int mFinalViewMarginBottom;


    public AvatarBehavior(Context context, AttributeSet attrs) {
        super(context, attrs);
        mContext = context;
        mMoveYInterpolator = new DecelerateInterpolator();
        mMoveXInterpolator = new AccelerateInterpolator();
        if (attrs != null) {
            TypedArray a = mContext.obtainStyledAttributes(attrs, R.styleable.AvatarImageBehavior);
            mFinalSize = (int) a.getDimension(R.styleable.AvatarImageBehavior_finalSize, 0);
            mFinalX = a.getDimension(R.styleable.AvatarImageBehavior_finalX, 0);
            mToolBarHeight = (int) a.getDimension(R.styleable.AvatarImageBehavior_toolBarHeight, 0);
            a.recycle();
        }
    }

    @Override
    public boolean layoutDependsOn(CoordinatorLayout parent, CircleImageView child, View dependency) {
        return dependency instanceof AppBarLayout;
    }

    @Override
    public boolean onDependentViewChanged(CoordinatorLayout parent, CircleImageView child, View dependency) {

        if (dependency instanceof AppBarLayout) {
            _initVariables(child, dependency);

            mPercent = (mAppBarStartY - dependency.getY()) * 1.0f / mTotalScrollRange;

            float percentY = mMoveYInterpolator.getInterpolation(mPercent);
            AnimHelper.setViewY(child, mOriginalY, mFinalY - mScaleSize, percentY);

            if (mPercent > ANIM_CHANGE_POINT) {
                float scalePercent = (mPercent - ANIM_CHANGE_POINT) / (1 - ANIM_CHANGE_POINT);
                float percentX = mMoveXInterpolator.getInterpolation(scalePercent);
                AnimHelper.scaleView(child, mOriginalSize, mFinalSize, scalePercent);
                AnimHelper.setViewX(child, mOriginalX, mFinalX - mScaleSize, percentX);
            } else {
                AnimHelper.scaleView(child, mOriginalSize, mFinalSize, 0);
                AnimHelper.setViewX(child, mOriginalX, mFinalX - mScaleSize, 0);
            }
            if (mFinalView != null) {
                if (percentY == 1.0f) {
                    // 滚动到顶时才显示
                    mFinalView.setVisibility(View.VISIBLE);
                } else {
                    mFinalView.setVisibility(View.GONE);
                }
            }
        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && dependency instanceof CollapsingToolbarLayout) {
            // 大于5.0才生成新的最终的头像,因为5.0以上AppBarLayout会覆盖变换后的头像
            if (mFinalView == null && mFinalSize != 0 && mFinalX != 0 && mFinalViewMarginBottom != 0) {
                mFinalView = new CircleImageView(mContext);
                mFinalView.setVisibility(View.GONE);
                // 添加为CollapsingToolbarLayout子视图
                ((CollapsingToolbarLayout) dependency).addView(mFinalView);
                FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) mFinalView.getLayoutParams();
                // 设置大小
                params.width = mFinalSize;
                params.height = mFinalSize;
                // 设置位置,最后显示时相当于变换后的头像位置
                params.gravity = Gravity.BOTTOM;
                params.leftMargin = (int) mFinalX;
                params.bottomMargin = mFinalViewMarginBottom;
                mFinalView.setLayoutParams(params);
                mFinalView.setImageDrawable(child.getDrawable());
                mFinalView.setBorderColor(child.getBorderColor());
                int borderWidth = (int) ((mFinalSize * 1.0f / mOriginalSize) * child.getBorderWidth());
                mFinalView.setBorderWidth(borderWidth);
            }
        }

        return true;
    }

    /**
     * 初始化变量
     * @param child
     * @param dependency
     */
    private void _initVariables(CircleImageView child, View dependency) {
        if (mAppBarHeight == 0) {
            mAppBarHeight = dependency.getHeight();
            mAppBarStartY = dependency.getY();
        }
        if (mTotalScrollRange == 0) {
            mTotalScrollRange = ((AppBarLayout) dependency).getTotalScrollRange();
        }
        if (mOriginalSize == 0) {
            mOriginalSize = child.getWidth();
        }
        if (mFinalSize == 0) {
            mFinalSize = mContext.getResources().getDimensionPixelSize(R.dimen.avatar_final_size);
        }
        if (mAppBarWidth == 0) {
            mAppBarWidth = dependency.getWidth();
        }
        if (mOriginalX == 0) {
            mOriginalX = child.getX();
        }
        if (mFinalX == 0) {
            mFinalX = mContext.getResources().getDimensionPixelSize(R.dimen.avatar_final_x);
        }
        if (mOriginalY == 0) {
            mOriginalY = child.getY();
        }
        if (mFinalY == 0) {
            if (mToolBarHeight == 0) {
                mToolBarHeight = mContext.getResources().getDimensionPixelSize(R.dimen.toolbar_height);
            }
            mFinalY = (mToolBarHeight - mFinalSize) / 2 + mAppBarStartY;
        }
        if (mScaleSize == 0) {
            mScaleSize = (mOriginalSize - mFinalSize) * 1.0f / 2;
        }
        if (mFinalViewMarginBottom == 0) {
            mFinalViewMarginBottom = (mToolBarHeight - mFinalSize) / 2;
        }
    }
}
上一篇 下一篇

猜你喜欢

热点阅读