wjc
2025-04-09 afb62b49b79a31ab62a548f9cef672dfc9bae16f
2025年04月09日09:04:34
55个文件已添加
5个文件已修改
2 文件已重命名
2914 ■■■■■ 已修改文件
app/build.gradle 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/hdl/photovoltaic/ui/home/HomePageFragment.java 80 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/hdl/photovoltaic/ui/powerstation/HouseAndDeviceFragment.java 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/hdl/photovoltaic/widget/LoadingDialog.java 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/hdl/photovoltaic/widget/refreshlayout/BGARefreshLayout.java 923 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/hdl/photovoltaic/widget/refreshlayout/BGARefreshScrollingUtil.java 292 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/hdl/photovoltaic/widget/refreshlayout/BGARefreshViewHolder.java 345 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/hdl/photovoltaic/widget/refreshlayout/BGAStickyNavLayout.java 520 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/hdl/photovoltaic/widget/refreshlayout/HDLRefreshView.java 127 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/hdl/photovoltaic/widget/refreshlayout/HDLRefreshViewHolder.java 131 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/hdl/photovoltaic/widget/refreshlayout/NestedScrollLayout.java 152 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/drawable-xhdpi/loading_gif.gif 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/drawable/loading_44.png 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/drawable/loading_gif.png 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/drawable/loading_refresh.xml 136 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/drawable/loading_refresh_end.xml 138 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/layout/fragment_home_page.xml 20 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/layout/view_refresh_header.xml 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/mipmap-xhdpi/loading_1.png 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/mipmap-xhdpi/loading_10.png 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/mipmap-xhdpi/loading_11.png 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/mipmap-xhdpi/loading_12.png 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/mipmap-xhdpi/loading_13.png 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/mipmap-xhdpi/loading_14.png 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/mipmap-xhdpi/loading_15.png 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/mipmap-xhdpi/loading_16.png 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/mipmap-xhdpi/loading_17.png 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/mipmap-xhdpi/loading_18.png 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/mipmap-xhdpi/loading_19.png 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/mipmap-xhdpi/loading_2.png 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/mipmap-xhdpi/loading_20.png 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/mipmap-xhdpi/loading_21.png 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/mipmap-xhdpi/loading_22.png 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/mipmap-xhdpi/loading_23.png 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/mipmap-xhdpi/loading_24.png 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/mipmap-xhdpi/loading_25.png 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/mipmap-xhdpi/loading_26.png 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/mipmap-xhdpi/loading_27.png 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/mipmap-xhdpi/loading_28.png 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/mipmap-xhdpi/loading_29.png 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/mipmap-xhdpi/loading_3.png 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/mipmap-xhdpi/loading_30.png 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/mipmap-xhdpi/loading_31.png 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/mipmap-xhdpi/loading_32.png 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/mipmap-xhdpi/loading_33.png 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/mipmap-xhdpi/loading_34.png 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/mipmap-xhdpi/loading_35.png 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/mipmap-xhdpi/loading_36.png 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/mipmap-xhdpi/loading_37.png 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/mipmap-xhdpi/loading_38.png 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/mipmap-xhdpi/loading_39.png 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/mipmap-xhdpi/loading_4.png 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/mipmap-xhdpi/loading_40.png 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/mipmap-xhdpi/loading_41.png 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/mipmap-xhdpi/loading_42.png 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/mipmap-xhdpi/loading_43.png 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/mipmap-xhdpi/loading_44.png 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/mipmap-xhdpi/loading_5.png 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/mipmap-xhdpi/loading_6.png 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/mipmap-xhdpi/loading_7.png 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/mipmap-xhdpi/loading_8.png 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/mipmap-xhdpi/loading_9.png 补丁 | 查看 | 原始文档 | blame | 历史
app/build.gradle
@@ -157,6 +157,7 @@
//    implementation 'com.hdl.android:debugsdk:1.0.71'
    implementation project(path: ':sdk')
    implementation 'androidx.activity:activity:1.2.3'
    implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
    testImplementation 'junit:junit:4.13.2'
    androidTestImplementation 'androidx.test.ext:junit:1.1.3'
@@ -206,13 +207,13 @@
    //分析和用户标签等功能(可选,集成极光分析SDK后,即可支持行为触发推送消息、推送转化率统计,用户行为分)
    implementation 'cn.jiguang.sdk:joperate:2.0.2'
    //若不集成厂商通道,可直接跳过以下依赖
    // 极光厂商插件版本与接入 JPush 版本保持一致,下同
    // 接入华为厂商
    implementation 'com.huawei.hms:push:6.9.0.300'
    implementation 'cn.jiguang.sdk.plugin:huawei:5.0.3'
    implementation 'com.huawei.agconnect:agconnect-core:1.9.1.301'
//    // 接入 FCM 厂商
//    implementation 'com.google.firebase:firebase-messaging:23.1.2'
//    implementation 'cn.jiguang.sdk.plugin:fcm:5.0.3'
app/src/main/java/com/hdl/photovoltaic/ui/home/HomePageFragment.java
@@ -8,6 +8,7 @@
import android.content.Intent;
import android.content.pm.PackageManager;
import android.location.LocationManager;
import android.os.AsyncTask;
import android.os.Bundle;
import android.provider.Settings;
import android.text.TextUtils;
@@ -16,7 +17,6 @@
import androidx.annotation.NonNull;
import androidx.appcompat.content.res.AppCompatResources;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import com.bigkoo.pickerview.builder.TimePickerBuilder;
import com.bigkoo.pickerview.listener.OnTimeSelectListener;
@@ -55,6 +55,8 @@
import com.hdl.photovoltaic.utils.TimeUtils;
import com.hdl.photovoltaic.widget.ConfirmationCancelDialog;
import com.hdl.photovoltaic.widget.FlashingBoxDialog;
import com.hdl.photovoltaic.widget.refreshlayout.BGARefreshLayout;
import com.hdl.photovoltaic.widget.refreshlayout.HDLRefreshViewHolder;
import com.hdl.sdk.link.core.bean.eventbus.BaseEventBus;
import org.greenrobot.eventbus.EventBus;
@@ -70,7 +72,7 @@
 * 首页模块
 */
public class HomePageFragment extends CustomBaseFragment implements AAChartView.AAChartViewCallBack {
public class HomePageFragment extends CustomBaseFragment implements AAChartView.AAChartViewCallBack, BGARefreshLayout.BGARefreshLayoutDelegate {
    FragmentHomePageBinding viewBinding;
@@ -342,15 +344,14 @@
            }
        });
        //设置下拉箭头颜色
        viewBinding.srl.setColorSchemeResources(R.color.text_FF245EC3);
        viewBinding.srl.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
            @Override
            public void onRefresh() {
                viewBinding.srl.setRefreshing(false);
                readDate();
            }
        });
//        viewBinding.rsl.setColorSchemeResources(R.color.text_FF245EC3);
//        viewBinding.rsl.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
//            @Override
//            public void onRefresh() {
//                viewBinding.rsl.setRefreshing(false);
//                readDate();
//            }
//        });
    }
