Android中底部的可滚动底部内容

时间:2019-04-06 12:05:39

标签: android google-maps bottom-sheet

我在Android中面临1个有关BottomSheet的问题。我想要达到的目标:

BottomSheet Google Direction

但是我无法像Google那样用页眉和页脚实现ListView。

主要问题是ListView显示所有项目,而我的底部工作表未显示页脚视图。我想始终看到底部工作表的底视图。

2 个答案:

答案 0 :(得分:3)

如果页脚是您的主要问题,这是一个简单的解决方案:

  1. 将页脚视图添加为BottomSheet的直接子项
<androidx.coordinatorlayout.widget.CoordinatorLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <FrameLayout
        android:id="@+id/bottom_sheet"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="#EECCCCCC"
            app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior">

        <TextView
            android:id="@+id/pinned_bottom"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="#500000FF"
            android:padding="16dp"
            android:text="Bottom" />
    </FrameLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
  1. 添加一个BottomSheetCallback并在onSlide()中调整页脚的translationY
BottomSheetBehavior.from(bottom_sheet).addBottomSheetCallback(
    object : BottomSheetBehavior.BottomSheetCallback() {

        override fun onSlide(bottomSheet: View, slideOffset: Float) {
            val bottomSheetVisibleHeight = bottomSheet.height - bottomSheet.top

            pinned_bottom.translationY =
                (bottomSheetVisibleHeight - pinned_bottom.height).toFloat()
        }

        override fun onStateChanged(bottomSheet: View, newState: Int) {
        }
    })

运行平稳,因为您只需更改translationY。

您还可以使用此技术将视图固定在BottomSheet的中心:

pinned_center.translationY = (bottomSheetVisibleHeight - pinned_center.height) / 2f

我已经在GitHub上创建了一个示例项目来重现两个用例(固定在中心和底部):https://github.com/dadouf/BottomSheetGravity

Demo

答案 1 :(得分:0)

