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;
}
}
SwipeRefreshLayoutが機能するためには、ListViewが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は正常に機能し、他の回答に記載されている回避策は必要ありません。
私自身の問題は、リストビューをフラグメントとしてロードしていたことでした。そのため、実際には次のようになりました。
_<SwipeRefreshLayout>
<FrameLayout> )
<ListView/> \ fragment
<TextView/> /
</FrameLayout> )
</SwipeRefreshLayout>
_
そのため、SwipeRefreshLayoutは「ターゲット」としてFrameLayoutを選択し、そのデフォルトのcanChildScrollUp()
実装は常にfalseを返していました。 SwipeRefreshLayoutをFragment内に移動すると、すべてが正常に機能し始めました。
_<FrameLayout>
<SwipeRefreshLayout> )
<ListView/> \ fragment
<TextView/> /
</SwipeRefreshLayout> )
</FrameLayout>
_
私は同じ問題を抱えて解決しました:
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);
}
}
});
このようなレイアウトを作成します。
<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を手動で配線することです。表示されている最初の行が最初の一番上の位置に一致するかどうかを確認し、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/
SwipeRefreshLayoutの独自の実装を作成し、この方法でcanChildScrollUpをオーバーライドします。
@Override
public boolean canChildScrollUp() {
if (scrollView != null)
return scrollView.canScrollVertically(-1);
return false;
}
scrollViewのサブクラスに置き換えるだけです。
私のSwipeRefreshLayout
の子がFrameLayout
とTextView
を子として持つListView
であり、ListView
更新を試みます。
canScrollVertically()
メソッドをオーバーライドするカスタムFrameLayout
を使用して修正しました
パッケージ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;
}
}
さらに簡単にSwipeRefreshLayout
を有効に設定するだけですiffirstVisibleItem
またはそうでない場合は無効になります:
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>
基本的に、スクロール可能なものをスクロールする必要があります。すべてのビューを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