@@ -466,8 +467,18 @@
        viewBinding.homePageStationSelectTimeLl.setVisibility(View.VISIBLE);
        viewBinding.autoSizeMyAAChart.setBackgroundColor(_mActivity.getColor(R.color.text_FF1C1C1E));
        setUpAAChartView(true);//图表加载
        processLogic();
    }
    protected void processLogic() {
        viewBinding.rsl.setDelegate(this);
        HDLRefreshViewHolder hdlRefreshViewHolder = new HDLRefreshViewHolder(_mActivity, true);
        hdlRefreshViewHolder.setPullDownImageResource(R.mipmap.loading_44);
        hdlRefreshViewHolder.setChangeToReleaseRefreshAnimResId(R.drawable.loading_refresh);
        hdlRefreshViewHolder.setRefreshingAnimResId(R.drawable.loading_refresh_end);
        viewBinding.rsl.setRefreshViewHolder(hdlRefreshViewHolder);
    }
    @Override
    public void chartViewDidFinishLoad(AAChartView aaChartView) {
@@ -501,7 +512,7 @@
     * 电站状态概览
     */
    private void getStatusOverview() {
        HdlResidenceLogic.getInstance().getStatusOverview("","","",new CloudCallBeak<StatusOverviewBean>() {
        HdlResidenceLogic.getInstance().getStatusOverview("", "", "", new CloudCallBeak<StatusOverviewBean>() {
            @Override
            public void onSuccess(StatusOverviewBean statusOverviewBean) {
                if (statusOverviewBean == null) {
@@ -726,4 +737,49 @@
        }
    }
    @Override
    public void onBGARefreshLayoutBeginRefreshing(BGARefreshLayout refreshLayout) {
        new AsyncTask<Void, Void, Void>() {
            @Override
            protected Void doInBackground(Void... params) {
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                return null;
            }
            @Override
            protected void onPostExecute(Void aVoid) {
                viewBinding.rsl.endRefreshing();
                //加载最新数据完成
                readDate();
            }
        }.execute();
    }
    @Override
    public boolean onBGARefreshLayoutBeginLoadingMore(BGARefreshLayout refreshLayout) {
        new AsyncTask<Void, Void, Void>() {
            @Override
            protected Void doInBackground(Void... params) {
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                return null;
            }
            @Override
            protected void onPostExecute(Void aVoid) {
                viewBinding.rsl.endLoadingMore();
            }
        }.execute();
        return true;
    }
}
app/src/main/java/com/hdl/photovoltaic/ui/powerstation/HouseAndDeviceFragment.java
@@ -162,6 +162,9 @@
        viewBinding.allLl.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (powerStationStatusValue.equals(PowerStationStatus.All)) {
                    return;
                }
                powerStationStatusValue = PowerStationStatus.All;
                viewBinding.allLl.setBackground(AppCompatResources.getDrawable(_mActivity, R.drawable.station_status_yes_ffffff));
                viewBinding.faultsLl.setBackground(AppCompatResources.getDrawable(_mActivity, R.drawable.station_status_no_ffffff));
@@ -180,6 +183,9 @@
        viewBinding.faultsLl.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (powerStationStatusValue.equals(PowerStationStatus.malfunction)) {
                    return;
                }
                powerStationStatusValue = PowerStationStatus.malfunction;
                viewBinding.allLl.setBackground(AppCompatResources.getDrawable(_mActivity, R.drawable.station_status_no_ffffff));
                viewBinding.faultsLl.setBackground(AppCompatResources.getDrawable(_mActivity, R.drawable.station_status_yes_ffffff));
@@ -198,6 +204,9 @@
        viewBinding.offlineLl.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (powerStationStatusValue.equals(PowerStationStatus.off)) {
                    return;
                }
                powerStationStatusValue = PowerStationStatus.off;
                viewBinding.allLl.setBackground(AppCompatResources.getDrawable(_mActivity, R.drawable.station_status_no_ffffff));
                viewBinding.faultsLl.setBackground(AppCompatResources.getDrawable(_mActivity, R.drawable.station_status_no_ffffff));
@@ -216,6 +225,9 @@
        viewBinding.connectedLl.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (powerStationStatusValue.equals(PowerStationStatus.connecting)) {
                    return;
                }
                powerStationStatusValue = PowerStationStatus.connecting;
                viewBinding.allLl.setBackground(AppCompatResources.getDrawable(_mActivity, R.drawable.station_status_no_ffffff));
                viewBinding.faultsLl.setBackground(AppCompatResources.getDrawable(_mActivity, R.drawable.station_status_no_ffffff));
@@ -765,7 +777,7 @@
     * 电站状态概览
     */
    private void getStatusOverview() {
        HdlResidenceLogic.getInstance().getStatusOverview(screeningConditionStatus,installedCapacityMinValue,installedCapacityMaxValue,new CloudCallBeak<StatusOverviewBean>() {
        HdlResidenceLogic.getInstance().getStatusOverview(screeningConditionStatus, installedCapacityMinValue, installedCapacityMaxValue, new CloudCallBeak<StatusOverviewBean>() {
            @Override
            public void onSuccess(StatusOverviewBean statusOverviewBean) {
                if (statusOverviewBean == null) {
app/src/main/java/com/hdl/photovoltaic/widget/LoadingDialog.java
@@ -1,8 +1,6 @@
package com.hdl.photovoltaic.widget;
import android.app.Dialog;
import android.content.Context;
import android.graphics.drawable.ColorDrawable;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.WindowManager;
@@ -39,7 +37,7 @@
        getWindow().setAttributes(params);
        Glide.with(context)
//                .asBitmap() // 强制作为静态图片加载
                .load(R.drawable.loading)
                .load(R.drawable.loading_gif)
                .into(imageView);
//        content.setText(R.string.device_加载中);
    }
app/src/main/java/com/hdl/photovoltaic/widget/refreshlayout/BGARefreshLayout.java
New file
@@ -0,0 +1,923 @@
/**
 * Copyright 2015 bingoogolapple
 * <p/>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p/>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p/>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.hdl.photovoltaic.widget.refreshlayout;
import android.animation.ValueAnimator;
import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.util.AttributeSet;
import android.util.Log;
import android.util.TypedValue;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.webkit.WebView;
import android.widget.AbsListView;
import android.widget.LinearLayout;
import android.widget.ScrollView;
import androidx.recyclerview.widget.RecyclerView;
import java.lang.reflect.Field;
/**
 * 作者:王浩 邮件:bingoogolapple@gmail.com
 * 创建时间:15/5/21 22:35
 * 描述:下拉刷新、上拉加载更多、可添加自定义(固定、可滑动)头部控件(例如慕课网app顶部的广告位)
 */
public class BGARefreshLayout extends LinearLayout {
    private static final String TAG = BGARefreshLayout.class.getSimpleName();
    private BGARefreshViewHolder mRefreshViewHolder;
    /**
     * 整个头部控件,下拉刷新控件mRefreshHeaderView和下拉刷新控件下方的自定义组件mCustomHeaderView的父控件
     */
    private LinearLayout mWholeHeaderView;
    /**
     * 下拉刷新控件
     */
    private View mRefreshHeaderView;
    /**
     * 下拉刷新控件下方的自定义控件
     */
    private View mCustomHeaderView;
    /**
     * 下拉刷新控件下方的自定义控件是否可滚动,默认不可滚动
     */
    private boolean mIsCustomHeaderViewScrollable = false;
    /**
     * 下拉刷新控件的高度
     */
    private int mRefreshHeaderViewHeight;
    /**
     * 当前刷新状态
     */
    private RefreshStatus mCurrentRefreshStatus = RefreshStatus.IDLE;
    /**
     * 上拉加载更多控件
     */
    private View mLoadMoreFooterView;
    /**
     * 上拉加载更多控件的高度
     */
    private int mLoadMoreFooterViewHeight;
    /**
     * 下拉刷新和上拉加载更多代理
     */
    private BGARefreshLayoutDelegate mDelegate;
    /**
     * 手指按下时,y轴方向的偏移量
     */
    private int mWholeHeaderDownY = -1;
    /**
     * 整个头部控件最小的paddingTop
     */
    private int mMinWholeHeaderViewPaddingTop;
    /**
     * 整个头部控件最大的paddingTop
     */
    private int mMaxWholeHeaderViewPaddingTop;
    /**
     * 是否处于正在加载更多状态
     */
    private boolean mIsLoadingMore = false;
    private AbsListView mAbsListView;
    private ScrollView mScrollView;
    private RecyclerView mRecyclerView;
    private View mNormalView;
    private WebView mWebView;
    private BGAStickyNavLayout mStickyNavLayout;
    private NestedScrollLayout mNestedScrollLayout;
    private View mContentView;
    private float mInterceptTouchDownX = -1;
    private float mInterceptTouchDownY = -1;
    /**
     * 按下时整个头部控件的paddingTop
     */
    private int mWholeHeaderViewDownPaddingTop = 0;
    /**
     * 记录开始下拉刷新时的downY
     */
    private int mRefreshDownY = -1;
    /**
     * 是否已经设置内容控件滚动监听器
     */
    private boolean mIsInitedContentViewScrollListener = false;
    /**
     * 触发上拉加载更多时是否显示加载更多控件
     */
    private boolean mIsShowLoadingMoreView = true;
    /**
     * 下拉刷新是否可用
     */
    private boolean mPullDownRefreshEnable = true;
    private Handler mHandler;
    private BGARefreshScaleDelegate mRefreshScaleDelegate;
    private int mTouchSlop;
    public BGARefreshLayout(Context context) {
        this(context, null);
    }
    public BGARefreshLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        setOrientation(LinearLayout.VERTICAL);
        mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
        mHandler = new Handler(Looper.getMainLooper());
        initWholeHeaderView();
    }
    /**
     * 初始化整个头部控件
     */
    private void initWholeHeaderView() {
        mWholeHeaderView = new LinearLayout(getContext());
        mWholeHeaderView.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
        mWholeHeaderView.setOrientation(LinearLayout.VERTICAL);
        addView(mWholeHeaderView);
    }
    @Override
    public void onFinishInflate() {
        super.onFinishInflate();
        if (getChildCount() != 2) {
            throw new RuntimeException(BGARefreshLayout.class.getSimpleName() + "必须有且只有一个子控件");
        }
        mContentView = getChildAt(1);
        if (mContentView instanceof AbsListView) {
            mAbsListView = (AbsListView) mContentView;
        } else if (mContentView instanceof RecyclerView) {
            mRecyclerView = (RecyclerView) mContentView;
        } else if (mContentView instanceof ScrollView) {
            mScrollView = (ScrollView) mContentView;
        } else if (mContentView instanceof WebView) {
            mWebView = (WebView) mContentView;
        } else if (mContentView instanceof BGAStickyNavLayout) {
            mStickyNavLayout = (BGAStickyNavLayout) mContentView;
            mStickyNavLayout.setRefreshLayout(this);
        } else if (mContentView instanceof NestedScrollLayout) {
            mNestedScrollLayout = (NestedScrollLayout) mContentView;
        } else {
            mNormalView = mContentView;
            // 设置为可点击,否则在空白区域无法拖动
            mNormalView.setClickable(true);
        }
    }
    public void setRefreshViewHolder(BGARefreshViewHolder refreshViewHolder) {
        mRefreshViewHolder = refreshViewHolder;
        mRefreshViewHolder.setRefreshLayout(this);
        initRefreshHeaderView();
        initLoadMoreFooterView();
    }
    public void startChangeWholeHeaderViewPaddingTop(int distance) {
        ValueAnimator animator = ValueAnimator.ofInt(mWholeHeaderView.getPaddingTop(), mWholeHeaderView.getPaddingTop() - distance);
        animator.setDuration(mRefreshViewHolder.getTopAnimDuration());
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                int paddingTop = (int) animation.getAnimatedValue();
                mWholeHeaderView.setPadding(0, paddingTop, 0, 0);
            }
        });
        animator.start();
    }
    /**
     * 初始化下拉刷新控件
     *
     * @return
     */
    private void initRefreshHeaderView() {
        mRefreshHeaderView = mRefreshViewHolder.getRefreshHeaderView();
        if (mRefreshHeaderView != null) {
            mRefreshHeaderView.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
            mRefreshHeaderViewHeight = mRefreshViewHolder.getRefreshHeaderViewHeight();
            mMinWholeHeaderViewPaddingTop = -mRefreshHeaderViewHeight;
            mMaxWholeHeaderViewPaddingTop = (int) (mRefreshHeaderViewHeight * mRefreshViewHolder.getSpringDistanceScale());
            mWholeHeaderView.setPadding(0, mMinWholeHeaderViewPaddingTop, 0, 0);
            mWholeHeaderView.addView(mRefreshHeaderView, 0);
        }
    }
    /**
     * 设置下拉刷新控件下方的自定义控件
     *
     * @param customHeaderView 下拉刷新控件下方的自定义控件
     * @param scrollable       是否可以滚动
     */
    public void setCustomHeaderView(View customHeaderView, boolean scrollable) {
        if (mCustomHeaderView != null && mCustomHeaderView.getParent() != null) {
            ViewGroup parent = (ViewGroup) mCustomHeaderView.getParent();
            parent.removeView(mCustomHeaderView);
        }
        mCustomHeaderView = customHeaderView;
        if (mCustomHeaderView != null) {
            mCustomHeaderView.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
            mWholeHeaderView.addView(mCustomHeaderView);
            mIsCustomHeaderViewScrollable = scrollable;
        }
    }
    /**
     * 初始化上拉加载更多控件
     *
     * @return
     */
    private void initLoadMoreFooterView() {
        mLoadMoreFooterView = mRefreshViewHolder.getLoadMoreFooterView();
        if (mLoadMoreFooterView != null) {
            // 测量上拉加载更多控件的高度
            mLoadMoreFooterView.measure(0, 0);
            mLoadMoreFooterViewHeight = mLoadMoreFooterView.getMeasuredHeight();
            mLoadMoreFooterView.setVisibility(GONE);
        }
    }
    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();
        // 被添加到窗口后再设置监听器,这样开发者就不必烦恼先初始化RefreshLayout还是先设置自定义滚动监听器
        if (!mIsInitedContentViewScrollListener && mLoadMoreFooterView != null) {
            setRecyclerViewOnScrollListener();
            setAbsListViewOnScrollListener();
            addView(mLoadMoreFooterView, getChildCount());
            mIsInitedContentViewScrollListener = true;
        }
    }
    private void setRecyclerViewOnScrollListener() {
        if (mRecyclerView != null) {
            mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
                @Override
                public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
                    if ((newState == RecyclerView.SCROLL_STATE_IDLE || newState == RecyclerView.SCROLL_STATE_SETTLING) && shouldHandleRecyclerViewLoadingMore(mRecyclerView)) {
                        beginLoadingMore();
                    }
                }
            });
        }
    }
    private void setAbsListViewOnScrollListener() {
        if (mAbsListView != null) {
            try {
                // 通过反射获取开发者自定义的滚动监听器,并将其替换成自己的滚动监听器,触发滚动时也要通知开发者自定义的滚动监听器(非侵入式,不让开发者继承特定的控件)
                // mAbsListView.getClass().getDeclaredField("mOnScrollListener")获取不到mOnScrollListener,必须通过AbsListView.class.getDeclaredField("mOnScrollListener")获取
                Field field = AbsListView.class.getDeclaredField("mOnScrollListener");
                field.setAccessible(true);
                // 开发者自定义的滚动监听器
                final AbsListView.OnScrollListener onScrollListener = (AbsListView.OnScrollListener) field.get(mAbsListView);
                mAbsListView.setOnScrollListener(new AbsListView.OnScrollListener() {
                    @Override
                    public void onScrollStateChanged(AbsListView absListView, int scrollState) {
                        if ((scrollState == SCROLL_STATE_IDLE || scrollState == SCROLL_STATE_FLING) && shouldHandleAbsListViewLoadingMore(mAbsListView)) {
                            beginLoadingMore();
                        }
                        if (onScrollListener != null) {
                            onScrollListener.onScrollStateChanged(absListView, scrollState);
                        }
                    }
                    @Override
                    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
                        if (onScrollListener != null) {
                            onScrollListener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount);
                        }
                    }
                });
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    public boolean shouldHandleAbsListViewLoadingMore(AbsListView absListView) {
        if (mIsLoadingMore || mCurrentRefreshStatus == RefreshStatus.REFRESHING || mLoadMoreFooterView == null || mDelegate == null || absListView == null || absListView.getAdapter() == null || absListView.getAdapter().getCount() == 0) {
            return false;
        }
        return BGARefreshScrollingUtil.isAbsListViewToBottom(absListView);
    }
    public boolean shouldHandleRecyclerViewLoadingMore(RecyclerView recyclerView) {
        if (mIsLoadingMore || mCurrentRefreshStatus == RefreshStatus.REFRESHING || mLoadMoreFooterView == null || mDelegate == null || recyclerView.getAdapter() == null || recyclerView.getAdapter().getItemCount() == 0) {
            return false;
        }
        return BGARefreshScrollingUtil.isRecyclerViewToBottom(recyclerView);
    }
    /**
     * 是否满足处理刷新的条件
     *
     * @return
     */
    private boolean shouldHandleLoadingMore() {
        if (mIsLoadingMore || mCurrentRefreshStatus == RefreshStatus.REFRESHING || mLoadMoreFooterView == null || mDelegate == null) {
            return false;
        }
        // 内容是普通控件,满足
        if (mNormalView != null) {
            return true;
        }
        if (BGARefreshScrollingUtil.isWebViewToBottom(mWebView)) {
            return true;
        }
        if (BGARefreshScrollingUtil.isScrollViewToBottom(mScrollView)) {
            return true;
        }
        if (mAbsListView != null) {
            return shouldHandleAbsListViewLoadingMore(mAbsListView);
        }
        if (mRecyclerView != null) {
            return shouldHandleRecyclerViewLoadingMore(mRecyclerView);
        }
        if (mStickyNavLayout != null) {
            return mStickyNavLayout.shouldHandleLoadingMore();
        }
        if (BGARefreshScrollingUtil.isNestedScrollLayoutToBottom(mNestedScrollLayout)) {
            return true;
        }
        return false;
    }
    @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                mInterceptTouchDownX = event.getRawX();
                mInterceptTouchDownY = event.getRawY();
                break;
            case MotionEvent.ACTION_MOVE:
                if (!mIsLoadingMore && (mCurrentRefreshStatus != RefreshStatus.REFRESHING)) {
                    if (mInterceptTouchDownX == -1) {
                        mInterceptTouchDownX = (int) event.getRawX();
                    }
                    if (mInterceptTouchDownY == -1) {
                        mInterceptTouchDownY = (int) event.getRawY();
                    }
                    int interceptTouchMoveDistanceY = (int) (event.getRawY() - mInterceptTouchDownY);
                    // 可以没有上拉加载更多,但是必须有下拉刷新,否则就不拦截事件
                    if (Math.abs(event.getRawX() - mInterceptTouchDownX) < Math.abs(interceptTouchMoveDistanceY) && mRefreshHeaderView != null) {
                        if ((interceptTouchMoveDistanceY > mTouchSlop && shouldHandleRefresh()) || (interceptTouchMoveDistanceY < -mTouchSlop && shouldHandleLoadingMore()) || (interceptTouchMoveDistanceY < -mTouchSlop && !isWholeHeaderViewCompleteInvisible()) || (interceptTouchMoveDistanceY > mTouchSlop && shouldInterceptToMoveCustomHeaderViewDown())) {
                            // ACTION_DOWN时没有消耗掉事件,子控件会处于按下状态,这里设置ACTION_CANCEL,使子控件取消按下状态
                            event.setAction(MotionEvent.ACTION_CANCEL);
                            super.onInterceptTouchEvent(event);
                            return true;
                        }
                    }
                }
                break;
            case MotionEvent.ACTION_CANCEL:
            case MotionEvent.ACTION_UP:
                // 重置
                mInterceptTouchDownX = -1;
                mInterceptTouchDownY = -1;
                break;
        }
        return super.onInterceptTouchEvent(event);
    }
    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        if (mIsCustomHeaderViewScrollable && !isWholeHeaderViewCompleteInvisible()) {
            super.dispatchTouchEvent(ev);
            return true;
        }
        return super.dispatchTouchEvent(ev);
    }
    /**
     * 是否满足处理刷新的条件
     *
     * @return
     */
    private boolean shouldHandleRefresh() {
        if (!mPullDownRefreshEnable || mIsLoadingMore || mCurrentRefreshStatus == RefreshStatus.REFRESHING || mRefreshHeaderView == null || mDelegate == null) {
            return false;
        }
        return isContentViewToTop();
    }
    private boolean isContentViewToTop() {
        // 内容是普通控件,满足
        if (mNormalView != null) {
            return true;
        }
        if (BGARefreshScrollingUtil.isScrollViewOrWebViewToTop(mWebView)) {
            return true;
        }
        if (BGARefreshScrollingUtil.isScrollViewOrWebViewToTop(mScrollView)) {
            return true;
        }
        if (BGARefreshScrollingUtil.isAbsListViewToTop(mAbsListView)) {
            return true;
        }
        if (BGARefreshScrollingUtil.isRecyclerViewToTop(mRecyclerView)) {
            return true;
        }
        if (BGARefreshScrollingUtil.isStickyNavLayoutToTop(mStickyNavLayout)) {
            return true;
        }
        if (BGARefreshScrollingUtil.isScrollViewOrWebViewToTop(mNestedScrollLayout)) {
            return true;
        }
        return false;
    }
    private boolean shouldInterceptToMoveCustomHeaderViewDown() {
        return isContentViewToTop() && mCustomHeaderView != null && mIsCustomHeaderViewScrollable && !isCustomHeaderViewCompleteVisible();
    }
    private boolean shouldInterceptToMoveCustomHeaderViewUp() {
        return isContentViewToTop() && mCustomHeaderView != null && mIsCustomHeaderViewScrollable && !isWholeHeaderViewCompleteInvisible();
    }
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (null != mRefreshHeaderView) {
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    mWholeHeaderDownY = (int) event.getY();
                    if (mCustomHeaderView != null) {
                        mWholeHeaderViewDownPaddingTop = mWholeHeaderView.getPaddingTop();
                    }
                    if (mCustomHeaderView == null || !mIsCustomHeaderViewScrollable) {
                        mRefreshDownY = (int) event.getY();
                    }
                    if (isWholeHeaderViewCompleteInvisible()) {
                        mRefreshDownY = (int) event.getY();
                        return true;
                    }
                    break;
                case MotionEvent.ACTION_MOVE:
                    if (handleActionMove(event)) {
                        return true;
                    }
                    break;
                case MotionEvent.ACTION_CANCEL:
                case MotionEvent.ACTION_UP:
                    if (handleActionUpOrCancel(event)) {
                        return true;
                    }
                    break;
                default:
                    break;
            }
        }
        return super.onTouchEvent(event);
    }
    /**
     * 自定义头部控件是否已经完全显示
     *
     * @return true表示已经完全显示,false表示没有完全显示
     */
    private boolean isCustomHeaderViewCompleteVisible() {
        if (mCustomHeaderView != null) {
            // 0表示x,1表示y
            int[] location = new int[2];
            getLocationOnScreen(location);
            int mOnScreenY = location[1];
            mCustomHeaderView.getLocationOnScreen(location);
            int customHeaderViewOnScreenY = location[1];
            if (mOnScreenY <= customHeaderViewOnScreenY) {
                return true;
            } else {
                return false;
            }
        }
        return true;
    }
    /**
     * 整个头部控件是否已经完全隐藏
     *
     * @return true表示完全隐藏,false表示没有完全隐藏
     */
    private boolean isWholeHeaderViewCompleteInvisible() {
        if (mCustomHeaderView != null && mIsCustomHeaderViewScrollable) {
            // 0表示x,1表示y
            int[] location = new int[2];
            getLocationOnScreen(location);
            int mOnScreenY = location[1];
            mWholeHeaderView.getLocationOnScreen(location);
            int wholeHeaderViewOnScreenY = location[1];
            if (wholeHeaderViewOnScreenY + mWholeHeaderView.getMeasuredHeight() <= mOnScreenY) {
                return true;
            } else {
                return false;
            }
        }
        return true;
    }
    /**
     * 处理手指滑动事件
     *
     * @param event
     * @return true表示自己消耗掉该事件,false表示不消耗该事件
     */
    private boolean handleActionMove(MotionEvent event) {
        if (mCurrentRefreshStatus == RefreshStatus.REFRESHING || mIsLoadingMore) {
            return false;
        }
        if ((mCustomHeaderView == null || !mIsCustomHeaderViewScrollable) && mRefreshDownY == -1) {
            mRefreshDownY = (int) event.getY();
        }
        if (mCustomHeaderView != null && mIsCustomHeaderViewScrollable && isCustomHeaderViewCompleteVisible() && mRefreshDownY == -1) {
            mRefreshDownY = (int) event.getY();
        }
        int refreshDiffY = (int) event.getY() - mRefreshDownY;
        refreshDiffY = (int) (refreshDiffY / mRefreshViewHolder.getPaddingTopScale());
        // 如果是向下拉,并且当前可见的第一个条目的索引等于0,才处理整个头部控件的padding
        if (refreshDiffY > 0 && shouldHandleRefresh() && isCustomHeaderViewCompleteVisible()) {
            int paddingTop = mMinWholeHeaderViewPaddingTop + refreshDiffY;
            if (paddingTop > 0 && mCurrentRefreshStatus != RefreshStatus.RELEASE_REFRESH) {
                // 下拉刷新控件完全显示,并且当前状态没有处于释放开始刷新状态
                mCurrentRefreshStatus = RefreshStatus.RELEASE_REFRESH;
                handleRefreshStatusChanged();
                mRefreshViewHolder.handleScale(1.0f, refreshDiffY);
                if (mRefreshScaleDelegate != null) {
                    mRefreshScaleDelegate.onRefreshScaleChanged(1.0f, refreshDiffY);
                }
            } else if (paddingTop < 0) {
                // 下拉刷新控件没有完全显示,并且当前状态没有处于下拉刷新状态
                if (mCurrentRefreshStatus != RefreshStatus.PULL_DOWN) {
                    boolean isPreRefreshStatusNotIdle = mCurrentRefreshStatus != RefreshStatus.IDLE;
                    mCurrentRefreshStatus = RefreshStatus.PULL_DOWN;
                    if (isPreRefreshStatusNotIdle) {
                        handleRefreshStatusChanged();
                    }
                }
                float scale = 1 - paddingTop * 1.0f / mMinWholeHeaderViewPaddingTop;
                /**
                 * 往下滑
                 * paddingTop    mMinWholeHeaderViewPaddingTop 到 0
                 * scale         0 到 1
                 * 往上滑
                 * paddingTop    0 到 mMinWholeHeaderViewPaddingTop
                 * scale         1 到 0
                 */
                mRefreshViewHolder.handleScale(scale, refreshDiffY);
                if (mRefreshScaleDelegate != null) {
                    mRefreshScaleDelegate.onRefreshScaleChanged(scale, refreshDiffY);
                }
            }
            paddingTop = Math.min(paddingTop, mMaxWholeHeaderViewPaddingTop);
            mWholeHeaderView.setPadding(0, paddingTop, 0, 0);
            if (mRefreshViewHolder.canChangeToRefreshingStatus()) {
                mWholeHeaderDownY = -1;
                mRefreshDownY = -1;
                beginRefreshing();
            }
            return true;
        }
        if (mCustomHeaderView != null && mIsCustomHeaderViewScrollable) {
            if (mWholeHeaderDownY == -1) {
                mWholeHeaderDownY = (int) event.getY();
                if (mCustomHeaderView != null) {
                    mWholeHeaderViewDownPaddingTop = mWholeHeaderView.getPaddingTop();
                }
            }
            int wholeHeaderDiffY = (int) event.getY() - mWholeHeaderDownY;
            if ((mPullDownRefreshEnable && !isWholeHeaderViewCompleteInvisible()) || (wholeHeaderDiffY > 0 && shouldInterceptToMoveCustomHeaderViewDown()) || (wholeHeaderDiffY < 0 && shouldInterceptToMoveCustomHeaderViewUp())) {
                int paddingTop = mWholeHeaderViewDownPaddingTop + wholeHeaderDiffY;
                if (paddingTop < mMinWholeHeaderViewPaddingTop - mCustomHeaderView.getMeasuredHeight()) {
                    paddingTop = mMinWholeHeaderViewPaddingTop - mCustomHeaderView.getMeasuredHeight();
                }
                mWholeHeaderView.setPadding(0, paddingTop, 0, 0);
                return true;
            }
        }
        return false;
    }
    /**
     * 处理手指抬起事件
     *
     * @return true表示自己消耗掉该事件,false表示不消耗该事件
     */
    private boolean handleActionUpOrCancel(MotionEvent event) {
        boolean isReturnTrue = false;
        // 如果当前头部刷新控件没有完全隐藏,则需要返回true,自己消耗ACTION_UP事件
        if ((mCustomHeaderView == null || (mCustomHeaderView != null && !mIsCustomHeaderViewScrollable)) && mWholeHeaderView.getPaddingTop() != mMinWholeHeaderViewPaddingTop) {
            isReturnTrue = true;
        }
        if (mCurrentRefreshStatus == RefreshStatus.PULL_DOWN || mCurrentRefreshStatus == RefreshStatus.IDLE) {
            // 处于下拉刷新状态,松手时隐藏下拉刷新控件
            if (mCustomHeaderView == null || (mCustomHeaderView != null && mWholeHeaderView.getPaddingTop() < 0 && mWholeHeaderView.getPaddingTop() > mMinWholeHeaderViewPaddingTop)) {
                hiddenRefreshHeaderView();
            }
            mCurrentRefreshStatus = RefreshStatus.IDLE;
            handleRefreshStatusChanged();
        } else if (mCurrentRefreshStatus == RefreshStatus.RELEASE_REFRESH) {
            // 处于松开进入刷新状态,松手时完全显示下拉刷新控件,进入正在刷新状态
            beginRefreshing();
        }
        if (mRefreshDownY == -1) {
            mRefreshDownY = (int) event.getY();
        }
        int diffY = (int) event.getY() - mRefreshDownY;
        if (shouldHandleLoadingMore() && diffY <= 0) {
            // 处理上拉加载更多,需要返回true,自己消耗ACTION_UP事件
            isReturnTrue = true;
            beginLoadingMore();
        }
        mWholeHeaderDownY = -1;
        mRefreshDownY = -1;
        return isReturnTrue;
    }
    /**
     * 处理下拉刷新控件状态变化
     */
    private void handleRefreshStatusChanged() {
        switch (mCurrentRefreshStatus) {
            case IDLE:
                mRefreshViewHolder.changeToIdle();
                break;
            case PULL_DOWN:
                mRefreshViewHolder.changeToPullDown();
                break;
            case RELEASE_REFRESH:
                mRefreshViewHolder.changeToReleaseRefresh();
                break;
            case REFRESHING:
                mRefreshViewHolder.changeToRefreshing();
                break;
            default:
                break;
        }
    }
    /**
     * 切换到正在刷新状态,会调用delegate的onBGARefreshLayoutBeginRefreshing方法
     */
    public void beginRefreshing() {
        if (mCurrentRefreshStatus != RefreshStatus.REFRESHING && mDelegate != null) {
            mCurrentRefreshStatus = RefreshStatus.REFRESHING;
            changeRefreshHeaderViewToZero();
            handleRefreshStatusChanged();
            mDelegate.onBGARefreshLayoutBeginRefreshing(this);
        }
    }
    /**
     * 结束下拉刷新
     */
    public void endRefreshing() {
        if (mCurrentRefreshStatus == RefreshStatus.REFRESHING) {
            mCurrentRefreshStatus = RefreshStatus.IDLE;
            hiddenRefreshHeaderView();
            handleRefreshStatusChanged();
            mRefreshViewHolder.onEndRefreshing();
        }
    }
    /**
     * 隐藏下拉刷新控件,带动画
     */
    private void hiddenRefreshHeaderView() {
        ValueAnimator animator = ValueAnimator.ofInt(mWholeHeaderView.getPaddingTop(), mMinWholeHeaderViewPaddingTop);
        animator.setDuration(mRefreshViewHolder.getTopAnimDuration());
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                int paddingTop = (int) animation.getAnimatedValue();
                mWholeHeaderView.setPadding(0, paddingTop, 0, 0);
            }
        });
        animator.start();
    }
    /**
     * 设置下拉刷新控件的paddingTop到0,带动画
     */
    private void changeRefreshHeaderViewToZero() {
        ValueAnimator animator = ValueAnimator.ofInt(mWholeHeaderView.getPaddingTop(), 0);
        animator.setDuration(mRefreshViewHolder.getTopAnimDuration());
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                int paddingTop = (int) animation.getAnimatedValue();
                mWholeHeaderView.setPadding(0, paddingTop, 0, 0);
            }
        });
        animator.start();
    }
    /**
     * 开始上拉加载更多,会触发delegate的onBGARefreshLayoutBeginRefreshing方法
     */
    public void beginLoadingMore() {
        if (!mIsLoadingMore && mLoadMoreFooterView != null && mDelegate != null && mDelegate.onBGARefreshLayoutBeginLoadingMore(this)) {
            mIsLoadingMore = true;
            if (mIsShowLoadingMoreView) {
                showLoadingMoreView();
            }
        }
    }
    /**
     * 显示上拉加载更多控件
     */
    private void showLoadingMoreView() {
        mRefreshViewHolder.changeToLoadingMore();
        mLoadMoreFooterView.setVisibility(VISIBLE);
        BGARefreshScrollingUtil.scrollToBottom(mScrollView);
        BGARefreshScrollingUtil.scrollToBottom(mRecyclerView);
        BGARefreshScrollingUtil.scrollToBottom(mAbsListView);
        if (mStickyNavLayout != null) {
            mStickyNavLayout.scrollToBottom();
        }
    }
    /**
     * 结束上拉加载更多
     */
    public void endLoadingMore() {
        if (mIsLoadingMore) {
            if (mIsShowLoadingMoreView) {
                // 避免WiFi环境下请求数据太快,加载更多控件一闪而过
                mHandler.postDelayed(mDelayHiddenLoadingMoreViewTask, 300);
            } else {
                mIsLoadingMore = false;
            }
        }
    }
    private Runnable mDelayHiddenLoadingMoreViewTask = new Runnable() {
        @Override
        public void run() {
            mIsLoadingMore = false;
            mRefreshViewHolder.onEndLoadingMore();
            mLoadMoreFooterView.setVisibility(GONE);
        }
    };
    /**
     * 上拉加载更多时是否显示加载更多控件
     *
     * @param isShowLoadingMoreView
     */
    public void setIsShowLoadingMoreView(boolean isShowLoadingMoreView) {
        mIsShowLoadingMoreView = isShowLoadingMoreView;
    }
    /**
     * 设置下拉刷新是否可用
     *
     * @param pullDownRefreshEnable
     */
    public void setPullDownRefreshEnable(boolean pullDownRefreshEnable) {
        mPullDownRefreshEnable = pullDownRefreshEnable;
    }
    /**
     * 设置下拉刷新上拉加载更多代理
     *
     * @param delegate
     */
    public void setDelegate(BGARefreshLayoutDelegate delegate) {
        mDelegate = delegate;
    }
    /**
     * 设置下拉刷新控件可见时,上下拉进度的代理
     *
     * @param refreshScaleDelegate
     */
    public void setRefreshScaleDelegate(BGARefreshScaleDelegate refreshScaleDelegate) {
        mRefreshScaleDelegate = refreshScaleDelegate;
    }
    /**
     * 获取当前下拉刷新的状态
     *
     * @return
     */
    public RefreshStatus getCurrentRefreshStatus() {
        return mCurrentRefreshStatus;
    }
    /**
     * 是否处于正在加载更多状态
     *
     * @return
     */
    public boolean isLoadingMore() {
        return mIsLoadingMore;
    }
    public interface BGARefreshLayoutDelegate {
        /**
         * 开始刷新
         */
        void onBGARefreshLayoutBeginRefreshing(BGARefreshLayout refreshLayout);
        /**
         * 开始加载更多。由于监听了ScrollView、RecyclerView、AbsListView滚动到底部的事件,所以这里采用返回boolean来处理是否加载更多。否则使用endLoadingMore方法会失效
         *
         * @param refreshLayout
         * @return 如果要开始加载更多则返回true,否则返回false。(返回false的场景:没有网络、一共只有x页数据并且已经加载了x页数据了)
         */
        boolean onBGARefreshLayoutBeginLoadingMore(BGARefreshLayout refreshLayout);
    }
    public interface BGARefreshScaleDelegate {
        /**
         * 下拉刷新控件可见时,处理上下拉进度
         *
         * @param scale         下拉过程0 到 1,回弹过程1 到 0,没有加上弹簧距离移动时的比例
         * @param moveYDistance 整个下拉刷新控件paddingTop变化的值,如果有弹簧距离,会大于整个下拉刷新控件的高度
         */
        void onRefreshScaleChanged(float scale, int moveYDistance);
    }
    public enum RefreshStatus {
        IDLE, PULL_DOWN, RELEASE_REFRESH, REFRESHING
    }
    public static int dp2px(Context context, int dpValue) {
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpValue, context.getResources().getDisplayMetrics());
    }
}
app/src/main/java/com/hdl/photovoltaic/widget/refreshlayout/BGARefreshScrollingUtil.java
New file
@@ -0,0 +1,292 @@
/**
 * Copyright 2015 bingoogolapple
 * <p/>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p/>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p/>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.hdl.photovoltaic.widget.refreshlayout;
import android.content.Context;
import android.graphics.Rect;
import android.util.DisplayMetrics;
import android.view.View;
import android.view.ViewParent;
import android.view.WindowManager;
import android.webkit.WebView;
import android.widget.AbsListView;
import android.widget.ScrollView;
import androidx.core.view.ViewCompat;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.recyclerview.widget.StaggeredGridLayoutManager;
import java.lang.reflect.Field;
/**
 * 作者:王浩 邮件:bingoogolapple@gmail.com
 * 创建时间:15/10/28 上午2:26
 * 描述:
 */