我创建了示例示例,以实现将底部表格作为Google的浮动方向 在任何用户界面和工作方式都将与Google方向可滑动查看相同。我用 “ ViewDragHelper”类(https://developer.android.com/reference/android/support/v4/widget/ViewDragHelper

以下是我使用“ ViewDragHelper”与Google方向可滑动查看相同的示例:

enter image description here

  

注意:在下面的示例中,有硬编码的字符串以及单个   适配器仅在可滑动查看的类中使用,还在静态列表中使用。   任何人都可以使用适当的代码格式对其进行自定义   吸气剂/设定者。例如,这仅是为了教   “ ViewDragHelper”有效。

首先创建“ GoogleBottomSheet”类,如下所示:

import android.content.Context;
import android.support.v4.view.MotionEventCompat;
import android.support.v4.view.ViewCompat;
import android.support.v4.widget.ViewDragHelper;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;

import java.util.ArrayList;

public class GoogleBottomSheet extends ViewGroup {

    private final ViewDragHelper mDragHelper;
    GoogleRoutesAdapter googleRoutesAdapter;

    private View mHeaderView;
    private RecyclerView rvList;

    private float mInitialMotionX;
    private float mInitialMotionY;

    private int mDragRange;
    private int mTop;
    private float mDragOffset;


    public GoogleBottomSheet(Context context) {
        this(context, null);
    }

    public GoogleBottomSheet(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        mHeaderView = findViewById(R.id.viewHeader);
        rvList = findViewById(R.id.rvList);
        LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getContext(), LinearLayoutManager.VERTICAL, false);
        rvList.setLayoutManager(linearLayoutManager);

        ArrayList<String> allRoutesList = new ArrayList<>();
        allRoutesList.add("47 Bourbon Li");
        allRoutesList.add("Head South");
        allRoutesList.add("Princess Street");
        allRoutesList.add("A 3-lane partially one-way street heading out of Manchester city centre");
        allRoutesList.add("Manchester Jewish Museum, \n" +
                "Peninsula Building");
        allRoutesList.add("Portland Street");
        allRoutesList.add("Quay Street");
        allRoutesList.add("Forms part of the city's historic Northern Quarter district");
        allRoutesList.add("Sackville Street Building, University of Manchester including the Godlee Observatory");
        allRoutesList.add("Turn Left on S Naper");
        allRoutesList.add("150 W-Stall");
        allRoutesList.add("Former National Westminster Bank");
        allRoutesList.add("Bradshaw, L. D.");
        allRoutesList.add("House of Commons Transport Committee");
        allRoutesList.add("A street only for Metrolink trams and previously buses which joined the street at Lower Mosley Street.");
        googleRoutesAdapter = new GoogleRoutesAdapter(getContext(), allRoutesList);
        rvList.setAdapter(googleRoutesAdapter);
    }

    public GoogleBottomSheet(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        mDragHelper = ViewDragHelper.create(this, 1f, new DragHelperCallback());
    }

    public void maximize() {
        smoothSlideTo(0f);
    }

    boolean smoothSlideTo(float slideOffset) {
        final int topBound = getPaddingTop();
        int y = (int) (topBound + slideOffset * mDragRange);

        if (mDragHelper.smoothSlideViewTo(mHeaderView, mHeaderView.getLeft(), y)) {
            ViewCompat.postInvalidateOnAnimation(this);
            return true;
        }
        return false;
    }

    private class DragHelperCallback extends ViewDragHelper.Callback {

        @Override
        public boolean tryCaptureView(View child, int pointerId) {
            return child == mHeaderView;
        }

        @Override
        public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
            mTop = top;

            mDragOffset = (float) top / mDragRange;

            requestLayout();
        }

        @Override
        public void onViewReleased(View releasedChild, float xvel, float yvel) {
            int top = getPaddingTop();
            if (yvel > 0 || (yvel == 0 && mDragOffset > 0.5f)) {
                top += mDragRange;
            }
            mDragHelper.settleCapturedViewAt(releasedChild.getLeft(), top);
        }

        @Override
        public int getViewVerticalDragRange(View child) {
            return mDragRange;
        }

        @Override
        public int clampViewPositionVertical(View child, int top, int dy) {
            final int topBound = getPaddingTop();
            final int bottomBound = getHeight() - mHeaderView.getHeight();

            final int newTop = Math.min(Math.max(top, topBound), bottomBound);
            return newTop;
        }

    }

    @Override
    public void computeScroll() {
        if (mDragHelper.continueSettling(true)) {
            ViewCompat.postInvalidateOnAnimation(this);
        }
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        final int action = MotionEventCompat.getActionMasked(ev);

        if ((action != MotionEvent.ACTION_DOWN)) {
            mDragHelper.cancel();
            return super.onInterceptTouchEvent(ev);
        }

        if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
            mDragHelper.cancel();
            return false;
        }

        final float x = ev.getX();
        final float y = ev.getY();
        boolean interceptTap = false;

        switch (action) {
            case MotionEvent.ACTION_DOWN: {
                mInitialMotionX = x;
                mInitialMotionY = y;
                interceptTap = mDragHelper.isViewUnder(mHeaderView, (int) x, (int) y);
                break;
            }

            case MotionEvent.ACTION_MOVE: {
                final float adx = Math.abs(x - mInitialMotionX);
                final float ady = Math.abs(y - mInitialMotionY);
                final int slop = mDragHelper.getTouchSlop();
                if (ady > slop && adx > ady) {
                    mDragHelper.cancel();
                    return false;
                }
            }
        }

        return mDragHelper.shouldInterceptTouchEvent(ev) || interceptTap;
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        mDragHelper.processTouchEvent(ev);

        final int action = ev.getAction();
        final float x = ev.getX();
        final float y = ev.getY();

        boolean isHeaderViewUnder = mDragHelper.isViewUnder(mHeaderView, (int) x, (int) y);
        switch (action & MotionEventCompat.ACTION_MASK) {
            case MotionEvent.ACTION_DOWN: {
                mInitialMotionX = x;
                mInitialMotionY = y;
                break;
            }

            case MotionEvent.ACTION_UP: {
                final float dx = x - mInitialMotionX;
                final float dy = y - mInitialMotionY;
                final int slop = mDragHelper.getTouchSlop();
                if (dx * dx + dy * dy < slop * slop && isHeaderViewUnder) {
                    if (mDragOffset == 0) {
                        smoothSlideTo(1f);
                    } else {
                        smoothSlideTo(0f);
                    }
                }
                break;
            }
        }


        return isHeaderViewUnder && isViewHit(mHeaderView, (int) x, (int) y) ||
                isViewHit(rvList, (int) x, (int) y);
    }


    private boolean isViewHit(View view, int x, int y) {
        int[] viewLocation = new int[2];
        view.getLocationOnScreen(viewLocation);
        int[] parentLocation = new int[2];
        this.getLocationOnScreen(parentLocation);
        int screenX = parentLocation[0] + x;
        int screenY = parentLocation[1] + y;
        return screenX >= viewLocation[0] && screenX < viewLocation[0] + view.getWidth() &&
                screenY >= viewLocation[1] && screenY < viewLocation[1] + view.getHeight();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        measureChildren(widthMeasureSpec, heightMeasureSpec);

        int maxWidth = MeasureSpec.getSize(widthMeasureSpec);
        int maxHeight = MeasureSpec.getSize(heightMeasureSpec);

        setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, 0),
                resolveSizeAndState(maxHeight, heightMeasureSpec, 0));
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        mDragRange = getHeight() - mHeaderView.getHeight();

        mHeaderView.layout(
                0,
                mTop,
                r,
                mTop + mHeaderView.getMeasuredHeight());

        rvList.layout(
                0,
                mTop + mHeaderView.getMeasuredHeight(),
                r,
                mTop + b);
    }
}

