如果沒有了解SmartRefreshLayout基本使用, 請先看 SmartRefreshLayout基本使用
因為自定義Header和Foote方式基本一樣, 所以這里介紹Header就可以了
一. 官方文檔介紹
github文檔
我們看到官方文檔實現方式:
① 自定義一個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實現了RefreshHeader接口, 但是繼承的卻不是基本布局, 而是一個自定義的布局InternalClassics<ClassicsHeader>.
得出繼承關系:
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{
MyHeaderView mHeaderView;
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);
}
}