public class BGARefreshScrollingUtil {
    private BGARefreshScrollingUtil() {
    }
    public static boolean isScrollViewOrWebViewToTop(View view) {
        return view != null && view.getScrollY() == 0;
    }
    public static boolean isAbsListViewToTop(AbsListView absListView) {
        if (absListView != null) {
            int firstChildTop = 0;
            if (absListView.getChildCount() > 0) {
                // 如果AdapterView的子控件数量不为0,获取第一个子控件的top
                firstChildTop = absListView.getChildAt(0).getTop() - absListView.getPaddingTop();
            }
            if (absListView.getFirstVisiblePosition() == 0 && firstChildTop == 0) {
                return true;
            }
        }
        return false;
    }
    public static boolean isRecyclerViewToTop(RecyclerView recyclerView) {
        if (recyclerView != null) {
            RecyclerView.LayoutManager manager = recyclerView.getLayoutManager();
            if (manager == null) {
                return true;
            }
            if (manager.getItemCount() == 0) {
                return true;
            }
            if (manager instanceof LinearLayoutManager) {
                LinearLayoutManager layoutManager = (LinearLayoutManager) manager;
                int firstChildTop = 0;
                if (recyclerView.getChildCount() > 0) {
                    // 处理item高度超过一屏幕时的情况
                    View firstVisibleChild = recyclerView.getChildAt(0);
                    if (firstVisibleChild != null && firstVisibleChild.getMeasuredHeight() >= recyclerView.getMeasuredHeight()) {
                        if (android.os.Build.VERSION.SDK_INT < 14) {
                            return !(ViewCompat.canScrollVertically(recyclerView, -1) || recyclerView.getScrollY() > 0);
                        } else {
                            return !ViewCompat.canScrollVertically(recyclerView, -1);
                        }
                    }
                    // 如果RecyclerView的子控件数量不为0,获取第一个子控件的top
                    // 解决item的topMargin不为0时不能触发下拉刷新
                    View firstChild = recyclerView.getChildAt(0);
                    RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) firstChild.getLayoutParams();
                    firstChildTop = firstChild.getTop() - layoutParams.topMargin - getRecyclerViewItemTopInset(layoutParams) - recyclerView.getPaddingTop();
                }
                if (layoutManager.findFirstCompletelyVisibleItemPosition() < 1 && firstChildTop == 0) {
                    return true;
                }
            } else if (manager instanceof StaggeredGridLayoutManager) {
                StaggeredGridLayoutManager layoutManager = (StaggeredGridLayoutManager) manager;
                int[] out = layoutManager.findFirstCompletelyVisibleItemPositions(null);
                if (out[0] < 1) {
                    return true;
                }
            }
        }
        return false;
    }
    /**
     * 通过反射获取RecyclerView的item的topInset
     *
     * @param layoutParams
     * @return
     */
    private static int getRecyclerViewItemTopInset(RecyclerView.LayoutParams layoutParams) {
        try {
            Field field = RecyclerView.LayoutParams.class.getDeclaredField("mDecorInsets");
            field.setAccessible(true);
            // 开发者自定义的滚动监听器
            Rect decorInsets = (Rect) field.get(layoutParams);
            return decorInsets.top;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return 0;
    }
    public static boolean isStickyNavLayoutToTop(BGAStickyNavLayout stickyNavLayout) {
        return isScrollViewOrWebViewToTop(stickyNavLayout) && stickyNavLayout.isContentViewToTop();
    }
    public static boolean isWebViewToBottom(WebView webView) {
        return webView != null && webView.getContentHeight() * webView.getScale() == (webView.getScrollY() + webView.getMeasuredHeight());
    }
    public static boolean isScrollViewToBottom(ScrollView scrollView) {
        if (scrollView != null) {
            int scrollContentHeight = scrollView.getScrollY() + scrollView.getMeasuredHeight() - scrollView.getPaddingTop() - scrollView.getPaddingBottom();
            int realContentHeight = scrollView.getChildAt(0).getMeasuredHeight();
            if (scrollContentHeight == realContentHeight) {
                return true;
            }
        }
        return false;
    }
    public static boolean isNestedScrollLayoutToBottom(NestedScrollLayout nestedScrollLayout) {
        if (nestedScrollLayout != null) {
            int scrollContentHeight = nestedScrollLayout.getScrollY() + nestedScrollLayout.getMeasuredHeight() - nestedScrollLayout.getPaddingTop() - nestedScrollLayout.getPaddingBottom();
            int realContentHeight = nestedScrollLayout.getChildAt(0).getMeasuredHeight();
            if (scrollContentHeight == realContentHeight) {
                return true;
            }
        }
        return false;
    }
    public static boolean isAbsListViewToBottom(AbsListView absListView) {
        if (absListView != null && absListView.getAdapter() != null && absListView.getChildCount() > 0 && absListView.getLastVisiblePosition() == absListView.getAdapter().getCount() - 1) {
            View lastChild = absListView.getChildAt(absListView.getChildCount() - 1);
            BGAStickyNavLayout stickyNavLayout = getStickyNavLayout(absListView);
            if (stickyNavLayout != null) {
                // 处理BGAStickyNavLayout中lastChild.getBottom() <= absListView.getMeasuredHeight()失效问题
                // 0表示x,1表示y
                int[] location = new int[2];
                lastChild.getLocationOnScreen(location);
                int lastChildBottomOnScreen = location[1] + lastChild.getMeasuredHeight();
                stickyNavLayout.getLocationOnScreen(location);
                int stickyNavLayoutBottomOnScreen = location[1] + stickyNavLayout.getMeasuredHeight();
                return lastChildBottomOnScreen + absListView.getPaddingBottom() <= stickyNavLayoutBottomOnScreen;
            } else {
                return lastChild.getBottom() <= absListView.getMeasuredHeight();
            }
        }
        return false;
    }
    public static boolean isRecyclerViewToBottom(RecyclerView recyclerView) {
        if (recyclerView != null) {
            RecyclerView.LayoutManager manager = recyclerView.getLayoutManager();
            if (manager == null || manager.getItemCount() == 0) {
                return false;
            }
            if (manager instanceof LinearLayoutManager) {
                // 处理item高度超过一屏幕时的情况
                View lastVisibleChild = recyclerView.getChildAt(recyclerView.getChildCount() - 1);
                if (lastVisibleChild != null && lastVisibleChild.getMeasuredHeight() >= recyclerView.getMeasuredHeight()) {
                    if (android.os.Build.VERSION.SDK_INT < 14) {
                        return !(ViewCompat.canScrollVertically(recyclerView, 1) || recyclerView.getScrollY() < 0);
                    } else {
                        return !ViewCompat.canScrollVertically(recyclerView, 1);
                    }
                }
                LinearLayoutManager layoutManager = (LinearLayoutManager) manager;
                if (layoutManager.findLastCompletelyVisibleItemPosition() == layoutManager.getItemCount() - 1) {
                    BGAStickyNavLayout stickyNavLayout = getStickyNavLayout(recyclerView);
                    if (stickyNavLayout != null) {
                        // 处理BGAStickyNavLayout中findLastCompletelyVisibleItemPosition失效问题
                        View lastCompletelyVisibleChild = layoutManager.getChildAt(layoutManager.findLastCompletelyVisibleItemPosition());
                        if (lastCompletelyVisibleChild == null) {
                            return true;
                        } else {
                            // 0表示x,1表示y
                            int[] location = new int[2];
                            lastCompletelyVisibleChild.getLocationOnScreen(location);
                            int lastChildBottomOnScreen = location[1] + lastCompletelyVisibleChild.getMeasuredHeight();
                            stickyNavLayout.getLocationOnScreen(location);
                            int stickyNavLayoutBottomOnScreen = location[1] + stickyNavLayout.getMeasuredHeight();
                            return lastChildBottomOnScreen <= stickyNavLayoutBottomOnScreen;
                        }
                    } else {
                        return true;
                    }
                }
            } else if (manager instanceof StaggeredGridLayoutManager) {
                StaggeredGridLayoutManager layoutManager = (StaggeredGridLayoutManager) manager;
                int[] out = layoutManager.findLastCompletelyVisibleItemPositions(null);
                int lastPosition = layoutManager.getItemCount() - 1;
                for (int position : out) {
                    if (position == lastPosition) {
                        return true;
                    }
                }
            }
        }
        return false;
    }
    public static void scrollToBottom(final ScrollView scrollView) {
        if (scrollView != null) {
            scrollView.post(new Runnable() {
                @Override
                public void run() {
                    scrollView.fullScroll(ScrollView.FOCUS_DOWN);
                }
            });
        }
    }
    public static void scrollToBottom(final AbsListView absListView) {
        if (absListView != null) {
            if (absListView.getAdapter() != null && absListView.getAdapter().getCount() > 0) {
                absListView.post(new Runnable() {
                    @Override
                    public void run() {
                        absListView.setSelection(absListView.getAdapter().getCount() - 1);
                    }
                });
            }
        }
    }
    public static void scrollToBottom(final RecyclerView recyclerView) {
        if (recyclerView != null) {
            if (recyclerView.getAdapter() != null && recyclerView.getAdapter().getItemCount() > 0) {
                recyclerView.post(new Runnable() {
                    @Override
                    public void run() {
                        recyclerView.smoothScrollToPosition(recyclerView.getAdapter().getItemCount() - 1);
                    }
                });
            }
        }
    }
    public static BGAStickyNavLayout getStickyNavLayout(View view) {
        ViewParent viewParent = view.getParent();
        while (viewParent != null) {
            if (viewParent instanceof BGAStickyNavLayout) {
                return (BGAStickyNavLayout) viewParent;
            }
            viewParent = viewParent.getParent();
        }
        return null;
    }
    public static int getScreenHeight(Context context) {
        WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
        DisplayMetrics dm = new DisplayMetrics();
        windowManager.getDefaultDisplay().getMetrics(dm);
        return dm.heightPixels;
    }
}
app/src/main/java/com/hdl/photovoltaic/widget/refreshlayout/BGARefreshViewHolder.java
New file
@@ -0,0 +1,345 @@
/**
 * Copyright 2015 bingoogolapple
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.hdl.photovoltaic.widget.refreshlayout;
import android.content.Context;
import android.graphics.Color;
import android.graphics.drawable.AnimationDrawable;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.ColorRes;
import androidx.annotation.DrawableRes;
import com.hdl.photovoltaic.R;
/**
 * 作者:王浩 邮件:bingoogolapple@gmail.com
 * 创建时间:15/5/21 12:56
 * 描述:继承该抽象类实现响应的抽象方法,做出各种下拉刷新效果。参考BGANormalRefreshViewHolder、BGAStickinessRefreshViewHolder、BGAMoocStyleRefreshViewHolder、BGAMeiTuanRefreshViewHolder
 */
