Android 仿高德地图可拉伸的BottomSheet

项目上要用到类似高德地图搜索结果后的结果展示的可拉伸控件。


由于没有找到合适的软件,制作的效果有点差,将就看下。

自己的效果:
Android 仿高德地图可拉伸的BottomSheet_第1张图片

虽然说效果没有高德的好(就是在下滑的时候不能准确折叠到中间),但是基本满足了项目的需要。如果各位大神有更好的方法,欢迎留言提供方法。

刚开始以为这是抽屉效果(slidingdrawer),结果发现这个类已经被谷歌抛弃,且content也不能滑动,得自定义控件实现。但是我的自定义控件水平不高,苦思一天还是写不出来,只能各种查类似的效果,终于找到了比较接近的CoordinatorLayout。

@deprecated 
This class is not supported anymore. It is recommended you base your own implementation on the source code for the Android Open Source Project if you must use it in your application.

关于CoordinatorLayout — 协调布局 这个新控件我了解的不多,等我找找资料后再写一篇文章。我就先说说效果的实现。
布局文件:


<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:id="@+id/coordinator"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#88bbbbbb"
    android:gravity="center"
    tools:context="com.joysuch.collapseviewdemo.MainActivity">

    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/girl"/>

    <RelativeLayout
        android:id="@+id/design_bottom_sheet_bar"
        android:layout_width="match_parent"
        android:layout_height="?attr/actionBarSize"
        android:background="@color/colorAccent"
        app:layout_anchor="@+id/design_bottom_sheet"
        app:layout_anchorGravity="top"
        android:layout_gravity="bottom"
        android:visibility="invisible">
        <ImageView
            android:layout_width="23dp"
            android:layout_height="23dp"
            android:layout_marginLeft="23dp"
            android:src="@mipmap/ic_launcher"
            android:layout_centerVertical="true"/>
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="点击收起BottomSheet"
            android:textColor="#ffffff"
            android:layout_centerInParent="true"/>
    RelativeLayout>

    <RelativeLayout
        android:id="@+id/design_bottom_sheet"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:minHeight="100dp"
        app:behavior_peekHeight="56dp"
        app:behavior_hideable="false"
        app:layout_behavior="@string/bottom_sheet_behavior"
        android:background="#ffffff">
        <TextView
            android:id="@+id/bottom_sheet_tv"
            android:layout_width="wrap_content"
            android:layout_height="56dp"
            android:layout_centerHorizontal="true"
            android:gravity="center"
            android:text="这是一个BottomSheet"/>
        <android.support.v7.widget.RecyclerView
            android:id="@+id/bottom_sheet_rv"
            android:layout_below="@+id/bottom_sheet_tv"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_centerInParent="true"
            android:padding="10dp"
            android:minHeight="100dp"
            android:adjustViewBounds="true"
            android:scaleType="centerInside"
            android:layout_gravity="center"/>
    RelativeLayout>

android.support.design.widget.CoordinatorLayout>

最主要的一个布局就是id为design_bottom_sheet的布局,它设置了

app:layout_behavior="@string/bottom_sheet_behavior"

的属性,将布局作为底部折叠纸片。

app:behavior_hideable="false" 让这个BottomSheet不可以被手动滑动隐藏,设置为true则可以滑到屏幕最底部隐藏。

app:behavior_peekHeight设置的是折叠状态时的高度,此时设置的折叠时的高度和子View的TextView的高度一样高,所以折叠时将显示TextView的内容。

id为design_bottom_sheet_bar的RelativeLayout是用来实现BottomSheet完全展开时的自定义顶部工具条。

app:layout_anchor="@+id/design_bottom_sheet"
 app:layout_anchorGravity="top"
 android:layout_gravity="bottom"

上面这段设置是使design_bottom_sheet_bar的位置与design_bottom_sheet锚定,锚定在design_bottom_sheet的上方.

design_bottom_sheet设置的高度是充满父布局,在展开状态下我们需要其给design_bottom_sheet_bar留出位置,所以要修改一下design_bottom_sheet的高度,代码设置放在了onWindowFocusChanged()里。

    @Override
    public void onWindowFocusChanged(boolean hasFocus) {
        super.onWindowFocusChanged(hasFocus);
        //修改design_bottom_sheet的高度 让其显示总布局一半的高度
        if(!setBottomSheetHeight){
            CoordinatorLayout.LayoutParams params =
                    (CoordinatorLayout.LayoutParams) design_bottom_sheet.getLayoutParams();
            params.height = coordinatorLayout.getHeight() / 2;
            design_bottom_sheet.setLayoutParams(params);
            setBottomSheetHeight = true ;
        }
    }
