Прокручиваемый контент внизу в середине в Android - PullRequest
0 голосов
/ 06 апреля 2019

У меня 1 проблема с BottomSheet в Android.Чего я пытаюсь достичь:

BottomSheet Google Direction

Но я не могу реализовать ListView с верхним и нижним колонтитулом, как Google.

Основная проблема заключается в том, что ListView показывает все элементы, а мой нижний лист не отображает нижний колонтитул.Я хочу всегда видеть нижний вид моего нижнего листа.

1 Ответ

0 голосов
/ 06 апреля 2019

Я создал пример примера для достижения нижнего листа в виде направления Google, которое будет плавать на любом пользовательском интерфейсе, и работа будет такой же, как в режиме просмотра направления Google.Я добился этого с помощью класса "ViewDragHelper" (https://developer.android.com/reference/android/support/v4/widget/ViewDragHelper)

) Вот пример того, чего я достиг с "ViewDragHelper" так же, как в Google Swipable View:

enter image description here

Примечание: В приведенном ниже примере есть жестко закодированные строки, а также один адаптер, взятый только в классе 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:)

...