public abstract class BGARefreshViewHolder {
    /**
     * 手指移动距离与下拉刷新控件paddingTop移动距离的比值
     */
    private static final float PULL_DISTANCE_SCALE = 1.8f;
    /**
     * 手指移动距离与下拉刷新控件paddingTop移动距离的比值,默认1.8f
     */
    private float mPullDistanceScale = PULL_DISTANCE_SCALE;
    /**
     * 下拉刷新控件paddingTop的弹簧距离与下拉刷新控件高度的比值
     */
    private static final float SPRING_DISTANCE_SCALE = 0.4f;
    /**
     * 下拉刷新控件paddingTop的弹簧距离与下拉刷新控件高度的比值,默认0.4f
     */
    private float mSpringDistanceScale = SPRING_DISTANCE_SCALE;
    protected Context mContext;
    /**
     * 下拉刷新上拉加载更多控件
     */
    protected BGARefreshLayout mRefreshLayout;
    /**
     * 下拉刷新控件
     */
    protected View mRefreshHeaderView;
    /**
     * 上拉加载更多控件
     */
    protected View mLoadMoreFooterView;
    /**
     * 底部加载更多提示控件
     */
    protected TextView mFooterStatusTv;
    /**
     * 底部加载更多菊花控件
     */
    protected ImageView mFooterChrysanthemumIv;
    /**
     * 底部加载更多菊花drawable
     */
    protected AnimationDrawable mFooterChrysanthemumAd;
    /**
     * 正在加载更多时的文本
     */
    protected String mLodingMoreText = "加载中...";
    /**
     * 是否开启加载更多功能
     */
    private boolean mIsLoadingMoreEnabled = true;
    /**
     * 整个加载更多控件的背景颜色资源id
     */
    private int mLoadMoreBackgroundColorRes = -1;
    /**
     * 整个加载更多控件的背景drawable资源id
     */
    private int mLoadMoreBackgroundDrawableRes = -1;
    /**
     * 下拉刷新控件的背景颜色资源id
     */
    protected int mRefreshViewBackgroundColorRes = -1;
    /**
     * 下拉刷新控件的背景drawable资源id
     */
    protected int mRefreshViewBackgroundDrawableRes = -1;
    /**
     * 头部控件移动动画时常
     */
    private int mTopAnimDuration = 500;
    /**
     * @param context
     * @param isLoadingMoreEnabled 上拉加载更多是否可用
     */
    public BGARefreshViewHolder(Context context, boolean isLoadingMoreEnabled) {
        mContext = context;
        mIsLoadingMoreEnabled = isLoadingMoreEnabled;
    }
    /**
     * 设置正在加载更多时的文本
     *
     * @param loadingMoreText
     */
    public void setLoadingMoreText(String loadingMoreText) {
        mLodingMoreText = loadingMoreText;
    }
    /**
     * 设置整个加载更多控件的背景颜色资源id
     *
     * @param loadMoreBackgroundColorRes
     */
    public void setLoadMoreBackgroundColorRes(@ColorRes int loadMoreBackgroundColorRes) {
        mLoadMoreBackgroundColorRes = loadMoreBackgroundColorRes;
    }
    /**
     * 设置整个加载更多控件的背景drawable资源id
     *
     * @param loadMoreBackgroundDrawableRes
     */
    public void setLoadMoreBackgroundDrawableRes(@DrawableRes int loadMoreBackgroundDrawableRes) {
        mLoadMoreBackgroundDrawableRes = loadMoreBackgroundDrawableRes;
    }
    /**
     * 设置下拉刷新控件的背景颜色资源id
     *
     * @param refreshViewBackgroundColorRes
     */
    public void setRefreshViewBackgroundColorRes(@ColorRes int refreshViewBackgroundColorRes) {
        mRefreshViewBackgroundColorRes = refreshViewBackgroundColorRes;
    }
    /**
     * 设置下拉刷新控件的背景drawable资源id
     *
     * @param refreshViewBackgroundDrawableRes
     */
    public void setRefreshViewBackgroundDrawableRes(@DrawableRes int refreshViewBackgroundDrawableRes) {
        mRefreshViewBackgroundDrawableRes = refreshViewBackgroundDrawableRes;
    }
    /**
     * 获取顶部未满足下拉刷新条件时回弹到初始状态、满足刷新条件时回弹到正在刷新状态、刷新完毕后回弹到初始状态的动画时间,默认为500毫秒
     *
     * @return
     */
    public int getTopAnimDuration() {
        return mTopAnimDuration;
    }
    /**
     * 设置顶部未满足下拉刷新条件时回弹到初始状态、满足刷新条件时回弹到正在刷新状态、刷新完毕后回弹到初始状态的动画时间,默认为300毫秒
     *
     * @param topAnimDuration
     */
    public void setTopAnimDuration(int topAnimDuration) {
        mTopAnimDuration = topAnimDuration;
    }
    /**
     * 获取上拉加载更多控件,如果不喜欢这种上拉刷新风格可重写该方法实现自定义LoadMoreFooterView
     *
     * @return
     */
    public View getLoadMoreFooterView() {
        if (!mIsLoadingMoreEnabled) {
            return null;
        }
        if (mLoadMoreFooterView == null) {
            mLoadMoreFooterView = View.inflate(mContext, R.layout.loading_alert, null);
            mLoadMoreFooterView.setBackgroundColor(Color.TRANSPARENT);
            if (mLoadMoreBackgroundColorRes != -1) {
                mLoadMoreFooterView.setBackgroundResource(mLoadMoreBackgroundColorRes);
            }
            if (mLoadMoreBackgroundDrawableRes != -1) {
                mLoadMoreFooterView.setBackgroundResource(mLoadMoreBackgroundDrawableRes);
            }
            mFooterStatusTv = (TextView) mLoadMoreFooterView.findViewById(R.id.load_content_tv);
            mFooterChrysanthemumIv = (ImageView) mLoadMoreFooterView.findViewById(R.id.load_pb);
            mFooterChrysanthemumIv.setBackground(mContext.getDrawable(R.drawable.progress_small));
            mFooterChrysanthemumAd = (AnimationDrawable) mFooterChrysanthemumIv.getDrawable();
            mFooterStatusTv.setText(mLodingMoreText);
        }
        return mLoadMoreFooterView;
    }
    /**
     * 获取头部下拉刷新控件
     *
     * @return
     */
    public abstract View getRefreshHeaderView();
    /**
     * 下拉刷新控件可见时,处理上下拉进度
     *
     * @param scale         下拉过程0 到 1,回弹过程1 到 0,没有加上弹簧距离移动时的比例
     * @param moveYDistance 整个下拉刷新控件paddingTop变化的值,如果有弹簧距离,会大于整个下拉刷新控件的高度
     */
    public abstract void handleScale(float scale, int moveYDistance);
    /**
     * 进入到未处理下拉刷新状态
     */
    public abstract void changeToIdle();
    /**
     * 进入下拉状态
     */
    public abstract void changeToPullDown();
    /**
     * 进入释放刷新状态
     */
    public abstract void changeToReleaseRefresh();
    /**
     * 进入正在刷新状态
     */
    public abstract void changeToRefreshing();
    /**
     * 结束下拉刷新
     */
    public abstract void onEndRefreshing();
    /**
     * 手指移动距离与下拉刷新控件paddingTop移动距离的比值
     *
     * @return
     */
    public float getPaddingTopScale() {
        return mPullDistanceScale;
    }
    /**
     * 设置手指移动距离与下拉刷新控件paddingTop移动距离的比值
     *
     * @param pullDistanceScale
     */
    public void setPullDistanceScale(float pullDistanceScale) {
        mPullDistanceScale = pullDistanceScale;
    }
    /**
     * 下拉刷新控件paddingTop的弹簧距离与下拉刷新控件高度的比值
     *
     * @return
     */
    public float getSpringDistanceScale() {
        return mSpringDistanceScale;
    }
    /**
     * 设置下拉刷新控件paddingTop的弹簧距离与下拉刷新控件高度的比值,不能小于0,如果刷新控件比较高,建议将该值设置小一些
     *
     * @param springDistanceScale
     */
    public void setSpringDistanceScale(float springDistanceScale) {
        if (springDistanceScale < 0) {
            throw new RuntimeException("下拉刷新控件paddingTop的弹簧距离与下拉刷新控件高度的比值springDistanceScale不能小于0");
        }
        mSpringDistanceScale = springDistanceScale;
    }
    /**
     * 是处于能够进入刷新状态
     *
     * @return
     */
    public boolean canChangeToRefreshingStatus() {
        return false;
    }
    /**
     * 进入加载更多状态
     */
    public void changeToLoadingMore() {
        if (mIsLoadingMoreEnabled && mFooterChrysanthemumAd != null) {
            mFooterChrysanthemumAd.start();
        }
    }
    /**
     * 结束上拉加载更多
     */
    public void onEndLoadingMore() {
        if (mIsLoadingMoreEnabled && mFooterChrysanthemumAd != null) {
            mFooterChrysanthemumAd.stop();
        }
    }
    /**
     * 获取下拉刷新控件的高度,如果初始化时的高度和最后展开的最大高度不一致,需重写该方法返回最大高度
     *
     * @return
     */
    public int getRefreshHeaderViewHeight() {
        if (mRefreshHeaderView != null) {
            // 测量下拉刷新控件的高度
            mRefreshHeaderView.measure(0, 0);
            return mRefreshHeaderView.getMeasuredHeight();
        }
        return 0;
    }
    /**
     * 改变整个下拉刷新头部控件移动一定的距离(带动画),自定义刷新控件进入刷新状态前后的高度有变化时可以使用该方法(参考BGAStickinessRefreshView)
     *
     * @param distance
     */
    public void startChangeWholeHeaderViewPaddingTop(int distance) {
        mRefreshLayout.startChangeWholeHeaderViewPaddingTop(distance);
    }
    /**
     * 设置下拉刷新上拉加载更多控件,该方法是设置BGARefreshViewHolder给BGARefreshLayout时由BGARefreshLayout调用
     *
     * @param refreshLayout
     */
    public void setRefreshLayout(BGARefreshLayout refreshLayout) {
        mRefreshLayout = refreshLayout;
    }
}
app/src/main/java/com/hdl/photovoltaic/widget/refreshlayout/BGAStickyNavLayout.java
New file
@@ -0,0 +1,520 @@
/**
 * Copyright 2015 bingoogolapple
 * <p/>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p/>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p/>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.hdl.photovoltaic.widget.refreshlayout;
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import android.webkit.WebView;
import android.widget.AbsListView;
import android.widget.LinearLayout;
import android.widget.OverScroller;
import android.widget.ScrollView;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentPagerAdapter;
import androidx.fragment.app.FragmentStatePagerAdapter;
import androidx.recyclerview.widget.RecyclerView;
import androidx.viewpager.widget.PagerAdapter;
import androidx.viewpager.widget.ViewPager;
/**
 * 作者:王浩 邮件:bingoogolapple@gmail.com
 * 创建时间:15/10/28 上午2:32
 * 描述:
 */
