Прокрутка вверх не работает с SwipeRefreshLayout в Listview
Я хочу реализовать прокрутку для обновления функций с помощью listView. Также есть другие элементы представления в том же файле макета, которые отображаются, если список пуст. Вот мой файл макета. Проблема в том, что когда я прокручиваю вниз, а затем пытаюсь прокручивать вверх, а не прокручивать весь путь до вершины, а затем обновлять, он просто обновляется и прокрутка вверх не работает.
<android.support.v4.widget.SwipeRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/swipe_container"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent" >
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="64dp"
android:paddingLeft="16dp"
android:paddingRight="16dp" >
<ImageView
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true"
android:src="@drawable/inbox_empty" />
<TextView
android:id="@+id/noEventsText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginLeft="16dp"
android:layout_toRightOf="@+id/noEventsIcon" />
<View
android:id="@+id/divider"
android:layout_width="match_parent"
android:layout_height="1px"
android:layout_alignParentBottom="true"
android:layout_marginLeft="4dp"
android:layout_marginRight="4dp"
android:background="@color/dividerOnBlack" />
</RelativeLayout>
<ListView
android:id="@+id/list_items"
android:layout_width="match_parent"
android:layout_height="fill_parent"
android:cacheColorHint="@android:color/transparent"
android:choiceMode="multipleChoice"
android:descendantFocusability="afterDescendants"
android:divider="@android:color/transparent"
android:dividerHeight="0px"
android:scrollbars="none" />
</LinearLayout>
</android.support.v4.widget.SwipeRefreshLayout>
Это мой метод onScroll.
@Override
public void onScroll(AbsListView view,int firstVisibleItem,int visibleItemCount,int totalItemCount) {
// If the total item count is zero and the previous isn't, assume the
// list is invalidated and should be reset back to initial state
if (totalItemCount < previousTotalItemCount) {
this.currentPage = this.startingPageIndex;
this.previousTotalItemCount = totalItemCount;
if (totalItemCount == 0) { this.loading = true; }
}
// If it still loading, we check to see if the dataset count has
// changed, if so we conclude it has finished loading and update the current page
// number and total item count.
if (loading && (totalItemCount > previousTotalItemCount)) {
loading = false;
previousTotalItemCount = totalItemCount;
currentPage++;
}
// If reverse then the firstVisibleItem is calculated wrong
if (reverse) {
firstVisibleItem = totalItemCount - firstVisibleItem;
}
// If it isn't currently loading, we check to see if we have breached
// the visibleThreshold and need to reload more data.
// If we do need to reload some more data, we execute onLoadMore to fetch the data.
if (!loading && (totalItemCount - visibleItemCount)<=(firstVisibleItem + visibleThreshold)) {
onLoadMore(currentPage + 1, totalItemCount);
loading = true;
}
}
Ответы
Ответ 1
Чтобы SwipeRefreshLayout работал, он должен быть прямым родителем вашего ListView, а ListView должен быть первым активным дочерним представлением SwipeRefreshLayout.
В документации для SwipeRefreshLayout указано, что ListView должен быть единственным дочерним, но это нормально, если у него более одного ребенка, если ListView является первым. Это означает, например, что SwipeRefreshLayout будет работать нормально, если вы используете адаптер с представлением "пустой". Например:
<android.support.v4.widget.SwipeRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/swipe_refresh"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".NearbyJobsActivity$PlaceholderFragment">
<ListView
android:id="@android:id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:divider="@color/list_divider"
android:dividerHeight="1dp"
android:listSelector="@drawable/list_row_selector" />
<TextView
android:id="@android:id/empty"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center" />
</android.support.v4.widget.SwipeRefreshLayout>
Если вы можете управлять этим видом макета, тогда SwipeRefreshLayout будет работать нормально, и вам не понадобятся какие-либо обходные пути, перечисленные в других ответах.
Моя собственная проблема заключалась в том, что я загружал свой ListView в качестве фрагмента, поэтому у меня на самом деле было:
<SwipeRefreshLayout>
<FrameLayout> )
<ListView/> \ fragment
<TextView/> /
</FrameLayout> )
</SwipeRefreshLayout>
Итак, SwipeRefreshLayout выбирал FrameLayout как "целевую", а реализация по умолчанию canChildScrollUp()
всегда возвращала false. Как только я переместил SwipeRefreshLayout внутри фрагмента, все начало работать правильно.
<FrameLayout>
<SwipeRefreshLayout> )
<ListView/> \ fragment
<TextView/> /
</SwipeRefreshLayout> )
</FrameLayout>
Ответ 2
У меня была та же проблема и она была решена:
listView = (ListView) findViewById(R.id.listView);
listView.setOnScrollListener(new AbsListView.OnScrollListener() {
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
if (listView.getChildAt(0) != null) {
swipeRefreshLayout.setEnabled(listView.getFirstVisiblePosition() == 0 && listView.getChildAt(0).getTop() == 0);
}
}
});
Ответ 3
Создайте макет, подобный этому.
<android.support.v4.widget.SwipeRefreshLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/swipe_container"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="@string/guides_no_results"
android:id="@+id/empty_view"
android:gravity="center"
android:padding="16dp"
android:fontFamily="sans-serif-light"
android:textSize="20sp"
android:visibility="gone"/>
<ListView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:drawSelectorOnTop="true"
android:divider="@android:color/transparent"
android:dividerHeight="0dp"
android:id="@+id/guides_list" />
</LinearLayout>
Если вы используете это как есть, каждый раз, когда вы просматриваете этот вид, SwipeRefreshLayout запускается и обновляется, что делает ваше приложение неспособным прокручиваться в списке.
Трюк здесь заключается в том, чтобы вручную связать OnScrollListener с ListView вручную. Вы просто проверяете, соответствует ли первая строка первой позиции самого верхнего положения, а затем включите SwipeRefreshLayout. В противном случае отключите его.
guidesList.setOnScrollListener(new AbsListView.OnScrollListener()
{
@Override
public void onScrollStateChanged(AbsListView view, int scrollState)
{
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount)
{
int topRowVerticalPosition = (guidesList == null || guidesList.getChildCount() == 0) ? 0 : guidesList.getChildAt(0).getTop();
swipeContainer.setEnabled(firstVisibleItem == 0 && topRowVerticalPosition >= 0);
}
});
С помощью этого небольшого фрагмента теперь он отлично работает.
Счастливое кодирование!
Источник - http://nlopez.io/swiperefreshlayout-with-listview-done-right/
Ответ 4
Сделайте свою собственную реализацию SwipeRefreshLayout и переопределите canChildScrollUp таким образом:
@Override
public boolean canChildScrollUp() {
if (scrollView != null)
return scrollView.canScrollVertically(-1);
return false;
}
просто замените любой подкласс ScrollView.
Ответ 5
У меня была аналогичная проблема, когда дочерний элемент моего SwipeRefreshLayout
был FrameLayout
, у которого были дочерние элементы TextView
и ListView
, и когда я прокручивался в ListView
, он попытался бы сделать обновление.
Я исправил его с помощью пользовательского FrameLayout
, который переопределяет метод canScrollVertically()
пакет com.wi.director.ui.common;
import android.content.Context;
import android.util.AttributeSet;
import android.widget.FrameLayout;
/**
* Created by devansh on 9/22/15.
*/
public class FrameLayoutForSwipeRefresh extends FrameLayout {
public FrameLayoutForSwipeRefresh(Context context) {
super(context);
}
public FrameLayoutForSwipeRefresh(Context context, AttributeSet attrs) {
super(context, attrs);
}
public FrameLayoutForSwipeRefresh(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public boolean canScrollVertically(int direction) {
if (super.canScrollVertically(direction)) {
return true;
}
int cc = getChildCount();
for (int i = 0; i < cc; i++) {
if (getChildAt(i).canScrollVertically(direction)) {
return true;
}
}
return false;
}
}
Ответ 6
Еще проще просто установить SwipeRefreshLayout
включен , если firstVisibleItem
или отключен, если нет:
yourList.setOnScrollListener(new AbsListView.OnScrollListener() {
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
yourSwipeRefreshLayout.setEnabled(firstVisibleItem == 0);
}
});
Ответ 7
Мой Gridview Начать работать с Swipefreshlayout
gridView.setOnScrollListener(new AbsListView.OnScrollListener() {
@Override
public void onScrollStateChanged(AbsListView view, int scrollState)
{
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
if (gridView.getChildAt(0) != null) {
mrefreshlayout.setEnabled(gridView.getFirstVisiblePosition() == 0 && gridView.getChildAt(0).getTop() == 0);
}
}
});
макет в XML
<android.support.v4.widget.SwipeRefreshLayout
android:id="@+id/swiperefreshlayout"
android:layout_height="match_parent"
android:layout_width="match_parent">
<LinearLayout
android:background="#ffffff"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<GridView
android:background="#ffffff"
android:gravity="center"
android:verticalSpacing="2dp"
android:horizontalSpacing="2dp"
android:numColumns="2"
android:id="@+id/grid_view"
android:animateLayoutChanges="true"
android:overScrollMode="always"
android:scrollbars="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<TextView
android:id="@+id/nofieldtv"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="@+id/adlayout"
android:textColor="#000000"
android:gravity="center"
android:visibility="gone"
android:textSize="16sp"
android:layout_below="@+id/headerLayout"
android:layout_marginTop="2dp">
</TextView>
</LinearLayout>
</android.support.v4.widget.SwipeRefreshLayout>
Ответ 8
По сути, мы хотим прокрутить что-нибудь прокручиваемое. Мы можем поместить все представления в ScrollView.
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
</LinearLayout>
</ScrollView>
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
Но прокрутка ListView может потребовать некоторых модификаций - wrap_content ListView
Ответ 9
Для Xamarin у меня была та же проблема (прокрутка вверх не работает с SwipeRefreshLayout в Listview) и я реализовал ее таким образом.
public void OnScroll (представление AbsListView, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
}
public void OnScrollStateChanged(AbsListView view, [GeneratedEnum] ScrollState scrollState)
{
if(listView.GetChildAt(0) != null)
{
swipeRefreshLayout.Enabled=listView.FirstVisiblePosition==0 && listView.GetChildAt(0).Top==0;
}
}