dkzwm / SmoothRefreshLayout

一款支持上下拉刷新、越界回弹、二级刷新、横向刷新、拉伸回弹、平滑滚动、嵌套滚动的多功能刷新控件
MIT License
1.3k stars 218 forks source link

1.7 版本 在CoordinatorLayout 里面使用 MaterialSmoothRefreshLayout ,上拉和下来均失效 #103

Closed dahai2070 closed 4 years ago

dahai2070 commented 4 years ago

图片

dahai2070 commented 4 years ago

之前的1.6.6.8 是没有问题的。

dkzwm commented 4 years ago

@dahai2070 不是很明白失效是什么意思,这边布局相关代码是没有修改的,另外我测试了发现没有问题。

dahai2070 commented 4 years ago

失效是指上拉加载和下拉刷新,recyclerView 滑动到上下极限后,refreshLayout 都没有反应了。

dahai2070 commented 4 years ago

日志没没看到报错,是有日志控制开关吗?

dkzwm commented 4 years ago

@dahai2070 NestedScrollView嵌套RecyclerView的话是和#101一样的问题,不能设置RecyclerViewsetHasFixedSize(true)或者必须手动requestLayout。如果不是NestedScrollView嵌套RecyclerView,我也测试了没有问题

<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <me.dkzwm.widget.srl.MaterialSmoothRefreshLayout
            android:id="@+id/smoothRefreshLayout_with_textView"
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <androidx.core.widget.NestedScrollView
                android:layout_width="match_parent"
                android:layout_height="match_parent">

                <LinearLayout
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:orientation="vertical">

                    <TextView
                        android:layout_width="match_parent"
                        android:layout_height="300dp"
                        android:background="#E8E8E8"
                        android:gravity="center"
                        android:text="@string/with_textView"
                        android:textColor="#333333"
                        android:textSize="28sp" />

                    <TextView
                        android:layout_width="match_parent"
                        android:layout_height="300dp"
                        android:background="#E8E8E8"
                        android:gravity="center"
                        android:text="@string/with_textView"
                        android:textColor="#333333"
                        android:textSize="28sp" />

                    <TextView
                        android:layout_width="match_parent"
                        android:layout_height="300dp"
                        android:background="#E8E8E8"
                        android:gravity="center"
                        android:text="@string/with_textView"
                        android:textColor="#333333"
                        android:textSize="28sp" />
                </LinearLayout>
            </androidx.core.widget.NestedScrollView>
        </me.dkzwm.widget.srl.MaterialSmoothRefreshLayout>
    </RelativeLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
package me.dkzwm.widget.srl.sample.ui;

import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.view.MenuItem;

import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import me.dkzwm.widget.srl.RefreshingListenerAdapter;
import me.dkzwm.widget.srl.SmoothRefreshLayout;
import me.dkzwm.widget.srl.sample.R;

/**
 * Created by dkzwm on 2017/6/1.
 *
 * @author dkzwm
 */
public class WithTextViewActivity extends AppCompatActivity {
    private SmoothRefreshLayout mRefreshLayout;
    private Handler mHandler = new Handler();

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_with_textview);
        getSupportActionBar().setDisplayHomeAsUpEnabled(true);
        getSupportActionBar().setDisplayShowHomeEnabled(true);
        getSupportActionBar().setTitle(R.string.with_textView);
        mRefreshLayout = findViewById(R.id.smoothRefreshLayout_with_textView);
        mRefreshLayout.setEnableKeepRefreshView(true);
        mRefreshLayout.setDisableLoadMore(false);
        mRefreshLayout.setOnRefreshListener(
                new RefreshingListenerAdapter() {
                    @Override
                    public void onRefreshing() {
                        mHandler.postDelayed(
                                new Runnable() {
                                    @Override
                                    public void run() {
                                        mRefreshLayout.refreshComplete();
                                    }
                                },
                                2000);
                    }

                    @Override
                    public void onLoadingMore() {
                        mHandler.postDelayed(
                                new Runnable() {
                                    @Override
                                    public void run() {
                                        mRefreshLayout.refreshComplete();
                                    }
                                },
                                2000);
                    }
                });
        mRefreshLayout.autoRefresh(true);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case android.R.id.home:
                onBackPressed();
                return true;
            default:
                return super.onOptionsItemSelected(item);
        }
    }

    @Override
    public void onBackPressed() {
        startActivity(new Intent(WithTextViewActivity.this, MainActivity.class));
        finish();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mHandler.removeCallbacksAndMessages(null);
    }
}