public class BGAStickyNavLayout extends LinearLayout {
    private View mHeaderView;
    private View mNavView;
    private View mContentView;
    private View mDirectNormalView;
    private RecyclerView mDirectRecyclerView;
    private AbsListView mDirectAbsListView;
    private ScrollView mDirectScrollView;
    private WebView mDirectWebView;
    private ViewPager mDirectViewPager;
    private View mNestedContentView;
    private View mNestedNormalView;
    private RecyclerView mNestedRecyclerView;
    private AbsListView mNestedAbsListView;
    private ScrollView mNestedScrollView;
    private WebView mNestedWebView;
    private OverScroller mOverScroller;
    private VelocityTracker mVelocityTracker;
    private int mTouchSlop;
    private int mMaximumVelocity;
    private int mMinimumVelocity;
    private boolean mIsInControl = true;
    private float mLastDispatchY;
    private float mLastTouchY;
    public BGARefreshLayout mRefreshLayout;
    public BGAStickyNavLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }
    private void init(Context context) {
        setOrientation(VERTICAL);
        mOverScroller = new OverScroller(context);
        final ViewConfiguration configuration = ViewConfiguration.get(context);
        mTouchSlop = configuration.getScaledTouchSlop();
        mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
        mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();
    }
    @Override
    public void setOrientation(int orientation) {
        if (VERTICAL == orientation) {
            super.setOrientation(VERTICAL);
        }
    }
    @Override
    public void onFinishInflate() {
        super.onFinishInflate();
        if (getChildCount() != 3) {
            throw new IllegalStateException(BGAStickyNavLayout.class.getSimpleName() + "必须有且只有三个子控件");
        }
        mHeaderView = getChildAt(0);
        mNavView = getChildAt(1);
        mContentView = getChildAt(2);
        if (mContentView instanceof AbsListView) {
            mDirectAbsListView = (AbsListView) mContentView;
            mDirectAbsListView.setOnScrollListener(mLvOnScrollListener);
        } else if (mContentView instanceof RecyclerView) {
            mDirectRecyclerView = (RecyclerView) mContentView;
            mDirectRecyclerView.addOnScrollListener(mRvOnScrollListener);
        } else if (mContentView instanceof ScrollView) {
            mDirectScrollView = (ScrollView) mContentView;
        } else if (mContentView instanceof WebView) {
            mDirectWebView = (WebView) mContentView;
        } else if (mContentView instanceof ViewPager) {
            mDirectViewPager = (ViewPager) mContentView;
            mDirectViewPager.addOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
                @Override
                public void onPageSelected(int position) {
                    regetNestedContentView();
                }
            });
        } else {
            mDirectNormalView = mContentView;
        }
    }
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        measureChild(mContentView, widthMeasureSpec, MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(heightMeasureSpec) - getNavViewHeight(), MeasureSpec.EXACTLY));
    }
    @Override
    public void computeScroll() {
        if (mOverScroller.computeScrollOffset()) {
            scrollTo(0, mOverScroller.getCurrY());
            invalidate();
        }
    }
    public void fling(int velocityY) {
        mOverScroller.fling(0, getScrollY(), 0, velocityY, 0, 0, 0, getHeaderViewHeight());
        invalidate();
    }
    @Override
    public void scrollTo(int x, int y) {
        if (y < 0) {
            y = 0;
        }
        int headerViewHeight = getHeaderViewHeight();
        if (y > headerViewHeight) {
            y = headerViewHeight;
        }
        if (y != getScrollY()) {
            super.scrollTo(x, y);
        }
    }
    /**
     * 获取头部视图高度,包括topMargin和bottomMargin
     *
     * @return
     */
    private int getHeaderViewHeight() {
        MarginLayoutParams layoutParams = (MarginLayoutParams) mHeaderView.getLayoutParams();
        return mHeaderView.getMeasuredHeight() + layoutParams.topMargin + layoutParams.bottomMargin;
    }
    /**
     * 获取导航视图的高度,包括topMargin和bottomMargin
     *
     * @return
     */
    private int getNavViewHeight() {
        MarginLayoutParams layoutParams = (MarginLayoutParams) mNavView.getLayoutParams();
        return mNavView.getMeasuredHeight() + layoutParams.topMargin + layoutParams.bottomMargin;
    }
    /**
     * 头部视图是否已经完全隐藏
     *
     * @return
     */
    private boolean isHeaderViewCompleteInvisible() {
        // 0表示x,1表示y
        int[] location = new int[2];
        getLocationOnScreen(location);
        int contentOnScreenTopY = location[1] + getPaddingTop();
        mNavView.getLocationOnScreen(location);
        MarginLayoutParams params = (MarginLayoutParams) mNavView.getLayoutParams();
        int navViewTopOnScreenY = location[1] - params.topMargin;
        if (navViewTopOnScreenY == contentOnScreenTopY) {
            return true;
        } else {
            return false;
        }
    }
    private void initVelocityTrackerIfNotExists() {
        if (mVelocityTracker == null) {
            mVelocityTracker = VelocityTracker.obtain();
        }
    }
    private void recycleVelocityTracker() {
        if (mVelocityTracker != null) {
            mVelocityTracker.recycle();
            mVelocityTracker = null;
        }
    }
    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        float currentTouchY = ev.getY();
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                mLastDispatchY = currentTouchY;
                break;
            case MotionEvent.ACTION_MOVE:
                float differentY = currentTouchY - mLastDispatchY;
                mLastDispatchY = currentTouchY;
                if (isContentViewToTop() && isHeaderViewCompleteInvisible()) {
                    if (differentY >= 0 && !mIsInControl) {
                        mIsInControl = true;
                        return resetDispatchTouchEvent(ev);
                    }
                    if (differentY <= 0 && mIsInControl) {
                        mIsInControl = false;
                        return resetDispatchTouchEvent(ev);
                    }
                }
                break;
        }
        return super.dispatchTouchEvent(ev);
    }
    private boolean resetDispatchTouchEvent(MotionEvent ev) {
        MotionEvent newEvent = MotionEvent.obtain(ev);
        ev.setAction(MotionEvent.ACTION_CANCEL);
        dispatchTouchEvent(ev);
        newEvent.setAction(MotionEvent.ACTION_DOWN);
        return dispatchTouchEvent(newEvent);
    }
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        float currentTouchY = ev.getY();
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                mLastTouchY = currentTouchY;
                break;
            case MotionEvent.ACTION_MOVE:
                float differentY = currentTouchY - mLastTouchY;
                if (Math.abs(differentY) > mTouchSlop) {
                    if (!isHeaderViewCompleteInvisible() || (isContentViewToTop() && isHeaderViewCompleteInvisible() && mIsInControl)) {
                        mLastTouchY = currentTouchY;
                        return true;
                    }
                }
                break;
        }
        return super.onInterceptTouchEvent(ev);
    }
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        initVelocityTrackerIfNotExists();
        mVelocityTracker.addMovement(event);
        float currentTouchY = event.getY();
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                if (!mOverScroller.isFinished()) {
                    mOverScroller.abortAnimation();
                }
                mLastTouchY = currentTouchY;
                break;
            case MotionEvent.ACTION_MOVE:
                float differentY = currentTouchY - mLastTouchY;
                mLastTouchY = currentTouchY;
                if (Math.abs(differentY) > 0) {
                    scrollBy(0, (int) -differentY);
                }
                break;
            case MotionEvent.ACTION_CANCEL:
                recycleVelocityTracker();
                if (!mOverScroller.isFinished()) {
                    mOverScroller.abortAnimation();
                }
                break;
            case MotionEvent.ACTION_UP:
                mVelocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
                int initialVelocity = (int) mVelocityTracker.getYVelocity();
                if ((Math.abs(initialVelocity) > mMinimumVelocity)) {
                    fling(-initialVelocity);
                }
                recycleVelocityTracker();
                break;
        }
        return true;
    }
    public boolean isContentViewToTop() {
        if (mDirectNormalView != null) {
            return true;
        }
        if (BGARefreshScrollingUtil.isScrollViewOrWebViewToTop(mDirectWebView)) {
            return true;
        }
        if (BGARefreshScrollingUtil.isScrollViewOrWebViewToTop(mDirectScrollView)) {
            return true;
        }
        if (BGARefreshScrollingUtil.isAbsListViewToTop(mDirectAbsListView)) {
            return true;
        }
        if (BGARefreshScrollingUtil.isRecyclerViewToTop(mDirectRecyclerView)) {
            return true;
        }
        if (mDirectViewPager != null) {
            return isViewPagerContentViewToTop();
        }
        return false;
    }
    private boolean isViewPagerContentViewToTop() {
        if (mNestedContentView == null) {
            regetNestedContentView();
        }
        if (mDirectNormalView != null) {
            return true;
        }
        if (BGARefreshScrollingUtil.isScrollViewOrWebViewToTop(mNestedWebView)) {
            return true;
        }
        if (BGARefreshScrollingUtil.isScrollViewOrWebViewToTop(mNestedScrollView)) {
            return true;
        }
        if (BGARefreshScrollingUtil.isAbsListViewToTop(mNestedAbsListView)) {
            return true;
        }
        if (BGARefreshScrollingUtil.isRecyclerViewToTop(mNestedRecyclerView)) {
            return true;
        }
        return false;
    }
    /**
     * 重新获取嵌套的内容视图
     */
    private void regetNestedContentView() {
        int currentItem = mDirectViewPager.getCurrentItem();
        PagerAdapter adapter = mDirectViewPager.getAdapter();
        if (adapter instanceof FragmentPagerAdapter || adapter instanceof FragmentStatePagerAdapter) {
            Fragment item = (Fragment) adapter.instantiateItem(mDirectViewPager, currentItem);
            mNestedContentView = item.getView();
            // 清空之前的
            mNestedNormalView = null;
            mNestedAbsListView = null;
            mNestedRecyclerView = null;
            mNestedScrollView = null;
            mNestedWebView = null;
            if (mNestedContentView instanceof AbsListView) {
                mNestedAbsListView = (AbsListView) mNestedContentView;
                mNestedAbsListView.setOnScrollListener(mLvOnScrollListener);
                if (!isHeaderViewCompleteInvisible()) {
                    mNestedAbsListView.setSelection(0);
                }
            } else if (mNestedContentView instanceof RecyclerView) {
                mNestedRecyclerView = (RecyclerView) mNestedContentView;
                mNestedRecyclerView.removeOnScrollListener(mRvOnScrollListener);
                mNestedRecyclerView.addOnScrollListener(mRvOnScrollListener);
                if (!isHeaderViewCompleteInvisible()) {
                    mNestedRecyclerView.scrollToPosition(0);
                }
            } else if (mNestedContentView instanceof ScrollView) {
                mNestedScrollView = (ScrollView) mNestedContentView;
                if (!isHeaderViewCompleteInvisible()) {
                    mNestedScrollView.scrollTo(mNestedScrollView.getScrollX(), 0);
                }
            } else if (mNestedContentView instanceof WebView) {
                mNestedWebView = (WebView) mNestedContentView;
                if (!isHeaderViewCompleteInvisible()) {
                    mNestedWebView.scrollTo(mNestedWebView.getScrollX(), 0);
                }
            } else {
                mNestedNormalView = mNestedContentView;
            }
        } else {
            throw new IllegalStateException(BGAStickyNavLayout.class.getSimpleName() + "的第三个子控件为ViewPager时,其adapter必须是FragmentPagerAdapter或者FragmentStatePagerAdapter");
        }
    }
    public void setRefreshLayout(BGARefreshLayout refreshLayout) {
        mRefreshLayout = refreshLayout;
    }
    private RecyclerView.OnScrollListener mRvOnScrollListener = new RecyclerView.OnScrollListener() {
        @Override
        public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
            if ((newState == RecyclerView.SCROLL_STATE_IDLE || newState == RecyclerView.SCROLL_STATE_SETTLING) && mRefreshLayout != null && mRefreshLayout.shouldHandleRecyclerViewLoadingMore(recyclerView)) {
                mRefreshLayout.beginLoadingMore();
            }
        }
    };
    private AbsListView.OnScrollListener mLvOnScrollListener = new AbsListView.OnScrollListener() {
        @Override
        public void onScrollStateChanged(AbsListView absListView, int scrollState) {
            if ((scrollState == SCROLL_STATE_IDLE || scrollState == SCROLL_STATE_FLING) && mRefreshLayout != null && mRefreshLayout.shouldHandleAbsListViewLoadingMore(absListView)) {
                mRefreshLayout.beginLoadingMore();
            }
        }
        @Override
        public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
        }
    };
    public boolean shouldHandleLoadingMore() {
        if (mRefreshLayout == null) {
            return false;
        }
        if (mDirectNormalView != null) {
            return true;
        }
        if (BGARefreshScrollingUtil.isWebViewToBottom(mDirectWebView)) {
            return true;
        }
        if (BGARefreshScrollingUtil.isScrollViewToBottom(mDirectScrollView)) {
            return true;
        }
        if (mDirectAbsListView != null) {
            return mRefreshLayout.shouldHandleAbsListViewLoadingMore(mDirectAbsListView);
        }
        if (mDirectRecyclerView != null) {
            return mRefreshLayout.shouldHandleRecyclerViewLoadingMore(mDirectRecyclerView);
        }
        if (mDirectViewPager != null) {
            if (mNestedContentView == null) {
                regetNestedContentView();
            }
            if (mNestedNormalView != null) {
                return true;
            }
            if (BGARefreshScrollingUtil.isWebViewToBottom(mNestedWebView)) {
                return true;
            }
            if (BGARefreshScrollingUtil.isScrollViewToBottom(mNestedScrollView)) {
                return true;
            }
            if (mNestedAbsListView != null) {
                return mRefreshLayout.shouldHandleAbsListViewLoadingMore(mNestedAbsListView);
            }
            if (mNestedRecyclerView != null) {
                return mRefreshLayout.shouldHandleRecyclerViewLoadingMore(mNestedRecyclerView);
            }
        }
        return false;
    }
    public void scrollToBottom() {
        BGARefreshScrollingUtil.scrollToBottom(mDirectScrollView);
        BGARefreshScrollingUtil.scrollToBottom(mDirectRecyclerView);
        BGARefreshScrollingUtil.scrollToBottom(mDirectAbsListView);
        if (mDirectViewPager != null) {
            if (mNestedContentView == null) {
                regetNestedContentView();
            }
            BGARefreshScrollingUtil.scrollToBottom(mNestedScrollView);
            BGARefreshScrollingUtil.scrollToBottom(mNestedRecyclerView);
            BGARefreshScrollingUtil.scrollToBottom(mNestedAbsListView);
        }
    }
}
app/src/main/java/com/hdl/photovoltaic/widget/refreshlayout/HDLRefreshView.java
New file
@@ -0,0 +1,127 @@
/**
 * Copyright 2015 bingoogolapple
 * <p/>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p/>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p/>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.hdl.photovoltaic.widget.refreshlayout;
import android.content.Context;
import android.graphics.drawable.AnimationDrawable;
import android.util.AttributeSet;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import androidx.annotation.DrawableRes;
import androidx.core.view.ViewCompat;
import com.hdl.photovoltaic.R;
public class HDLRefreshView extends RelativeLayout {
    private ImageView mPullDownView;
    private ImageView mReleaseRefreshingView;
    private AnimationDrawable mChangeToReleaseRefreshAd;
    private AnimationDrawable mRefreshingAd;
    private int mChangeToReleaseRefreshAnimResId;
    private int mRefreshingAnimResId;
    public HDLRefreshView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        mPullDownView = (ImageView) findViewById(R.id.iv_pull_down);
        mReleaseRefreshingView = (ImageView) findViewById(R.id.iv_release_refreshing);
    }
    public void setPullDownImageResource(@DrawableRes int resId) {
        mPullDownView.setImageResource(resId);
    }
    public void setChangeToReleaseRefreshAnimResId(@DrawableRes int resId) {
        mChangeToReleaseRefreshAnimResId = resId;
        mReleaseRefreshingView.setImageResource(mChangeToReleaseRefreshAnimResId);
    }
    public void setRefreshingAnimResId(@DrawableRes int resId) {
        mRefreshingAnimResId = resId;
    }
    public void handleScale(float scale) {
        scale = 0.1f + 0.9f * scale;
        ViewCompat.setPivotX(mPullDownView, mPullDownView.getWidth() * 0.5f);
        ViewCompat.setPivotY(mPullDownView, mPullDownView.getHeight() * 0.5f);
        ViewCompat.setScaleX(mPullDownView, scale);
        ViewCompat.setScaleY(mPullDownView, scale);
    }
    public void changeToIdle() {
        stopChangeToReleaseRefreshAd();
        stopRefreshingAd();
        mPullDownView.setVisibility(VISIBLE);
        mReleaseRefreshingView.setVisibility(INVISIBLE);
    }
    public void changeToPullDown() {
        mPullDownView.setVisibility(VISIBLE);
        mReleaseRefreshingView.setVisibility(INVISIBLE);
    }
    public void changeToReleaseRefresh() {
        mReleaseRefreshingView.setImageResource(mChangeToReleaseRefreshAnimResId);
        mChangeToReleaseRefreshAd = (AnimationDrawable) mReleaseRefreshingView.getDrawable();
        mReleaseRefreshingView.setVisibility(VISIBLE);
        mPullDownView.setVisibility(INVISIBLE);
        mChangeToReleaseRefreshAd.start();
    }
    public void changeToRefreshing() {
        stopChangeToReleaseRefreshAd();
        mReleaseRefreshingView.setImageResource(mRefreshingAnimResId);
        mRefreshingAd = (AnimationDrawable) mReleaseRefreshingView.getDrawable();
        mReleaseRefreshingView.setVisibility(VISIBLE);
        mPullDownView.setVisibility(INVISIBLE);
        mRefreshingAd.start();
    }
    public void onEndRefreshing() {
        stopChangeToReleaseRefreshAd();
        stopRefreshingAd();
    }
    private void stopRefreshingAd() {
        if (mRefreshingAd != null) {
            mRefreshingAd.stop();
            mRefreshingAd = null;
        }
    }
    private void stopChangeToReleaseRefreshAd() {
        if (mChangeToReleaseRefreshAd != null) {
            mChangeToReleaseRefreshAd.stop();
            mChangeToReleaseRefreshAd = null;
        }
    }
}
app/src/main/java/com/hdl/photovoltaic/widget/refreshlayout/HDLRefreshViewHolder.java
New file
@@ -0,0 +1,131 @@
/**
 * Copyright 2015 bingoogolapple
 * <p/>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p/>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p/>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.hdl.photovoltaic.widget.refreshlayout;
import android.content.Context;
import android.graphics.Color;
import android.view.View;
import androidx.annotation.DrawableRes;
import com.hdl.photovoltaic.R;
public class HDLRefreshViewHolder extends BGARefreshViewHolder {
    private HDLRefreshView mRefreshView;
    private int mPullDownImageResId = -1;
    private int mChangeToReleaseRefreshAnimResId = -1;
    private int mRefreshingAnimResId = -1;
    /**
     * @param context
     * @param isLoadingMoreEnabled 上拉加载更多是否可用
     */
    public HDLRefreshViewHolder(Context context, boolean isLoadingMoreEnabled) {
        super(context, isLoadingMoreEnabled);
    }
    /**
     * 设置下拉过程中的图片资源
     *
     * @param resId
     */
    public void setPullDownImageResource(@DrawableRes int resId) {
        mPullDownImageResId = resId;
    }
    /**
     * 设置进入释放刷新状态时的动画资源
     *
     * @param resId
     */
    public void setChangeToReleaseRefreshAnimResId(@DrawableRes int resId) {
        mChangeToReleaseRefreshAnimResId = resId;
    }
    /**
     * 设置正在刷新时的动画资源
     *
     * @param resId
     */
    public void setRefreshingAnimResId(@DrawableRes int resId) {
        mRefreshingAnimResId = resId;
    }
    @Override
    public View getRefreshHeaderView() {
        if (mRefreshHeaderView == null) {
            mRefreshHeaderView = View.inflate(mContext, R.layout.view_refresh_header, null);
            mRefreshHeaderView.setBackgroundColor(Color.TRANSPARENT);
            if (mRefreshViewBackgroundColorRes != -1) {
                mRefreshHeaderView.setBackgroundResource(mRefreshViewBackgroundColorRes);
            }
            if (mRefreshViewBackgroundDrawableRes != -1) {
                mRefreshHeaderView.setBackgroundResource(mRefreshViewBackgroundDrawableRes);
            }
            mRefreshView = (HDLRefreshView) mRefreshHeaderView.findViewById(R.id.refreshView);
            if (mPullDownImageResId != -1) {
                mRefreshView.setPullDownImageResource(mPullDownImageResId);
            } else {
                throw new RuntimeException("请调用" + HDLRefreshViewHolder.class.getSimpleName() + "的setPullDownImageResource方法设置下拉过程中的图片资源");
            }
            if (mChangeToReleaseRefreshAnimResId != -1) {
                mRefreshView.setChangeToReleaseRefreshAnimResId(mChangeToReleaseRefreshAnimResId);
            } else {
                throw new RuntimeException("请调用" + HDLRefreshViewHolder.class.getSimpleName() + "的setChangeToReleaseRefreshAnimResId方法设置进入释放刷新状态时的动画资源");
            }
            if (mRefreshingAnimResId != -1) {
                mRefreshView.setRefreshingAnimResId(mRefreshingAnimResId);
            } else {
                throw new RuntimeException("请调用" + HDLRefreshViewHolder.class.getSimpleName() + "的setRefreshingAnimResId方法设置正在刷新时的动画资源");
            }
        }
        return mRefreshHeaderView;
    }
    @Override
    public void handleScale(float scale, int moveYDistance) {
        if (scale <= 1.0f) {
            mRefreshView.handleScale(scale);
        }
    }
    @Override
    public void changeToIdle() {
        mRefreshView.changeToIdle();
    }
    @Override
    public void changeToPullDown() {
        mRefreshView.changeToPullDown();
    }
    @Override
    public void changeToReleaseRefresh() {
        mRefreshView.changeToReleaseRefresh();
    }
    @Override
    public void changeToRefreshing() {
        mRefreshView.changeToRefreshing();
    }
    @Override
    public void onEndRefreshing() {
        mRefreshView.onEndRefreshing();
    }
}
app/src/main/java/com/hdl/photovoltaic/widget/refreshlayout/NestedScrollLayout.java
New file
@@ -0,0 +1,152 @@
package com.hdl.photovoltaic.widget.refreshlayout;
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.widget.NestedScrollView;
import androidx.recyclerview.widget.RecyclerView;
import io.dcloud.weex.FlingHelper;
public class NestedScrollLayout extends NestedScrollView {
    private View topView;
    private ViewGroup contentView;
    private static final String TAG = "NestedScrollLayout";
    public NestedScrollLayout(Context context) {
        this(context, null);
        init();
    }
    public NestedScrollLayout(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
        init();
    }
    public NestedScrollLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        this(context, attrs, defStyleAttr, 0);
        init();
    }
    public NestedScrollLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr);
        init();
    }
    private FlingHelper mFlingHelper;
    int totalDy = 0;
    /**
     * 用于判断RecyclerView是否在fling
     */
    boolean isStartFling = false;
    /**
     * 记录当前滑动的y轴加速度
     */
    private int velocityY = 0;
    private void init() {
        mFlingHelper = new FlingHelper(getContext());
        setOnScrollChangeListener(new View.OnScrollChangeListener() {
            @Override
            public void onScrollChange(View v, int scrollX, int scrollY, int oldScrollX, int oldScrollY) {
                if (isStartFling) {
                    totalDy = 0;
                    isStartFling = false;
                }
                if (scrollY == 0) {
                    Log.e(TAG, "TOP SCROLL");
                    // refreshLayout.setEnabled(true);
                }
                if (scrollY == (getChildAt(0).getMeasuredHeight() - v.getMeasuredHeight())) {
                    Log.e(TAG, "BOTTOM SCROLL");
                    dispatchChildFling();
                }
                //在RecyclerView fling情况下,记录当前RecyclerView在y轴的偏移
                totalDy += scrollY - oldScrollY;
            }
        });
    }
    private void dispatchChildFling() {
        if (velocityY != 0) {
            Double splineFlingDistance = mFlingHelper.getSplineFlingDistance(velocityY);
            if (splineFlingDistance > totalDy) {
                childFling(mFlingHelper.getVelocityByDistance(splineFlingDistance - Double.valueOf(totalDy)));
            }
        }
        totalDy = 0;
        velocityY = 0;
    }
    private void childFling(int velY) {
        RecyclerView childRecyclerView = getChildRecyclerView(contentView);
        if (childRecyclerView != null) {
            childRecyclerView.fling(0, velY);
        }
    }
    @Override
    public void fling(int velocityY) {
        super.fling(velocityY);
        if (velocityY <= 0) {
            this.velocityY = 0;
        } else {
            isStartFling = true;
            this.velocityY = velocityY;
        }
    }
    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        topView = ((ViewGroup) getChildAt(0)).getChildAt(0);
        contentView = (ViewGroup) ((ViewGroup) getChildAt(0)).getChildAt(1);
    }
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        // 调整contentView的高度为父容器高度,使之填充布局,避免父容器滚动后出现空白
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        ViewGroup.LayoutParams lp = contentView.getLayoutParams();
        lp.height = getMeasuredHeight();
        contentView.setLayoutParams(lp);
    }
    /**
     * 解决滑动冲突:RecyclerView在滑动之前会问下父布局是否需要拦截,父布局使用此方法
     */
    @Override
    public void onNestedPreScroll(@NonNull View target, int dx, int dy, @NonNull int[] consumed, int type) {
//        Log.e("NestedScrollLayout", getScrollY() + "::onNestedPreScroll::" + topView.getMeasuredHeight() + "::dy::" + dy);
        // 向上滑动。若当前topview可见,需要将topview滑动至不可见
        boolean hideTop = dy > 0 && getScrollY() < topView.getMeasuredHeight();
        if (hideTop) {
            scrollBy(0, dy);
            //  这个是被消费的距离,如果没有会被重复消费,现象是父布局与子布局同时滑动
            consumed[1] = dy;
        }
    }
    private RecyclerView getChildRecyclerView(ViewGroup viewGroup) {
        for (int i = 0; i < viewGroup.getChildCount(); i++) {
            View view = viewGroup.getChildAt(i);
            if (view instanceof RecyclerView) {
                return (RecyclerView) viewGroup.getChildAt(i);
            } else if (viewGroup.getChildAt(i) instanceof ViewGroup) {
                ViewGroup childRecyclerView = getChildRecyclerView((ViewGroup) viewGroup.getChildAt(i));
                if (childRecyclerView instanceof RecyclerView) {
                    return (RecyclerView) childRecyclerView;
                }
            }
            continue;
        }
        return null;
    }
}
app/src/main/res/drawable-xhdpi/loading_gif.gif

