Je veux implémenter le défilement pour actualiser la fonctionnalité avec un listView. Il existe également d'autres éléments d'affichage dans le même fichier de mise en page qui s'affichent si la liste est vide. Voici mon fichier de mise en page. Le problème est que lorsque je fais défiler vers le bas, puis j'essaie de faire défiler vers le haut, au lieu de faire défiler jusqu'en haut, puis de le rafraîchir, il est simplement actualisé et le défilement vers le haut ne fonctionne pas.
<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>
Ceci est ma méthode 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's 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;
}
}
Pour que SwipeRefreshLayout fonctionne, il doit être le parent direct de votre ListView, et le ListView doit être la première vue enfant active du SwipeRefreshLayout.
La documentation de SwipeRefreshLayout indique que le ListView doit être le seul enfant, mais il est correct s'il a plus d'un enfant tant que le ListView est le premier. Cela signifie, par exemple, que SwipeRefreshLayout fonctionnera correctement si vous utilisez un adaptateur avec une vue pour "vide". Par exemple:
<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>
Si vous pouvez gérer ce type de disposition, alors SwipeRefreshLayout fonctionnera correctement et vous n'aurez besoin d'aucune des solutions de contournement répertoriées dans les autres réponses.
Mon propre problème était que je chargeais mon ListView en tant que fragment, donc j'avais en fait:
<SwipeRefreshLayout>
<FrameLayout> )
<ListView/> \ fragment
<TextView/> /
</FrameLayout> )
</SwipeRefreshLayout>
Donc, le SwipeRefreshLayout choisissait le FrameLayout car il est "cible" et son implémentation par défaut canChildScrollUp()
retournait toujours false. Une fois que j'ai déplacé le SwipeRefreshLayout à l'intérieur du fragment, tout a commencé à fonctionner correctement.
<FrameLayout>
<SwipeRefreshLayout> )
<ListView/> \ fragment
<TextView/> /
</SwipeRefreshLayout> )
</FrameLayout>
J'ai eu le même problème et l'ai résolu:
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);
}
}
});
Créez une mise en page comme celle-ci.
<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>
Si vous l'utilisez tel quel, chaque fois que vous faites défiler vers le haut dans cette vue, SwipeRefreshLayout se déclenche et se met à jour, rendant votre application incapable de faire défiler vers le haut dans une liste.
L'astuce consiste à câbler manuellement OnScrollListener à partir de ListView. Vous vérifiez simplement si la première ligne affichée correspond à la première position la plus haute, puis activez SwipeRefreshLayout. Sinon, désactivez-le.
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);
}
});
Avec ce petit extrait, cela fonctionne maintenant parfaitement.
Bon codage!
Source - http://nlopez.io/swiperefreshlayout-with-listview-done-right/
Créez votre propre implémentation de SwipeRefreshLayout et remplacez canChildScrollUp de cette manière:
@Override
public boolean canChildScrollUp() {
if (scrollView != null)
return scrollView.canScrollVertically(-1);
return false;
}
il suffit de remplacer par n'importe quelle sous-classe de ScrollView.
J'ai eu un problème similaire où l'enfant de mon SwipeRefreshLayout
était un FrameLayout
qui avait un TextView
et ListView
comme enfants et quand j'ai fait défiler le ListView
il essaiera de faire un rafraîchissement.
Je l'ai corrigé en utilisant un FrameLayout
personnalisé qui remplace la méthode canScrollVertically()
package 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;
}
}
Encore plus simple, définissez SwipeRefreshLayout
activé sifirstVisibleItem
ou désactivé sinon:
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);
}
});
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);
}
}
});
<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>
Fondamentalement, nous voulons faire défiler quelque chose de défilable. Nous pouvons mettre toutes les vues dans 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>
Mais faire défiler ListView peut nécessiter quelques modifications - wrap_content ListView