ListView в BottomSheet
Я столкнулся с проблемой, когда у меня было простое ListView
в BottomSheet
и ListView
было достаточно элементов для заполнения экрана и прокрутки еще больше.
Когда я прокручиваю вниз, все работает, однако, когда я пытался прокручивать назад, он прокручивал сам BottomSheet
и закрывал представление, а не просто прокручивал ListView
.
Мне удалось найти решение через некоторое время, и поскольку я не мог найти его здесь, я решил, что разместил его здесь.
Ответы
Ответ 1
Решение состоит в том, чтобы расширить ListView
следующим образом:
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;
}
}
Затем в вашем файле макета 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>
Затем, наконец, в 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();
Это обеспечит BottomSheet
, который полностью работает с прокруткой ListView
.
Ответ 2
Есть лучший подход, если вы не хотите расширять 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;
}
});
Ответ 3
Это правильный пользовательский класс представления списка с сенсорным событием
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;
}
}
Ответ 4
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;
}
}
Ответ 5
Я знаю это немного взломать, но это сработало для меня, и даже listview можно выбрать.
Как мы знаем, когда когда-либо нижний лист меняет свое состояние, его можно поймать в этом слушателе, поэтому не позволяйте ему изменять состояние, если listview не касается вершины, впредь сенсорное событие будет передано в listview и будет работать так, как оно есть.
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);
}
Ответ 6
Начиная с версии 22.1.0, вы можете попробовать setNestedScrollingEnabled=true
Если для этого свойства установлено значение true, представлению будет разрешено инициировать вложенные операции прокрутки с совместимым родительским представлением в текущей иерархии. Если это представление не реализует вложенную прокрутку, это не будет иметь никакого эффекта. Отключение вложенной прокрутки во время выполнения вложенной прокрутки приводит к остановке вложенной прокрутки.
Ссылка на Google API