app/src/main/res/drawable/loading_44.png
app/src/main/res/drawable/loading_gif.png

app/src/main/res/drawable/loading_refresh.xml
New file
@@ -0,0 +1,136 @@
<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
    android:oneshot="false">
    <item
        android:drawable="@mipmap/loading_1"
        android:duration="100" />
    <!--    <item-->
    <!--        android:drawable="@mipmap/loading_2"-->
    <!--        android:duration="100" />-->
    <!--    <item-->
    <!--        android:drawable="@mipmap/loading_3"-->
    <!--        android:duration="100" />-->
    <!--    <item-->
    <!--        android:drawable="@mipmap/loading_4"-->
    <!--        android:duration="100" />-->
    <!--    <item-->
    <!--        android:drawable="@mipmap/loading_5"-->
    <!--        android:duration="100" />-->
    <!--    <item-->
    <!--        android:drawable="@mipmap/loading_6"-->
    <!--        android:duration="100" />-->
    <item
        android:drawable="@mipmap/loading_7"
        android:duration="100" />
    <!--    <item-->
    <!--        android:drawable="@mipmap/loading_8"-->
    <!--        android:duration="100" />-->
    <!--    <item-->
    <!--        android:drawable="@mipmap/loading_9"-->
    <!--        android:duration="100" />-->
    <!--    <item-->
    <!--        android:drawable="@mipmap/loading_10"-->
    <!--        android:duration="100" />-->
    <!--    <item-->
    <!--        android:drawable="@mipmap/loading_11"-->
    <!--        android:duration="100" />-->
    <!--    <item-->
    <!--        android:drawable="@mipmap/loading_12"-->
    <!--        android:duration="100" />-->
    <item
        android:drawable="@mipmap/loading_13"
        android:duration="100" />
    <!--    <item-->
    <!--        android:drawable="@mipmap/loading_14"-->
    <!--        android:duration="100" />-->
    <!--    <item-->
    <!--        android:drawable="@mipmap/loading_15"-->
    <!--        android:duration="100" />-->
    <!--    <item-->
    <!--        android:drawable="@mipmap/loading_16"-->
    <!--        android:duration="100" />-->
    <!--    <item-->
    <!--        android:drawable="@mipmap/loading_17"-->
    <!--        android:duration="100" />-->
    <!--    <item-->
    <!--        android:drawable="@mipmap/loading_18"-->
    <!--        android:duration="100" />-->
    <item
        android:drawable="@mipmap/loading_19"
        android:duration="100" />
    <!--    <item-->
    <!--        android:drawable="@mipmap/loading_20"-->
    <!--        android:duration="100" />-->
    <!--    <item-->
    <!--        android:drawable="@mipmap/loading_21"-->
    <!--        android:duration="100" />-->
    <!--    <item-->
    <!--        android:drawable="@mipmap/loading_22"-->
    <!--        android:duration="100" />-->
    <!--    <item-->
    <!--        android:drawable="@mipmap/loading_23"-->
    <!--        android:duration="100" />-->
    <!--    <item-->
    <!--        android:drawable="@mipmap/loading_24"-->
    <!--        android:duration="100" />-->
    <item
        android:drawable="@mipmap/loading_25"
        android:duration="100" />
    <!--    <item-->
    <!--        android:drawable="@mipmap/loading_26"-->
    <!--        android:duration="100" />-->
    <!--    <item-->
    <!--        android:drawable="@mipmap/loading_27"-->
    <!--        android:duration="100" />-->
    <!--    <item-->
    <!--        android:drawable="@mipmap/loading_28"-->
    <!--        android:duration="100" />-->
    <!--    <item-->
    <!--        android:drawable="@mipmap/loading_29"-->
    <!--        android:duration="100" />-->
    <!--    <item-->
    <!--        android:drawable="@mipmap/loading_30"-->
    <!--        android:duration="100" />-->
    <item
        android:drawable="@mipmap/loading_31"
        android:duration="100" />
    <!--    <item-->
    <!--        android:drawable="@mipmap/loading_32"-->
    <!--        android:duration="100" />-->
    <!--    <item-->
    <!--        android:drawable="@mipmap/loading_33"-->
    <!--        android:duration="100" />-->
    <!--    <item-->
    <!--        android:drawable="@mipmap/loading_34"-->
    <!--        android:duration="100" />-->
    <!--    <item-->
    <!--        android:drawable="@mipmap/loading_35"-->
    <!--        android:duration="100" />-->
    <!--    <item-->
    <!--        android:drawable="@mipmap/loading_36"-->
    <!--        android:duration="100" />-->
    <item
        android:drawable="@mipmap/loading_37"
        android:duration="100" />
    <!--    <item-->
    <!--        android:drawable="@mipmap/loading_38"-->
    <!--        android:duration="100" />-->
    <!--    <item-->
    <!--        android:drawable="@mipmap/loading_39"-->
    <!--        android:duration="100" />-->
    <!--    <item-->
    <!--        android:drawable="@mipmap/loading_40"-->
    <!--        android:duration="100" />-->
    <!--    <item-->
    <!--        android:drawable="@mipmap/loading_41"-->
    <!--        android:duration="100" />-->
    <!--    <item-->
    <!--        android:drawable="@mipmap/loading_42"-->
    <!--        android:duration="100" />-->