// 协调布局
        coordinatorLayout = (CoordinatorLayout) findViewById(R.id.coordinator);
        // 伸展后显示类似 ActionBar
        design_bottom_sheet_bar = (RelativeLayout) findViewById(R.id.design_bottom_sheet_bar);
        // 折叠后显示的类似handle
        design_bottom_sheet = (RelativeLayout) findViewById(R.id.design_bottom_sheet);
        bottom_sheet_tv = (TextView) findViewById(R.id.bottom_sheet_tv);
        // RecyclerView
        mRecyclerView = (RecyclerView) findViewById(R.id.bottom_sheet_rv);
        initRecyclerView();

        final BottomSheetBehavior behavior = BottomSheetBehavior.from(design_bottom_sheet);
        //// 默认让其展开状态,此时design_bottom_sheet的高度是总布局高度的一半,正好显示在中间。
        behavior.setState(BottomSheetBehavior.STATE_EXPANDED);
        bottom_sheet_tv.setVisibility(View.GONE);

//点击上方的锚定条---折叠
        design_bottom_sheet_bar.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                behavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
            }
        });

        behavior.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {

            //BottomSheet状态改变时的回调
            @Override
            public void onStateChanged(@NonNull View bottomSheet, int newState) {
                // collapsed.
                if(newState!=BottomSheetBehavior.STATE_COLLAPSED && bottom_sheet_tv.getVisibility()==View.VISIBLE){
                    bottom_sheet_tv.setVisibility(View.GONE);
                    mRecyclerView.setVisibility(View.VISIBLE);
                }else if(newState==BottomSheetBehavior.STATE_COLLAPSED&& bottom_sheet_tv.getVisibility()==View.GONE){
                    bottom_sheet_tv.setVisibility(View.VISIBLE);
                    mRecyclerView.setVisibility(View.GONE);
                }
            }

            //BottomSheet滑动时的回调
            @Override
            public void onSlide(@NonNull View bottomSheet, float slideOffset) {

                if(bottomSheet.getTop() > coordinatorLayout.getHeight() /2){
                    CoordinatorLayout.LayoutParams params =
                            (CoordinatorLayout.LayoutParams) design_bottom_sheet.getLayoutParams();
                    params.height = coordinatorLayout.getHeight() / 2;
                    design_bottom_sheet.setLayoutParams(params);
                }else {
                    CoordinatorLayout.LayoutParams params =
                            (CoordinatorLayout.LayoutParams) design_bottom_sheet.getLayoutParams();
                    params.height = coordinatorLayout.getHeight() - design_bottom_sheet_bar.getHeight();
                    design_bottom_sheet.setLayoutParams(params);
                }

                if(bottomSheet.getTop()<2*design_bottom_sheet_bar.getHeight()){
                    design_bottom_sheet_bar.setVisibility(View.VISIBLE);
                    design_bottom_sheet_bar.setAlpha(slideOffset);
                    design_bottom_sheet_bar.setTranslationY(bottomSheet.getTop()-2*design_bottom_sheet_bar.getHeight());
                }
                else{
                    design_bottom_sheet_bar.setVisibility(View.INVISIBLE);
                }
            }
        });

BottomSheet 的状态回调有两个方法:onStateChanged:状态改变是我们要控制控件的显示与隐藏。onSlide:滑动时:当 bottomSheet.getTop() > coordinatorLayout.getHeight() /2这个节点设置design_bottom_sheet的高度是可行的,其他情况均出现问题。滑动时设置锚定条的移动距离,让其显示出来。

根据我的coordinatorLayout.getHeight() /2这个节点我手动测试发现,在bottomSheet.getTop() >2*coordinatorLayout.getHeight() /3时是折叠区间。coordinatorLayout.getHeight() /2 ~2 * coordinatorLayout.getHeight() /3是展示在中间的区间,其他情况展示在顶部。

好吧,我就这样实现了,好像是卡了一个Bug实现了。源码实在太难理清逻辑,有点遗憾没有完美解决下滑的时候不能准确折叠到中间(像高德的那种体验),欢迎各位大神提供此控件实现方法或者改进方法。以上属于自己理解,欢迎指正!
项目下载:https://github.com/GoldenStrawberry/MaterialDesignDemo

你可能感兴趣的:(android)