如上代码运行良好

dahai2070 commented 4 years ago

设置了 refreshLayout.setEnableKeepRefreshView(true); 还是不行,下面是日志。

下拉.txt 上拉.txt

dkzwm commented 4 years ago

@dahai2070 你的具体是什么样子的,内部是否像 #101 一样的布局,如果是你使用 #101 我贴出代码和布局测试是没有问题的。我不是很清楚内部是什么布局,看Log定位不到问题的

dahai2070 commented 4 years ago

NestedScrollView 中确实有个布局,是recyclerView , 我不折腾了,我继续用之前的版本。

dkzwm commented 4 years ago

@dahai2070 如果老版本真没这种情况也可以,但布局相关代码是一致的,理论上不存在新版本不可以,应该就是RecyclerViewsetHasFixedSize(true)导致的问题

dahai2070 commented 4 years ago

去看了下代码, 没有设置 setHasFixedSize(true), 但是设置了 setNestedScrollingEnabled(false);

图片

dkzwm commented 4 years ago

@dahai2070 这边能麻烦将setNestedScrollingEnabled(false)代码移入xml中吗,至少从你发的log中得知关闭代码没有起作用

dahai2070 commented 4 years ago

图片

图片

这样吗?

dahai2070 commented 4 years ago

刚才给你发的日子上拉 下拉日志,是这个页面的,非常简单的页面

图片

dahai2070 commented 4 years ago

这是一个fragment,所在的activity 使用了 CoordinatorLayout

dkzwm commented 4 years ago
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <me.dkzwm.widget.srl.MaterialSmoothRefreshLayout
            android:id="@+id/smoothRefreshLayout_with_textView"
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <androidx.core.widget.NestedScrollView
                android:layout_width="match_parent"
                android:layout_height="match_parent">

                <LinearLayout
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:orientation="vertical">

                    <TextView
                        android:layout_width="match_parent"
                        android:layout_height="300dp"
                        android:background="#E8E8E8"
                        android:gravity="center"
                        android:text="@string/with_textView"
                        android:textColor="#333333"
                        android:textSize="28sp" />

                    <androidx.recyclerview.widget.RecyclerView
                        android:id="@+id/recyclerView_with_testView"
                        android:layout_width="match_parent"
                        android:layout_height="match_parent"
                        android:nestedScrollingEnabled="false" />
                </LinearLayout>
            </androidx.core.widget.NestedScrollView>
        </me.dkzwm.widget.srl.MaterialSmoothRefreshLayout>
    </RelativeLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
package me.dkzwm.widget.srl.sample.ui;

import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.view.MenuItem;

import java.util.List;

import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import me.dkzwm.widget.srl.RefreshingListenerAdapter;
import me.dkzwm.widget.srl.SmoothRefreshLayout;
import me.dkzwm.widget.srl.sample.R;
import me.dkzwm.widget.srl.sample.adapter.RecyclerViewAdapter;
import me.dkzwm.widget.srl.sample.utils.DataUtil;

/**
 * Created by dkzwm on 2017/6/1.
 *
 * @author dkzwm
 */
