我想用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'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;
}
}
答案 0 :(得分:52)
为了让SwipeRefreshLayout工作,它需要是ListView的直接父级,而ListView应该是SwipeRefreshLayout的第一个活动子视图。
SwipeRefreshLayout的文档说ListView应该是唯一的子节点,但只要ListView是第一个,它就可以有多个子节点。这意味着,例如,如果您使用带有&#34; empty&#34;视图的适配器,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,因为它&#34; target&#34;并且它的默认canChildScrollUp()
实现总是返回false。一旦我在Fragment中移动SwipeRefreshLayout,一切都开始正常工作。
<FrameLayout>
<SwipeRefreshLayout> )
<ListView/> \ fragment
<TextView/> /
</SwipeRefreshLayout> )
</FrameLayout>
答案 1 :(得分:29)
我有同样的问题并解决了它:
MongoCollection<Document> collection = mongoDB.getDatabase(DB_NAME).getCollection(COLLECTION);
Document incUpdate = new Document("doc_freq", doc.count());
Document setUpdate = new Document("doc", doc.data());
Document update = new Document();
update.append("$setOnInsert", setUpdate);
update.append("$inc", incUpdate);
UpdateOptions updateOptions = new UpdateOptions();
updateOptions.upsert(true);
Document query = new Document("doc", doc.data());
collection.updateOne(query, update, updateOptions)
答案 2 :(得分:28)
创建一个像这样的布局。
<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都会触发并更新,从而使您的应用无法在列表中向上滚动。
这里的技巧是手动连接ListView的OnScrollListener。您只需检查显示的第一行是否与第一个最顶层位置匹配,然后启用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/
答案 3 :(得分:9)
制作自己的SwipeRefreshLayout实现并以这种方式覆盖canChildScrollUp:
@Override
public boolean canChildScrollUp() {
if (scrollView != null)
return scrollView.canScrollVertically(-1);
return false;
}
只需替换ScrollView的任何子类。
答案 4 :(得分:5)
我遇到了一个类似的问题,我的SwipeRefreshLayout
的孩子是FrameLayout
,孩子有TextView
和ListView
,当我向上滚动时ListView
1}}它会尝试刷新。
我使用自定义的FrameLayout
修改了它,它覆盖了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;
}
}
答案 5 :(得分:3)
更简单的只是设置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);
}
});
或禁用,如果不是:
self.textView.textContainer.lineBreakMode = NSLineBreakByTruncatingTail;
答案 6 :(得分: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>
答案 7 :(得分:0)
基本上,我们想滚动一些可滚动的东西。我们可以将所有视图放入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
答案 8 :(得分:0)
对于Xamarin,我遇到了同样的问题(向上滚动不适用于Listview中的SwipeRefreshLayout),并以这种方式实现。
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;
}
}
答案 9 :(得分:0)
如果RecyclerView
或ListView
不是SwipeRefreshLayout
的直接子代,则会发生此问题。
最简单的解决方案是提供OnChildScrollUpCallback
实现并适当地返回结果。在下面的Kotlin代码中,refreshLayout
是SwipeRefreshLayout
,recyclerView
是RecyclerView
,在xml
布局代码中也可以看到。
refreshLayout.setOnChildScrollUpCallback(object : SwipeRefreshLayout.OnChildScrollUpCallback {
override fun canChildScrollUp(parent: SwipeRefreshLayout, child: View?): Boolean {
if (recyclerView != null) {
return recyclerView.canScrollVertically(-1)
}
return false
}
})
xml
的布局是这样的,
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="@+id/swipeRefresh"
...
lots of other views i.e TextView, ImageView, MotionLayout
...
...
...
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView".../>
...
...
...
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
答案 10 :(得分:0)
试试下面的代码:-
recyclerview.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
int topRowVerticalPosition = (binding.rvMessageList == null || binding.rvMessageList.getChildCount() == 0) ? 0 : binding.rvMessageList.getChildAt(0).getTop();
swipeRefresh.setEnabled(topRowVerticalPosition >= 0);
}
});
答案 11 :(得分:0)
@the_dani
给出了正确的答案,但它是针对 listview
的,所以在这里我给您解决 recylerview
与滑动刷新布局的滚动冲突的解决方案。对于 Recyclerview
,您必须使用 LayoutManager
来获取 first visible item
。
rvStaggered.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
if (recyclerView.getChildAt(0) != null) {
swipeMain.setEnabled(
linearLayoutManager.findFirstVisibleItemPosition() == 0
&& recyclerView.getChildAt(0).getTop() == 0);
}
}
});