<!--    <item-->
<!--        android:drawable="@mipmap/loading_43"-->
<!--        android:duration="150" />-->
    <item
        android:drawable="@mipmap/loading_44"
        android:duration="100" />
</animation-list>
app/src/main/res/drawable/loading_refresh_end.xml
New file
@@ -0,0 +1,138 @@
<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
    android:oneshot="false">
    <item
        android:drawable="@mipmap/loading_44"
        android:duration="100" />
    <!--    <item-->
    <!--        android:drawable="@mipmap/loading_2"-->
    <!--        android:duration="100" />-->
    <!--    <item-->
    <!--        android:drawable="@mipmap/loading_3"-->
    <!--        android:duration="100" />-->
    <!--    <item-->
    <!--        android:drawable="@mipmap/loading_4"-->
    <!--        android:duration="100" />-->
    <!--    <item-->
    <!--        android:drawable="@mipmap/loading_5"-->
    <!--        android:duration="100" />-->
    <!--    <item-->
    <!--        android:drawable="@mipmap/loading_6"-->
    <!--        android:duration="100" />-->
    <item
        android:drawable="@mipmap/loading_37"
        android:duration="100" />
    <!--    <item-->
    <!--        android:drawable="@mipmap/loading_8"-->
    <!--        android:duration="100" />-->
    <!--    <item-->
    <!--        android:drawable="@mipmap/loading_9"-->
    <!--        android:duration="100" />-->
    <!--    <item-->
    <!--        android:drawable="@mipmap/loading_10"-->
    <!--        android:duration="100" />-->
    <!--    <item-->
    <!--        android:drawable="@mipmap/loading_11"-->
    <!--        android:duration="100" />-->
    <!--    <item-->
    <!--        android:drawable="@mipmap/loading_12"-->
    <!--        android:duration="100" />-->
    <item
        android:drawable="@mipmap/loading_31"
        android:duration="100" />
    <!--    <item-->
    <!--        android:drawable="@mipmap/loading_14"-->
    <!--        android:duration="100" />-->
    <!--    <item-->
    <!--        android:drawable="@mipmap/loading_15"-->
    <!--        android:duration="100" />-->
    <!--    <item-->
    <!--        android:drawable="@mipmap/loading_16"-->
    <!--        android:duration="100" />-->
    <!--    <item-->
    <!--        android:drawable="@mipmap/loading_17"-->
    <!--        android:duration="100" />-->
    <!--    <item-->
    <!--        android:drawable="@mipmap/loading_18"-->
    <!--        android:duration="100" />-->
    <item
        android:drawable="@mipmap/loading_25"
        android:duration="100" />
    <!--    <item-->
    <!--        android:drawable="@mipmap/loading_20"-->
    <!--        android:duration="100" />-->
    <!--    <item-->
    <!--        android:drawable="@mipmap/loading_21"-->
    <!--        android:duration="100" />-->
    <!--    <item-->
    <!--        android:drawable="@mipmap/loading_22"-->
    <!--        android:duration="100" />-->
    <!--    <item-->
    <!--        android:drawable="@mipmap/loading_23"-->
    <!--        android:duration="100" />-->
    <!--    <item-->
    <!--        android:drawable="@mipmap/loading_24"-->
    <!--        android:duration="100" />-->
    <item
        android:drawable="@mipmap/loading_19"
        android:duration="100" />
    <!--    <item-->
    <!--        android:drawable="@mipmap/loading_26"-->
    <!--        android:duration="100" />-->
    <!--    <item-->
    <!--        android:drawable="@mipmap/loading_27"-->
    <!--        android:duration="100" />-->
    <!--    <item-->
    <!--        android:drawable="@mipmap/loading_28"-->
    <!--        android:duration="100" />-->
    <!--    <item-->
    <!--        android:drawable="@mipmap/loading_29"-->
    <!--        android:duration="100" />-->
    <!--    <item-->
    <!--        android:drawable="@mipmap/loading_30"-->
    <!--        android:duration="100" />-->
    <item
        android:drawable="@mipmap/loading_31"
        android:duration="100" />
    <!--    <item-->
    <!--        android:drawable="@mipmap/loading_32"-->
    <!--        android:duration="100" />-->
    <!--    <item-->
    <!--        android:drawable="@mipmap/loading_33"-->
    <!--        android:duration="100" />-->
    <!--    <item-->
    <!--        android:drawable="@mipmap/loading_34"-->
    <!--        android:duration="100" />-->
    <!--    <item-->
    <!--        android:drawable="@mipmap/loading_35"-->
    <!--        android:duration="100" />-->
    <!--    <item-->
    <!--        android:drawable="@mipmap/loading_36"-->
    <!--        android:duration="100" />-->
    <item
        android:drawable="@mipmap/loading_37"
        android:duration="100" />
    <!--    <item-->
    <!--        android:drawable="@mipmap/loading_38"-->
    <!--        android:duration="100" />-->
    <!--    <item-->
    <!--        android:drawable="@mipmap/loading_39"-->
    <!--        android:duration="100" />-->
    <!--    <item-->
    <!--        android:drawable="@mipmap/loading_40"-->
    <!--        android:duration="100" />-->
    <!--    <item-->
    <!--        android:drawable="@mipmap/loading_41"-->
    <!--        android:duration="100" />-->
    <!--    <item-->
    <!--        android:drawable="@mipmap/loading_42"-->
    <!--        android:duration="100" />-->
    <!--    <item-->
    <!--        android:drawable="@mipmap/loading_43"-->
    <!--        android:duration="150" />-->
    <item
        android:drawable="@mipmap/loading_44"
        android:duration="100" />