public class WithTextViewActivity extends AppCompatActivity {
    private SmoothRefreshLayout mRefreshLayout;
    private Handler mHandler = new Handler();
    private RecyclerView mRecyclerView;
    private RecyclerViewAdapter mAdapter;
    private int mCount;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_with_textview);
        getSupportActionBar().setDisplayHomeAsUpEnabled(true);
        getSupportActionBar().setDisplayShowHomeEnabled(true);
        getSupportActionBar().setTitle(R.string.with_textView);
        mRecyclerView = findViewById(R.id.recyclerView_with_testView);
        mRecyclerView.setLayoutManager(new GridLayoutManager(this, 3));
        mAdapter = new RecyclerViewAdapter(this, getLayoutInflater());
        mRecyclerView.setAdapter(mAdapter);
        mRefreshLayout = findViewById(R.id.smoothRefreshLayout_with_textView);
        mRefreshLayout.setDisableLoadMore(false);
        mRefreshLayout.setOnRefreshListener(
                new RefreshingListenerAdapter() {
                    @Override
                    public void onRefreshing() {
                        mHandler.postDelayed(
                                new Runnable() {
                                    @Override
                                    public void run() {
                                        List<String> list = DataUtil.createList(mCount, 60);
                                        mCount = list.size();
                                        mAdapter.insertData(list);
                                        mRefreshLayout.refreshComplete();
                                    }
                                },
                                2000);
                    }

                    @Override
                    public void onLoadingMore() {
                        mHandler.postDelayed(
                                new Runnable() {
                                    @Override
                                    public void run() {
                                        List<String> list = DataUtil.createList(mCount, 15);
                                        mCount += list.size();
                                        mAdapter.appendData(list);
                                        mRefreshLayout.refreshComplete();
                                    }
                                },
                                2000);
                    }
                });
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case android.R.id.home:
                onBackPressed();
                return true;
            default:
                return super.onOptionsItemSelected(item);
        }
    }

    @Override
    public void onBackPressed() {
        startActivity(new Intent(WithTextViewActivity.this, MainActivity.class));
        finish();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mHandler.removeCallbacksAndMessages(null);
    }
}

以上是我写的测试代码,和你的布局基本一致

dahai2070 commented 4 years ago

接下来,我吃了晚饭,再给你发 activity 里面的日志。

dahai2070 commented 4 years ago

嗯。你发的这个页面,和我activity 里面的布局很接近,不过我有一个View 使用了 app:layout_behavior="@string/bottom_sheet_behavior" 图片

图片

dahai2070 commented 4 years ago

吃饭去了,,吃了饭,我给你发日子,这个activity的日志

dkzwm commented 4 years ago
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <me.dkzwm.widget.srl.MaterialSmoothRefreshLayout
            android:id="@+id/smoothRefreshLayout_with_textView"
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <androidx.core.widget.NestedScrollView
                android:layout_width="match_parent"
                android:layout_height="match_parent">

                <LinearLayout
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:orientation="vertical">

                    <TextView
                        android:layout_width="match_parent"
                        android:layout_height="300dp"
                        android:background="#E8E8E8"
                        android:gravity="center"
                        android:text="@string/with_textView"
                        android:textColor="#333333"
                        android:textSize="28sp" />

                    <androidx.recyclerview.widget.RecyclerView
                        android:id="@+id/recyclerView_with_testView"
                        android:layout_width="match_parent"
                        android:layout_height="match_parent"
                        android:nestedScrollingEnabled="false" />
                </LinearLayout>
            </androidx.core.widget.NestedScrollView>
        </me.dkzwm.widget.srl.MaterialSmoothRefreshLayout>
    </RelativeLayout>

    <LinearLayout
        android:id="@+id/linearLayout_with_testView"
        android:layout_width="match_parent"
        android:layout_height="200dp"
        android:orientation="vertical"
        app:behavior_hideable="true"
        app:behavior_peekHeight="40dp"
        app:layout_behavior="@string/bottom_sheet_behavior">

        <TextView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:gravity="center"
            android:background="@color/colorPrimary"
            android:text="测试"
            android:textColor="@android:color/white"
            android:textSize="18sp" />
    </LinearLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
