自定义SmartRefreshLayout的Header和Footer

本文详细介绍了如何使用SmartRefreshLayout自定义Header和Footer。首先,通过经典Header的实现,包括需求分析、样式指定、动画控制和状态管理等方面。接着,展示了如何通过继承InternalAbstract简化自定义布局Header的过程,包括布局、动画、状态控制和主题颜色的设置。自定义Header和Footer让下拉刷新和上拉加载功能更加个性化。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

使用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)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值