web-dev-qa-db-fra.com

ListView en bas de la feuille

J'ai rencontré un problème où j'avais une simple ListView dans une BottomSheet et ListView avait assez d'éléments pour remplir l'écran et faire défiler encore plus.

Lorsque je fais défiler l'écran, tout semble fonctionner. Cependant, lorsque j'ai essayé de revenir en arrière, il s'agissait de faire défiler la BottomSheet elle-même et de fermer la vue au lieu de simplement faire défiler la ListView.

J'ai été capable de trouver une solution après un moment et comme je ne pouvais pas la trouver nulle part ici, j'ai pensé la poster ici.

12

La solution consiste à étendre la ListView comme ceci: 

public class BottomSheetListView extends ListView {
    public BottomSheetListView (Context context, AttributeSet p_attrs) {
        super (context, p_attrs);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        return true;
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        if (canScrollVertically(this)) {
            getParent().requestDisallowInterceptTouchEvent(true);
        }
        return super.onTouchEvent(ev);
    }

    public boolean canScrollVertically (AbsListView view) {
        boolean canScroll = false;

        if (view !=null && view.getChildCount ()> 0) {
            boolean isOnTop = view.getFirstVisiblePosition() != 0 || view.getChildAt(0).getTop() != 0;
            boolean isAllItemsVisible = isOnTop && view.getLastVisiblePosition() == view.getChildCount();

            if (isOnTop || isAllItemsVisible) {
                canScroll = true;
            }
        }

        return  canScroll;
    }
}

Puis dans votre fichier de mise en page bottom_sheet_view.xml:

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

    <com.mypackage.name.BottomSheetListView
        Android:id="@+id/listViewBtmSheet"
        Android:divider="@color/colorPrimary"
        Android:dividerHeight="1dp"
        Android:layout_weight="1"
        Android:layout_width="match_parent"
        Android:layout_height="0dp" />

</LinearLayout>

Enfin, dans votre Activity/Fragment:

BottomSheetDialog dialog = new BottomSheetDialog(context);
dialog.setContentView(R.layout.bottom_sheet_view);

BottomSheetListView listView = (BottomSheetListView) dialog.findViewById(R.id.listViewBtmSheet);
// apply some adapter - add some data to listview

dialog.show();

Ceci fournira une BottomSheet qui fonctionne entièrement avec le ListView scroll.

22

Il existe une meilleure approche si vous ne souhaitez pas développer la ListView:

//in onCreate

_listView.setOnTouchListener(new ListView.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                int action = event.getAction();
                switch (action) {
                    case MotionEvent.ACTION_DOWN:
                        // Disallow NestedScrollView to intercept touch events.
                        v.getParent().requestDisallowInterceptTouchEvent(true);
                        break;

                    case MotionEvent.ACTION_UP:
                        // Allow NestedScrollView to intercept touch events.
                        v.getParent().requestDisallowInterceptTouchEvent(false);
                        break;
                }

                // Handle ListView touch events.
                v.onTouchEvent(event);
                return true;
            }
        });
4
okkko
public class BottomSheetListView extends ListView {

    public BottomSheetListView(Context context, AttributeSet attributeSet) {
        super(context, attributeSet);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent motionEvent) {
        View view = (View) getChildAt(getChildCount() - 1);

        int diffBottom = (view.getBottom() - (getHeight() + getScrollY()));
        if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) {
            if (diffBottom == 0) {
                return false;
            }
        }

         /*//Need more improvement on this logic. Do not uncomment
        int diffTop = (view.getTop() - (getHeight() + getScrollY()));
        if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) {
            if (diffTop < 0) {
                return true;
            }
        }*/

        return super.onInterceptTouchEvent(motionEvent);
    }

    @Override
    public boolean onTouchEvent(MotionEvent motionEvent) {
        if (canScrollVertically(this)) {
            getParent().requestDisallowInterceptTouchEvent(true);
        }
        return super.onTouchEvent(motionEvent);
    }

    public boolean canScrollVertically(AbsListView absListView) {

        boolean canScroll = false;

        if (absListView != null && absListView.getChildCount() > 0) {

            boolean isOnTop = absListView.getFirstVisiblePosition() != 0 || absListView.getChildAt(0).getTop() != 0;
            boolean isAllItemsVisible = isOnTop && getLastVisiblePosition() == absListView.getChildCount();

            if (isOnTop || isAllItemsVisible)
                canScroll = true;
        }

        return canScroll;
    }
}
3
sUndeep

Ceci est la classe personnalisée de la liste avec la gestion des événements tactiles

public class BottomSheetListView extends ListView
{
    public BottomSheetListView(Context context, AttributeSet p_attrs)
    {
        super(context, p_attrs);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev)
    {
        if (canScrollVertically(this))
        {
            getParent().requestDisallowInterceptTouchEvent(true);
        }
        return super.onInterceptTouchEvent(ev);
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev)
    {
        if (canScrollVertically(this))
        {
            getParent().requestDisallowInterceptTouchEvent(true);
        }
        return super.onTouchEvent(ev);
    }

    public boolean canScrollVertically(AbsListView view)
    {
        boolean canScroll = false;

        if (view != null && view.getChildCount() > 0)
        {
            boolean isOnTop = view.getFirstVisiblePosition() != 0 || view.getChildAt(0).getTop() != 0;
            boolean isAllItemsVisible = isOnTop && view.getLastVisiblePosition() == view.getChildCount();

            if (isOnTop || isAllItemsVisible)
            {
                canScroll = true;
            }
        }

        return canScroll;
    }
}
2
Nauman Afzaal

Je sais que c'est un peu bidouillé mais cela a fonctionné pour moi, et même listview peut être sélectionné . Comme nous savons que chaque fois que la feuille de fond change d'état, elle peut être interceptée dans son auditeur, ne la laissez pas changer par conséquent si listview ne touche pas en haut, l’événement tactile sera dorénavant passé à listview et fonctionnera comme il le fait.

    mBottomSheetBehavior.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback(){
@Override
public void onStateChanged(View bottomSheet, int newState) {
        if (newState == BottomSheetBehavior.STATE_DRAGGING && !listIsAtTop()){
            mBottomSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);
        }
}
@Override
public void onSlide(View bottomSheet, float slideOffset) {}});

public boolean listIsAtTop()   {
    if(tripListView.getChildCount() == 0) return true;
    return (tripListView.getChildAt(0).getTop() == 0 && tripListView.getFirstVisiblePosition() ==0);
}
0
Prateek Gupta