package me.dkzwm.widget.srl.sample.ui;

import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.LinearLayout;

import com.google.android.material.bottomsheet.BottomSheetBehavior;

import java.util.List;

import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import me.dkzwm.widget.srl.RefreshingListenerAdapter;
import me.dkzwm.widget.srl.SmoothRefreshLayout;
import me.dkzwm.widget.srl.sample.R;
import me.dkzwm.widget.srl.sample.adapter.RecyclerViewAdapter;
import me.dkzwm.widget.srl.sample.utils.DataUtil;

/**
 * Created by dkzwm on 2017/6/1.
 *
 * @author dkzwm
 */
public class WithTextViewActivity extends AppCompatActivity {
    private SmoothRefreshLayout mRefreshLayout;
    private Handler mHandler = new Handler();
    private RecyclerView mRecyclerView;
    private RecyclerViewAdapter mAdapter;
    private int mCount;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_with_textview);
        getSupportActionBar().setDisplayHomeAsUpEnabled(true);
        getSupportActionBar().setDisplayShowHomeEnabled(true);
        getSupportActionBar().setTitle(R.string.with_textView);
        mRecyclerView = findViewById(R.id.recyclerView_with_testView);
        mRecyclerView.setLayoutManager(new GridLayoutManager(this, 3));
        mAdapter = new RecyclerViewAdapter(this, getLayoutInflater());
        mRecyclerView.setAdapter(mAdapter);
        mRefreshLayout = findViewById(R.id.smoothRefreshLayout_with_textView);
        mRefreshLayout.setDisableLoadMore(false);
        mRefreshLayout.setOnRefreshListener(
                new RefreshingListenerAdapter() {
                    @Override
                    public void onRefreshing() {
                        mHandler.postDelayed(
                                new Runnable() {
                                    @Override
                                    public void run() {
                                        List<String> list = DataUtil.createList(mCount, 60);
                                        mCount = list.size();
                                        mAdapter.insertData(list);
                                        mRefreshLayout.refreshComplete();
                                    }
                                },
                                2000);
                    }

                    @Override
                    public void onLoadingMore() {
                        mHandler.postDelayed(
                                new Runnable() {
                                    @Override
                                    public void run() {
                                        List<String> list = DataUtil.createList(mCount, 15);
                                        mCount += list.size();
                                        mAdapter.appendData(list);
                                        mRefreshLayout.refreshComplete();
                                    }
                                },
                                2000);
                    }
                });
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case android.R.id.home:
                onBackPressed();
                return true;
            case Menu.FIRST:
                LinearLayout linearLayout = findViewById(R.id.linearLayout_with_testView);
                BottomSheetBehavior bottomSheetBehavior = BottomSheetBehavior.from(linearLayout);
                if (bottomSheetBehavior.getState() == BottomSheetBehavior.STATE_EXPANDED) {
                    bottomSheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
                } else {
                    bottomSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);
                }
                return true;
            default:
                return super.onOptionsItemSelected(item);
        }
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        menu.add(Menu.NONE, Menu.FIRST, Menu.NONE, "展开/收缩");
        return super.onCreateOptionsMenu(menu);
    }

    @Override
    public void onBackPressed() {
        startActivity(new Intent(WithTextViewActivity.this, MainActivity.class));
        finish();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mHandler.removeCallbacksAndMessages(null);
    }
}

测试没有问题

dahai2070 commented 4 years ago

activity日志.txt

dkzwm commented 4 years ago

dahai2070 这边跟踪日志,你是否嵌套有自定义视图且实现了NestedScrollingParent2以下接口,从1.7.0.androidx开始,SRL是实现了NestedScrollingParent3接口的,需配合实现了NestedScrollingParent3接口的视图才能正确工作,由于向下兼容的问题,ViewParentCompatonNestedScroll方法中将实现低于NestedScrollingParent2的视图时选择将未消耗量直接传递给了消耗量,导致consumed拿到的值等于dyUnconsumed/dxUnconsumed。传递到SRL层时,SRL发现两值相等表示已完成消耗故而不会做任何操作

