我在Android中面临1个有关BottomSheet的问题。我想要达到的目标:
但是我无法像Google那样用页眉和页脚实现ListView。
主要问题是ListView显示所有项目,而我的底部工作表未显示页脚视图。我想始终看到底部工作表的底视图。
答案 0 :(得分:3)
如果页脚是您的主要问题,这是一个简单的解决方案:
<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>
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
答案 1 :(得分:0)
我创建了示例示例,以实现将底部表格作为Google的浮动方向 在任何用户界面和工作方式都将与Google方向可滑动查看相同。我用 “ ViewDragHelper”类(https://developer.android.com/reference/android/support/v4/widget/ViewDragHelper)
以下是我使用“ ViewDragHelper”与Google方向可滑动查看相同的示例:
注意:在下面的示例中,有硬编码的字符串以及单个 适配器仅在可滑动查看的类中使用,还在静态列表中使用。 任何人都可以使用适当的代码格式对其进行自定义 吸气剂/设定者。例如,这仅是为了教 “ 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”中进行自定义。
希望有帮助!快乐编码:)