使用SmartRefreshLayout自定义Header并不难,第一个例子重在介绍需要实现的方法,第二个例子为使用示例;Footer类似。
一、经典Header
实现一个最简单的经典Header(没有更新时间)
1.1需求分析
我们的经典Header需要一个标题文本、刷新动画、下拉箭头。由此我们可以选定一个继承LinearLayout并列出成员变量
public class ClassicsHeader extends LinearLayout implements RefreshHeader {
private TextView mHeaderText;//标题文本
private PathsView mArrowView;//下拉箭头
private ImageView mProgressView;//刷新动画视图
private ProgressDrawable mProgressDrawable;//刷新动画
public ClassicsHeader(Context context) {
super(context);
initView(context);
}
public ClassicsHeader(Context context, AttributeSet attrs) {
super(context, attrs);
this.initView(context);
}
public ClassicsHeader(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
this.initView(context);
}
private void initView(Context context) {
setGravity(Gravity.CENTER);
//标题文本
mHeaderText = new TextView(context);
//下拉箭头
mArrowView = new PathsView(context);
mArrowView.parserPaths("M20,12l-1.41,-1.41L13,16.17V4h-2v12.17l-5.58,-5.59L4,12l8,8 8,-8z");
//刷新动画
mProgressDrawable = new ProgressDrawable();
mProgressView = new ImageView(context);
mProgressView.setImageDrawable(mProgressDrawable);
addView(mProgressView, SmartUtil.dp2px(20), SmartUtil.dp2px(20));
addView(mArrowView, SmartUtil.dp2px(20), SmartUtil.dp2px(20));
addView(new View(context), SmartUtil.dp2px(20), SmartUtil.dp2px(20));
addView(mHeaderText, LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
setMinimumHeight(SmartUtil.dp2px(60));
}
}
1.2指定样式
根据我们的常识,经典Header在下拉的时候是贴着列表平移向下冒出,所以我们实现样式直接指定为:平移
/**
* 获取真实视图(必须返回,不能为null)
*/
@NonNull
public View getView() {
return this;
}
/**
* 获取变换方式(必须指定一个:平移、拉伸、固定、全屏)
*/
@NotNull
@Override
public SpinnerStyle getSpinnerStyle() {
return SpinnerStyle.Translate;
}
五种样式
Translate //平行移动 特点: HeaderView高度不会改变,
Scale //拉伸形变 特点:在下拉和上弹(HeaderView高度改变)时候,会自动触发OnDraw事件
FixedBehind //固定在背后 特点:HeaderView高度不会改变,
FixedFront //固定在前面 特点:HeaderView高度不会改变,
MatchLayout //填满布局 特点:HeaderView高度不会改变,尺寸充满 RefreshLayout
1.3动画开关
接下来我们需要在关键地方对动画进行控制和开启
/**
* 开始动画(开始刷新或者开始加载动画)
*
* @param layout RefreshLayout
* @param headHeight HeaderHeight or FooterHeight
* @param maxDragHeight 最大拖动高度
*/
@Override
public void onStartAnimator(@NotNull RefreshLayout layout, int headHeight, int maxDragHeight) {
mProgressDrawable.start();//开始动画
}
/**
* 动画结束
*
* @param layout RefreshLayout
* @param success 数据是否成功刷新或加载
* @return 完成动画所需时间 如果返回 Integer.MAX_VALUE 将取消本次完成事件,继续保持原有状态
*/
@Override
public int onFinish(@NotNull RefreshLayout layout, boolean success) {
mProgressDrawable.stop();//停止动画
if (success) {
mHeaderText.setText("刷新完成");
} else {
mHeaderText.setText("刷新失败");
}
return 500;//延迟500毫秒之后再弹回
}
1.4状态控制
我们还要在不同的状态控制内部控件的显示和旋转
/**
* 【仅限框架内调用】状态改变事件 {@link RefreshState}
*
* @param refreshLayout RefreshLayout
* @param oldState 改变之前的状态
* @param newState 改变之后的状态
*/
@Override
public void onStateChanged(@NotNull RefreshLayout refreshLayout, @NotNull RefreshState oldState, @NotNull RefreshState newState) {
switch (newState) {
case None:
case PullDownToRefresh:
mHeaderText.setText("下拉开始刷新");
mArrowView.setVisibility(VISIBLE);//显示下拉箭头
mProgressView.setVisibility(GONE);//隐藏动画
mArrowView.animate().rotation(0);//还原箭头方向
break;
case Refreshing:
mHeaderText.setText("正在刷新");
mProgressView.setVisibility(VISIBLE);//显示加载动画
mArrowView.setVisibility(GONE);//隐藏箭头
break;
case ReleaseToRefresh:
mHeaderText.setText("释放立即刷新");
mArrowView.animate().rotation(180);//显示箭头改为朝上
break;
}
}
1.5其他
/**
* 设置主题颜色 (如果自定义的Header没有注意颜色,本方法可以什么都不处理)
* @param colors 对应Xml中配置的 srlPrimaryColor srlAccentColor
*/
@Override
public void setPrimaryColors(int... colors) {
}
/**
* 尺寸定义初始化完成 (如果高度不改变(代码修改:setHeader),只调用一次, 在RefreshLayout#onMeasure中调用)
* @param kernel RefreshKernel 核心接口(用于完成高级Header功能)
* @param height HeaderHeight or FooterHeight
* @param maxDragHeight 最大拖动高度
*/
@Override
public void onInitialized(@NonNull RefreshKernel kernel, int height, int maxDragHeight) {
}
/**
* 【仅限框架内调用】手指拖动下拉(会连续多次调用,添加isDragging并取代之前的onPulling、onReleasing)
* @param isDragging true 手指正在拖动 false 回弹动画
* @param percent 下拉的百分比 值 = offset/footerHeight (0 - percent - (footerHeight+maxDragHeight) / footerHeight )
* @param offset 下拉的像素偏移量 0 - offset - (footerHeight+maxDragHeight)
* @param height 高度 HeaderHeight or FooterHeight
* @param maxDragHeight 最大拖动高度
*/
@Override
public void onMoving(boolean isDragging, float percent, int offset, int height, int maxDragHeight) {
}
/**
* 【仅限框架内调用】释放时刻(调用一次,将会触发加载)
* @param refreshLayout RefreshLayout
* @param height 高度 HeaderHeight or FooterHeight
* @param maxDragHeight 最大拖动高度
*/
@Override
public void onReleased(@NonNull RefreshLayout refreshLayout, int height, int maxDragHeight) {
}
/**
* 【仅限框架内调用】水平方向的拖动
* @param percentX 下拉时,手指水平坐标对屏幕的占比(0 - percentX - 1)
* @param offsetX 下拉时,手指水平坐标对屏幕的偏移(0 - offsetX - LayoutWidth)
* @param offsetMax 最大的偏移量
*/
@Override
public void onHorizontalDrag(float percentX, int offsetX, int offsetMax) {
}
/**
* 是否支持水平方向的拖动(将会影响到onHorizontalDrag的调用)
* @return 水平拖动需要消耗更多的时间和资源,所以如果不支持请返回false
*/
@Override
public boolean isSupportHorizontalDrag() {
return false;
}
二、自定义布局Header
2.1继承InternalAbstract
继承InternalAbstract 可以少写很多接口方法
public class IntenalHeader extends InternalAbstract implements RefreshHeader {
private ImageView header;//动画视图
private ConstraintLayout primary;//布局id,用于设置背景颜色,非必要
public IntenalHeader(Context context,@LayoutRes int resource) {
this(context, null,resource);
}
public IntenalHeader(Context context, AttributeSet attrs,@LayoutRes int resource) {
this(context, attrs, 0,resource);
}
public IntenalHeader(Context context, AttributeSet attrs, int defStyleAttr,@LayoutRes int resource) {
super(context, attrs, defStyleAttr);
View view = View.inflate(context, resource, this);
header = view.findViewById(R.id.header);
primary = view.findViewById(R.id.primary);
}
}
2.2指定样式(可以不设置)
@NonNull
@Override
public SpinnerStyle getSpinnerStyle() {
return SpinnerStyle.Scale;
}
2.3动画开关
/**
* 开始动画(开始刷新或者开始加载动画)
*
* @param layout RefreshLayout
* @param headHeight HeaderHeight or FooterHeight
* @param maxDragHeight 最大拖动高度
*/
@Override
public void onStartAnimator(@NonNull RefreshLayout refreshLayout, int height, int maxDragHeight) {
//旋转动画
ObjectAnimator rotation = ObjectAnimator.ofFloat(header, "rotation",0,360);
rotation.setDuration(2000);
rotation.start();
}
/**
* 动画结束
*
* @param layout RefreshLayout
* @param success 数据是否成功刷新或加载
* @return 完成动画所需时间 如果返回 Integer.MAX_VALUE 将取消本次完成事件,继续保持原有状态
*/
@Override
public int onFinish(@NonNull RefreshLayout refreshLayout, boolean success) {
return 0;//动画结束就回弹
}
2.4状态控制
/**
* 【仅限框架内调用】状态改变事件 {@link RefreshState}
*
* @param refreshLayout RefreshLayout
* @param oldState 改变之前的状态
* @param newState 改变之后的状态
*/
@Override
public void onStateChanged(@NonNull RefreshLayout refreshLayout, @NonNull RefreshState oldState, @NonNull RefreshState newState) {
switch (newState) {
case None:
case PullDownToRefresh://动画结束释放刷新
header.setVisibility(GONE);
break;
case Refreshing://正在刷新
header.setImageResource(R.drawable.brvah_sample_footer_loading);
break;
case ReleaseToRefresh://开始下拉操作
header.setVisibility(VISIBLE);
header.setImageResource(android.R.drawable.arrow_down_float);
break;
}
}
2.5设置主题颜色(非必要)
@Override
public void setPrimaryColors(int... colors) {
for (int color : colors){
primary.setBackgroundColor(color);
}
}
2.6布局
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/primary"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/header"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_marginTop="16dp"
android:layout_marginBottom="16dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@mipmap/ic_launcher" />
</android.support.constraint.ConstraintLayout>
2.7使用
val header = IntenalHeader(this,R.layout.classics_header)
//设置主题颜色,非必要
header.setPrimaryColors(ContextCompat.getColor(applicationContext
, R.color.colorPrimary))
refreshLayout.setRefreshHeader(header)