dahai2070 commented 4 years ago

有嵌套自定义视图,刚才去 搜了下,没有实现 NestedScrollingParent 相关的接口。

dkzwm commented 4 years ago

@dahai2070 你可以尝试跟踪下

    @Override
    public void onNestedScroll(
            @NonNull View target,
            int dxConsumed,
            int dyConsumed,
            int dxUnconsumed,
            int dyUnconsumed,
            int type,
            @NonNull int[] consumed) {
        // 此处Debug下
        dispatchNestedScroll(
                dxConsumed,
                dyConsumed,
                dxUnconsumed,
                dyUnconsumed,
                mParentOffsetInWindow,
                type,
                consumed);

打下断点,跟踪下日志,看是哪一个视图的问题

dkzwm commented 4 years ago

最终会走到ViewParentCompat

    public static void onNestedScroll(ViewParent parent, View target, int dxConsumed,
            int dyConsumed, int dxUnconsumed, int dyUnconsumed, int type,
            @NonNull int[] consumed) {

        if (parent instanceof NestedScrollingParent3) {
            ((NestedScrollingParent3) parent).onNestedScroll(target, dxConsumed, dyConsumed,
                    dxUnconsumed, dyUnconsumed, type, consumed);
        } else {
            // If we are calling anything less than NestedScrollingParent3, add the unconsumed
            // distances to the consumed parameter so calling NestedScrollingChild3 implementations
            // are told the entire scroll distance was consumed (for backwards compat).
            consumed[0] += dxUnconsumed;
            consumed[1] += dyUnconsumed;

            if (parent instanceof NestedScrollingParent2) {
                ((NestedScrollingParent2) parent).onNestedScroll(target, dxConsumed, dyConsumed,
                        dxUnconsumed, dyUnconsumed, type);
            } else if (type == ViewCompat.TYPE_TOUCH) {
                // Else if the type is the default (touch), try the NestedScrollingParent API
                if (Build.VERSION.SDK_INT >= 21) {
                    try {
                        parent.onNestedScroll(target, dxConsumed, dyConsumed, dxUnconsumed,
                                dyUnconsumed);
                    } catch (AbstractMethodError e) {
                        Log.e(TAG, "ViewParent " + parent + " does not implement interface "
                                + "method onNestedScroll", e);
                    }
                } else if (parent instanceof NestedScrollingParent) {
                    ((NestedScrollingParent) parent).onNestedScroll(target, dxConsumed, dyConsumed,
                            dxUnconsumed, dyUnconsumed);
                }
            }
        }
    }

问题可能出在

                if (Build.VERSION.SDK_INT >= 21) {
                    try {
                        parent.onNestedScroll(target, dxConsumed, dyConsumed, dxUnconsumed,
                                dyUnconsumed);
                    } catch (AbstractMethodError e) {
                        Log.e(TAG, "ViewParent " + parent + " does not implement interface "
                                + "method onNestedScroll", e);
                    }
                } else if (parent instanceof NestedScrollingParent) {
                    ((NestedScrollingParent) parent).onNestedScroll(target, dxConsumed, dyConsumed,
                            dxUnconsumed, dyUnconsumed);
                }
dahai2070 commented 4 years ago

嗯,空了再学习,测试下,sml里面代码很多,也许我一时半会找不到问题所在,我现在暂且用1.6.**。

dkzwm commented 4 years ago

根源可能是有自定义没有正确实现NestedScrolling

    public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) {
        return false;//此处返回了true
    }

也有可能是自定义视图只实现了NestedScrollingParent2及以下接口。在onStartNestedScroll返回true视图并完整实现了NestedScrollingParent3情况下,SRL工作得都挺好

dahai2070 commented 4 years ago

不知道是不是 我对NestedScrollView 设置了监听的缘故,仅仅是猜测 图片