CoordinatorLayout中各种控件实现Demo

一、简述CoordinatorLayout

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

1018039-920fa0d9d86fa68b.gif

下面直接贴网上一些经典Demo

例子一:底部菜单抽屉效果
1 添加依赖
compile 'com.android.support:appcompat-v7:25.1.1'
2 xml




    
        
        
    

    
    


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

    
        
    

    
         
    


属性介绍
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才能正常展现


app:layout_scrollFlags="scroll|enterAlways|enterAlwaysCollapsed"
如果你的AppBarLayout中包含其他的View,那么含有layout_scrollFlags的标签的View请布局在前面。因为AppBarLayout实际上是一个LinearLayout。
例子三:折叠的Toolbar

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

1 xml


    

        
            
            
          

    

    
        
    


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上不会显示

例子四:同步移动

先看效果


CoordinatorLayout中各种控件实现Demo_第1张图片
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

效果如下


CoordinatorLayout中各种控件实现Demo_第2张图片
2800913-9d0211e3e8802f09.gif
三、其他各种效果Demo
1、BackBehavior

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


CoordinatorLayout中各种控件实现Demo_第3张图片
1018039-5d44d427cea2929e.gif
2、项目可用的Demo

CoordinatorLayoutSample
CoordinatorBehaviorExample

CoordinatorLayout中各种控件实现Demo_第4张图片
2267876-d4f42ee04a3d2760.gif

附上关键源码

xml


    

        

            

            
            
        
    

    

        
    

    
    

layout_content_tom.xml




    

        

            

            
        

        

            

            
        

        

            

            
        

        
    


AvatarBehavior
public class AvatarBehavior extends CoordinatorLayout.Behavior {

    // 缩放动画变化的支点
    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;
        }
    }
}

你可能感兴趣的:(CoordinatorLayout中各种控件实现Demo)