SmartRefreshLayout自定義Header和Foote
如果沒有了解SmartRefreshLayout基本使用, 請先看 SmartRefreshLayout基本使用
因為自定義Header和Foote方式基本一樣, 所以這裡介紹Header就可以了

MyHeaderView.gif
一. 官方文件介紹
我們看到官方文件實現方式:
① 自定義一個View, 實現RefreshHeader介面.
② 重寫RefreshHeader裡面所有方法
① 有部分方法我們根本就用不到, 但是還是要重寫去實現.
② 重寫用不到的方法的時候, 部分方法有返回值, 我們還得關注返回值是什麼意思.
二. 解決辦法
- 先去看看SmartRefreshLayout框架內部有沒有解決方案.
- 如果沒有, 自己寫一個基類BaseHeader實現RefreshHeader介面, 重寫介面方法, 然後自定義HeaderView繼承BaseHeader, 需要什麼方法就重寫什麼方法.
因為部分方法需要子類必須實現, 所以BaseHeader定義成抽象類
//通用的, 任意專案都可以用 public class abstract BaseHeader implements RefreshHeader{ //重寫RefreshHeader裡面的方法 ... }
//具體專案Header public class HeaderView extends BaseHeader{ //需要什麼方法就重寫什麼方法 }
SmartRefreshLayout內部解決方案:
因為框架本身就整合有預設的Header, 所以我們先看系統的ClassicsHeader是怎麼實現的.

ClassicsHeader.png
我們看到ClassicsHeader實現了RefreshHeader介面, 但是繼承的卻不是基本佈局, 而是一個自定義的佈局InternalClassics<ClassicsHeader>.

InternalClassics.jpg

InternalAbstract.png
得出繼承關係:
class ClassicsHeader extends InternalClassics implements RefreshHeader
class InternalClassics extends InternalAbstract implements RefreshInternal
class InternalAbstract extends RelativeLayout implements RefreshInternal
interface RefreshHeader extends RefreshInternal
我們看到InternalAbstract 註釋裡寫著: 實現 Header 和 Footer 時,繼承 InternalAbstract 的話可以少寫很多介面方法
於是可以這樣寫
public class MyHeaderView extends InternalAbstract{ protected MyHeaderView(@NonNull View wrapped) { super(wrapped); } protected MyHeaderView(@NonNull View wrappedView, @Nullable RefreshInternal wrappedInternal) { super(wrappedView, wrappedInternal); } protected MyHeaderView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } }
但是還有2個問題要解決:
- 自定義View佈局如何新增進去?
- 下拉過程和釋放重新整理等狀態如何監聽?
解決方法:
1: 我們看到InternalAbstract 的父類其實就是RelativeLayout, 所以在初始化的時候直接addView(headerView)就可以了
-
參考文件 結合ClassicsHeader原始碼
最基礎的寫法, 只需重寫onFinish和onStateChanged即可. 當然其它方法看具體需求
於是得到以下寫法
import com.scwang.smartrefresh.layout.api.RefreshLayout; import com.scwang.smartrefresh.layout.constant.RefreshState; import com.scwang.smartrefresh.layout.internal.InternalAbstract; /** * 自定義HeaderView */ public class MyHeaderView extends InternalAbstract{ public static String REFRESH_HEADER_PULLING = "下拉可以重新整理";//"下拉可以重新整理"; public static String REFRESH_HEADER_LOADING = "正在載入...";//"正在載入..."; public static String REFRESH_HEADER_RELEASE = "釋放立即重新整理"; public static String REFRESH_HEADER_FINISH = "重新整理成功";//"重新整理完成"; public static String REFRESH_HEADER_FAILED = "重新整理失敗";//"重新整理失敗"; private TextView mTitleText; public MyHeaderView(Context context) { this(context, null); } public MyHeaderView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public MyHeaderView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); View view = LayoutInflater.from(context).inflate(R.layout.miyuan_refresh_head, this); mTitleText = view.findViewById(R.id.txt); } @Override public int onFinish(@NonNull RefreshLayout layout, boolean success) { if (success) { mTitleText.setText(REFRESH_HEADER_FINISH); } else { mTitleText.setText(REFRESH_HEADER_FAILED); } super.onFinish(layout, success); return 500; //延遲500毫秒之後再彈回 } @Override public void onStateChanged(@NonNull RefreshLayout refreshLayout, @NonNull RefreshState oldState, @NonNull RefreshState newState) { switch (newState) { case PullDownToRefresh: //下拉過程 mTitleText.setText(REFRESH_HEADER_PULLING); break; case ReleaseToRefresh: //鬆開重新整理 mTitleText.setText(REFRESH_HEADER_RELEASE); break; case Refreshing: //loading中 mTitleText.setText(REFRESH_HEADER_LOADING); break; } } }
如果想檢視newState更多狀態碼, 可以去看看RefreshState(列舉)的原始碼
總結 自定義Header步驟:
- 自定義View 繼承 InternalAbstract.
- 初始化時, 新增自定義佈局到Header
- 重寫onStateChanged和onFinish監聽手勢滑動, 根據不同的狀態改變佈局UI.
優化
上面的MyHeaderView基本是可以用了,但是還有會出現2個問題
- 我們一個專案中, 基本上會有多頁面都會用到同一個MyHeaderView, 那我們每次都需要在xml中這麼寫.
<com.scwang.smartrefresh.layout.SmartRefreshLayout android:id="@+id/refreshLayout" android:layout_width="match_parent" android:layout_height="match_parent"> <com.liys.smartrefreshlayout.MyHeaderView android:layout_width="match_parent" android:layout_height="wrap_content"/> <!-- 我的佈局 --> <ImageView android:layout_width="match_parent" android:layout_height="match_parent" android:background="@drawable/a"/> </com.scwang.smartrefresh.layout.SmartRefreshLayout>
很明顯, 這麼寫會很撈, 重複程式碼太多.
- 多個頁面我想換成另一個自定義HeaderView, 怎麼辦呢? 每個佈局去改或者修改MyHeaderView原始碼. 顯然這樣也不好.
解決思路:
我們可以寫多一層, 把SmartRefreshLayout和MyHeaderView封裝起來, 為了降低耦合性, 我們可以把MyHeaderView封裝成一個屬性, 預設給它一個MyHeaderView, 也就是整個專案需要的HeaderView, 部分頁面需要獨立的HeaderView可以自定義新增進去. 封裝完成我們可以直接這麼寫
<com.liys.smartrefreshlayout.MySmartRefreshLayout android:id="@+id/refreshLayout" android:layout_width="match_parent" android:layout_height="match_parent"> <!--app:headView = "..."--> <!-- 我的佈局 --> <ImageView android:layout_width="match_parent" android:layout_height="match_parent" android:background="@drawable/a"/> </com.liys.smartrefreshlayout.MySmartRefreshLayout>
這裡只提供思路, 這個具體怎麼封裝就得看個人了.
例如: 簡單封裝, 這裡只是單純把MyHeaderView新增進去而已.
public class MySmartRefreshLayout extends SmartRefreshLayout{ MyHeaderViewmHeaderView; public MySmartRefreshLayout (Context context) { this(context, null); } public MySmartRefreshLayout (Context context, AttributeSet attrs) { this(context, attrs, 0); } public MySmartRefreshLayout (Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); ViewGroup.LayoutParams layoutParams = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.WRAP_CONTENT); mHeaderView= new MyHeaderView(context); mHeaderView.setLayoutParams(layoutParams); addView(headRefresh, 0); } }