</animation-list>
app/src/main/res/layout/fragment_home_page.xml
@@ -37,8 +37,8 @@
            android:src="@drawable/add" />
    </RelativeLayout>
    <androidx.swiperefreshlayout.widget.SwipeRefreshLayout
        android:id="@+id/srl"
    <com.hdl.photovoltaic.widget.refreshlayout.BGARefreshLayout
        android:id="@+id/rsl"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_marginTop="@dimen/dp_10"
@@ -952,7 +952,21 @@
        </ScrollView>
    </androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
    </com.hdl.photovoltaic.widget.refreshlayout.BGARefreshLayout>
<!--    <androidx.constraintlayout.widget.Guideline-->
<!--        android:id="@+id/guideline2"-->
<!--        android:layout_width="wrap_content"-->
<!--        android:layout_height="wrap_content"-->
<!--        android:orientation="horizontal"-->
<!--        app:layout_constraintGuide_begin="20dp" />-->
<!--    <androidx.constraintlayout.widget.Guideline-->
<!--        android:id="@+id/guideline3"-->
<!--        android:layout_width="wrap_content"-->
<!--        android:layout_height="wrap_content"-->
<!--        android:orientation="vertical"-->
<!--        app:layout_constraintGuide_begin="20dp" />-->
</androidx.constraintlayout.widget.ConstraintLayout>
app/src/main/res/layout/view_refresh_header.xml
New file
@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:gravity="center"
    android:orientation="vertical">
    <com.hdl.photovoltaic.widget.refreshlayout.HDLRefreshView
        android:id="@+id/refreshView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="5dp"
        android:layout_marginBottom="5dp">
        <ImageView
            android:id="@+id/iv_pull_down"
            android:layout_width="@dimen/dp_60"
            android:layout_height="@dimen/dp_36"
            android:layout_centerInParent="true" />
        <ImageView
            android:id="@+id/iv_release_refreshing"
            android:layout_width="@dimen/dp_60"
            android:layout_height="@dimen/dp_36"
            android:layout_centerInParent="true"
            android:visibility="invisible" />
    </com.hdl.photovoltaic.widget.refreshlayout.HDLRefreshView>
</LinearLayout>
app/src/main/res/mipmap-xhdpi/loading_1.png
app/src/main/res/mipmap-xhdpi/loading_10.png
app/src/main/res/mipmap-xhdpi/loading_11.png
app/src/main/res/mipmap-xhdpi/loading_12.png
app/src/main/res/mipmap-xhdpi/loading_13.png
app/src/main/res/mipmap-xhdpi/loading_14.png
app/src/main/res/mipmap-xhdpi/loading_15.png
app/src/main/res/mipmap-xhdpi/loading_16.png
app/src/main/res/mipmap-xhdpi/loading_17.png
app/src/main/res/mipmap-xhdpi/loading_18.png
app/src/main/res/mipmap-xhdpi/loading_19.png
app/src/main/res/mipmap-xhdpi/loading_2.png
app/src/main/res/mipmap-xhdpi/loading_20.png
app/src/main/res/mipmap-xhdpi/loading_21.png
app/src/main/res/mipmap-xhdpi/loading_22.png
app/src/main/res/mipmap-xhdpi/loading_23.png
app/src/main/res/mipmap-xhdpi/loading_24.png
app/src/main/res/mipmap-xhdpi/loading_25.png
app/src/main/res/mipmap-xhdpi/loading_26.png
app/src/main/res/mipmap-xhdpi/loading_27.png
app/src/main/res/mipmap-xhdpi/loading_28.png
app/src/main/res/mipmap-xhdpi/loading_29.png
app/src/main/res/mipmap-xhdpi/loading_3.png
app/src/main/res/mipmap-xhdpi/loading_30.png
app/src/main/res/mipmap-xhdpi/loading_31.png
app/src/main/res/mipmap-xhdpi/loading_32.png
app/src/main/res/mipmap-xhdpi/loading_33.png
app/src/main/res/mipmap-xhdpi/loading_34.png
app/src/main/res/mipmap-xhdpi/loading_35.png
app/src/main/res/mipmap-xhdpi/loading_36.png
app/src/main/res/mipmap-xhdpi/loading_37.png
app/src/main/res/mipmap-xhdpi/loading_38.png
app/src/main/res/mipmap-xhdpi/loading_39.png
app/src/main/res/mipmap-xhdpi/loading_4.png
app/src/main/res/mipmap-xhdpi/loading_40.png
app/src/main/res/mipmap-xhdpi/loading_41.png
app/src/main/res/mipmap-xhdpi/loading_42.png
app/src/main/res/mipmap-xhdpi/loading_43.png
app/src/main/res/mipmap-xhdpi/loading_44.png
app/src/main/res/mipmap-xhdpi/loading_5.png
app/src/main/res/mipmap-xhdpi/loading_6.png
app/src/main/res/mipmap-xhdpi/loading_7.png
app/src/main/res/mipmap-xhdpi/loading_8.png
app/src/main/res/mipmap-xhdpi/loading_9.png