Я создал пример примера для достижения нижнего листа в виде направления Google, которое будет плавать на любом пользовательском интерфейсе, и работа будет такой же, как в режиме просмотра направления Google.Я добился этого с помощью класса "ViewDragHelper" (https://developer.android.com/reference/android/support/v4/widget/ViewDragHelper)
) Вот пример того, чего я достиг с "ViewDragHelper" так же, как в Google Swipable View:
Примечание: В приведенном ниже примере есть жестко закодированные строки, а также один адаптер, взятый только в классе Swipable View, а также в статическом списке. Любой может настроить его с соответствующим форматом кода, а также getter /setters. Это только для того, чтобы научить, как работает «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);
}
}
Создайте файл XML с именем «rawitem_mapdetails».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>
Создайте простой адаптер с именем" 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();
}
});
}
}
}
Создайте файл" activity_main.xml ", как показано ниже для MainActivity, какниже:
<?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. Чтобы получить скользящую панель внизу / скрытую как состояние по умолчанию для view создан первый раз
Сначала возьмите глобальную логическую переменную 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».Я также сделал один простой щелчок на предмете повторного просмотра, чтобы любой мог лучше понять, что другой пользовательский интерфейс будет работать так же, как и его поведение.Мы также можем настроить, поместив любой пользовательский интерфейс в «GoogleBottomSheet».
Надеюсь, это поможет!Happy Coding:)