为recyclerview Viewholder项目创建名为“ rawitem_mapdetails.xml”的xml文件,如下所示:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <TextView
            android:id="@+id/mTextView"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="@color/colorPrimaryDark"
            android:text="Route 1"
            android:padding="@dimen/_10sdp"
            android:textColor="@android:color/white"
            android:textSize="@dimen/_15sdp" />

    </RelativeLayout>

</LinearLayout>

为recyclerview创建名为“ GoogleRoutesAdapter”的简单适配器,如下所示:

import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import android.widget.Toast;

import java.util.ArrayList;

public class GoogleRoutesAdapter extends RecyclerView.Adapter<GoogleRoutesAdapter.GoogleRouteViewHolder> {

    private Context mContext;
    private ArrayList<String> allRoutesList;

    public GoogleRoutesAdapter(Context context, ArrayList<String> allRoutesList) {
        this.mContext = context;
        this.allRoutesList = allRoutesList;
    }

    @Override
    public GoogleRouteViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View layoutView = LayoutInflater.from(parent.getContext()).inflate(R.layout.rawitem_mapdetails, null);
        GoogleRouteViewHolder rcv = new GoogleRouteViewHolder(layoutView);
        return rcv;
    }

    @Override
    public void onBindViewHolder(final GoogleRouteViewHolder holder, final int position) {
        holder.tvRoute.setText(allRoutesList.get(position));
    }

    @Override
    public int getItemCount() {
        return allRoutesList.size();
    }


    public class GoogleRouteViewHolder extends RecyclerView.ViewHolder {
        private TextView tvRoute;

        public GoogleRouteViewHolder(View view) {
            super(view);
            tvRoute = view.findViewById(R.id.mTextView);
            tvRoute.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    Toast.makeText(mContext, allRoutesList.get(getAdapterPosition()), Toast.LENGTH_SHORT).show();
                }
            });
        }

    }

}

为MainActivity如下创建“ activity_main.xml”:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <com.viewdraghelper.GoogleBottomSheet
        android:id="@+id/my_googleBottomSheet"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <TextView
            android:id="@+id/viewHeader"
            android:layout_width="match_parent"
            android:background="@color/colorAccent"
            android:layout_height="@dimen/_80sdp"
            android:textSize="@dimen/_25sdp"
            android:padding="@dimen/_10sdp"
            android:textColor="@android:color/white"
            android:text="31 min (29 mi)"/>

        <android.support.v7.widget.RecyclerView
            android:id="@+id/rvList"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />

    </com.viewdraghelper.GoogleBottomSheet>

</RelativeLayout>
  

根据以下要求编辑答案:

1。要使滑动面板位于底部/隐藏为首次创建的视图的默认状态

首先获取initOnce全局布尔变量

private boolean initOnce = false;

然后更改onLayout()方法,如下所示:

@Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        if(!initOnce){
            initOnce = true;
            mDragRange = getHeight() - mHeaderView.getHeight();
            mHeaderView.layout(
                    0,
                    b - mHeaderView.getMeasuredHeight(),
                    r,
                    b);
        }else {
            mDragRange = getHeight() - mHeaderView.getHeight();

            mHeaderView.layout(
                    0,
                    mTop,
                    r,
                    mTop + mHeaderView.getMeasuredHeight());

            rvList.layout(
                    0,
                    mTop + mHeaderView.getMeasuredHeight(),
                    r,
                    mTop + b);
        }
    }

现在全部完成!正如我上面所说的,这只是教“ ViewDragHelper”如何工作,这就是为什么我们不这样做 现在必须在MainActivity中执行任何操作,因为所有适配器逻辑都位于“ GoogleBottomSheet”类中。 我还单击了一个简单的recyclerview项,以便任何人都可以更好地了解其他ui可以正常使用 作为自己的行为。我们还可以通过将任何ui放在“ GoogleBottomSheet”中进行自定义。

希望有帮助!快乐编码:)