天天看点

Android PullToRefresh 分析之五、扩展刷新加载样式一、 闲扯二、 样式扩展基类封装三、设置自定义样式四、源码五、结语

转载请标明出处:

http://blog.csdn.net/xuehuayous/article/details/50394640

本文出自:【Kevin.zhou的博客】

前言:接着上 一篇 《Android PullToRefresh 分析之四、扩展RecyclerView》 ,这一篇主要分析如何扩展刷新加载样式,来创建各式各样的刷新加载效果。

一、 闲扯

          我们在《PullToRefresh 分析之二、UI结构》提到刷新加载的样式默认的两种样式如下:

Android PullToRefresh 分析之五、扩展刷新加载样式一、 闲扯二、 样式扩展基类封装三、设置自定义样式四、源码五、结语

Android PullToRefresh 分析之五、扩展刷新加载样式一、 闲扯二、 样式扩展基类封装三、设置自定义样式四、源码五、结语

    但是我们的需求或许是这样的:

Android PullToRefresh 分析之五、扩展刷新加载样式一、 闲扯二、 样式扩展基类封装三、设置自定义样式四、源码五、结语
Android PullToRefresh 分析之五、扩展刷新加载样式一、 闲扯二、 样式扩展基类封装三、设置自定义样式四、源码五、结语
Android PullToRefresh 分析之五、扩展刷新加载样式一、 闲扯二、 样式扩展基类封装三、设置自定义样式四、源码五、结语

    那么如果是这样的会不会使用户体验更好些,当然这不是我们开发人员能决定的,但是我们还是要掌握快速定制出这些动画的方法的。其实一个复杂的动画都是一系列简单动作的集合。     通过分析,这些动画其实正是和我们刷新加载的几个状态相对应:

Android PullToRefresh 分析之五、扩展刷新加载样式一、 闲扯二、 样式扩展基类封装三、设置自定义样式四、源码五、结语

二、 样式扩展基类封装

         基于以上分析,我们只要把几个关键的时间点回调出来,让大家去自己定义自己的样式就可以了。这里我写了个LoadingLayoutBase基类,然后该类有一些抽象方法,只要继承了该基类实现方法,就可以啦。由于修改PullToRefresh框架的地方还是比较多的,最后会给大家提供源码,大家可以自己看下还是比较简单的。这里只是讲下如何使用。     下面以京东商城为例进行说明如何扩展,当然美团和汽车之家的扩展也会给大家提供源码的。

(一)、扩展京东样式

1、继承LoadingLayoutBase,实现抽象方法

Android PullToRefresh 分析之五、扩展刷新加载样式一、 闲扯二、 样式扩展基类封装三、设置自定义样式四、源码五、结语

    大家可以把"Copy JavaDoc"勾选上,这样就是把说明也添加过来。

2. 实现父类构造函数

Android PullToRefresh 分析之五、扩展刷新加载样式一、 闲扯二、 样式扩展基类封装三、设置自定义样式四、源码五、结语

    我们在最后使用的时候是通过代码new 对象的方式创建实例,这里只实现带有Context的构造函数就可以了。     现在我们得到了一个架子:

/**
 * Created by zhouwk on 2015/12/24 0024.
 */
public class JingDongHeaderLayout extends LoadingLayoutBase{
    public JingDongHeaderLayout(Context context) {
        super(context);
    }

    /**
     * get the LoadingLayout height or width
     *
     * @return size
     */
    @Override
    public int getContentSize() {
        return 0;
    }

    /**
     * Call when the widget begins to slide
     */
    @Override
    public void pullToRefresh() {

    }

    /**
     * Call when the LoadingLayout is fully displayed
     */
    @Override
    public void releaseToRefresh() {

    }

    /**
     * Call when the LoadingLayout is sliding
     *
     * @param scaleOfLayout scaleOfLayout
     */
    @Override
    public void onPull(float scaleOfLayout) {

    }

    /**
     * Call when the LoadingLayout is fully displayed and the widget has released.
     * Used to prompt the user loading data
     */
    @Override
    public void refreshing() {

    }

    /**
     * Call when the data has loaded
     */
    @Override
    public void reset() {

    }

    /**
     * Set Text to show when the Widget is being Pulled
     * <code>setPullLabel(releaseLabel, Mode.BOTH)</code>
     *
     * @param pullLabel - CharSequence to display
     */
    @Override
    public void setPullLabel(CharSequence pullLabel) {

    }

    /**
     * Set Text to show when the Widget is refreshing
     * <code>setRefreshingLabel(releaseLabel, Mode.BOTH)</code>
     *
     * @param refreshingLabel - CharSequence to display
     */
    @Override
    public void setRefreshingLabel(CharSequence refreshingLabel) {

    }

    /**
     * Set Text to show when the Widget is being pulled, and will refresh when
     * released. This is the same as calling
     * <code>setReleaseLabel(releaseLabel, Mode.BOTH)</code>
     *
     * @param releaseLabel - CharSequence to display
     */
    @Override
    public void setReleaseLabel(CharSequence releaseLabel) {

    }
}
           

看起来一大坨还是不少的,其实就如下几个方法,然后给大家写成人话:

Android PullToRefresh 分析之五、扩展刷新加载样式一、 闲扯二、 样式扩展基类封装三、设置自定义样式四、源码五、结语

3. 写"加载头部"布局

<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android" >

    <FrameLayout
        android:id="@+id/fl_inner"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:paddingBottom="@dimen/header_footer_top_bottom_padding"
        android:paddingLeft="@dimen/header_footer_left_right_padding"
        android:paddingRight="@dimen/header_footer_left_right_padding"
        android:paddingTop="@dimen/header_footer_top_bottom_padding" >

        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:gravity="center_horizontal"
            android:orientation="vertical" >

            <TextView
                android:id="@+id/pull_to_refresh_text"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:singleLine="true"
                android:text="让购物更便捷"
                android:textColor="#5b5b5b"
                android:textAppearance="?android:attr/textAppearance" />

            <TextView
                android:id="@+id/pull_to_refresh_sub_text"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:singleLine="true"
                android:text="下拉刷新"
                android:textColor="#5b5b5b"
                android:textAppearance="?android:attr/textAppearanceSmall"/>
        </LinearLayout>

        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <ImageView
                android:id="@+id/pull_to_refresh_people"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginLeft="40dp"
                android:src="@mipmap/app_refresh_people_0" />

            <ImageView
                android:id="@+id/pull_to_refresh_goods"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignRight="@id/pull_to_refresh_people"
                android:layout_centerVertical="true"
                android:src="@mipmap/app_refresh_goods_0" />

        </RelativeLayout>

    </FrameLayout>

</merge>
           

布局比较简单,效果是这样的:

Android PullToRefresh 分析之五、扩展刷新加载样式一、 闲扯二、 样式扩展基类封装三、设置自定义样式四、源码五、结语

4. 初始化布局视图

public JingDongHeaderLayout(Context context) {
	super(context);

	LayoutInflater.from(context).inflate(R.layout.jingdong_header_loadinglayout, this);

	mInnerLayout = (FrameLayout) findViewById(R.id.fl_inner);
	mHeaderText = (TextView) mInnerLayout.findViewById(R.id.pull_to_refresh_text);
	mSubHeaderText = (TextView) mInnerLayout.findViewById(R.id.pull_to_refresh_sub_text);
	mGoodsImage = (ImageView) mInnerLayout.findViewById(R.id.pull_to_refresh_goods);
	mPersonImage = (ImageView) mInnerLayout.findViewById(R.id.pull_to_refresh_people);

	LayoutParams lp = (LayoutParams) mInnerLayout.getLayoutParams();
	lp.gravity = Gravity.BOTTOM;

	// Load in labels
	mPullLabel = context.getString(R.string.jingdong_pull_label);
	mRefreshingLabel = context.getString(R.string.jingdong_refreshing_label);
	mReleaseLabel = context.getString(R.string.jingdong_release_label);

	reset();
}
           

    大家要注意的是初始化布局,"刷新头部"的时候要加上这个:

LayoutParams lp = (LayoutParams) mInnerLayout.getLayoutParams();
lp.gravity = Gravity.BOTTOM;
           

如果是"加载尾部",就是这样的:

LayoutParams lp = (LayoutParams) mInnerLayout.getLayoutParams();
lp.gravity = Gravity.TOP;
           

5. 设置"加载头部"高度

// 获取"加载头部"高度
@Override
public int getContentSize() {
	return mInnerLayout.getHeight();
}
           

6. 下拉过程动画编写          我们再把效果图拿过来研究下:

Android PullToRefresh 分析之五、扩展刷新加载样式一、 闲扯二、 样式扩展基类封装三、设置自定义样式四、源码五、结语

    可以发现在下拉的时候的动画比较简单,就是京东小哥和包裹都由小变大。我们就想到要在下拉开始的回调以及下拉过程的回调中去写。

// 开始下拉时的回调
@Override
public void pullToRefresh() {
    mSubHeaderText.setText(mPullLabel);
}
           
// 下拉拖动时的回调
@Override
public void onPull(float scaleOfLayout) {
    scaleOfLayout = scaleOfLayout > 1.0f ? 1.0f : scaleOfLayout;

    if (mGoodsImage.getVisibility() != View.VISIBLE) {
        mGoodsImage.setVisibility(View.VISIBLE);
    }

    //透明度动画
    ObjectAnimator animAlphaP = ObjectAnimator.ofFloat(mPersonImage, "alpha", -1, 1).setDuration(300);
    animAlphaP.setCurrentPlayTime((long) (scaleOfLayout * 300));
    ObjectAnimator animAlphaG = ObjectAnimator.ofFloat(mGoodsImage, "alpha", -1, 1).setDuration(300);
    animAlphaG.setCurrentPlayTime((long) (scaleOfLayout * 300));

    //缩放动画
    ViewHelper.setPivotX(mPersonImage, 0);  // 设置中心点
    ViewHelper.setPivotY(mPersonImage, 0);
    ObjectAnimator animPX = ObjectAnimator.ofFloat(mPersonImage, "scaleX", 0, 1).setDuration(300);
    animPX.setCurrentPlayTime((long) (scaleOfLayout * 300));
    ObjectAnimator animPY = ObjectAnimator.ofFloat(mPersonImage, "scaleY", 0, 1).setDuration(300);
    animPY.setCurrentPlayTime((long) (scaleOfLayout * 300));

    ViewHelper.setPivotX(mGoodsImage, mGoodsImage.getMeasuredWidth());
    ObjectAnimator animGX = ObjectAnimator.ofFloat(mGoodsImage, "scaleX", 0, 1).setDuration(300);
    animGX.setCurrentPlayTime((long) (scaleOfLayout * 300));
    ObjectAnimator animGY = ObjectAnimator.ofFloat(mGoodsImage, "scaleY", 0, 1).setDuration(300);
    animGY.setCurrentPlayTime((long) (scaleOfLayout * 300));
}
           

看着代码很多,其实思路非常简单,就是设置变大的动画随着拖动的距离大小变化,这里用到了nineoldandroids这个动画兼容库。

 7. "加载头部"完全显示时更改提示显示

    我们发现开始的提示是"下拉可以刷新"后来变为了"松开可以刷新",这个的设置就是在 "加载头部"完全显示的回调中设置的。

// "加载头部"完全显示时的回调
@Override
public void releaseToRefresh() {
    mSubHeaderText.setText(mReleaseLabel);
}
           

8. 正在加载的设置

    手指释放后,我们看到一个京东小哥在飞奔,就是在 释放后刷新时的回调 中设置的。

// 释放后刷新时的回调
@Override
public void refreshing() {
    mSubHeaderText.setText(mRefreshingLabel);

    if (animP == null) {
        mPersonImage.setImageResource(R.drawable.refreshing_anim);
        animP = (AnimationDrawable) mPersonImage.getDrawable();
    }
    animP.start();
    if (mGoodsImage.getVisibility() == View.VISIBLE) {
        mGoodsImage.setVisibility(View.INVISIBLE);
    }
}
           

    这里我们使用的是帧动画,就是几张图片刷啊刷,给人的假象就是京东小哥正在卖力跑。

9. 初始化到未刷新状态

// 初始化到未刷新状态
@Override
public void reset() {
    if (animP != null) {
        animP.stop();
        animP = null;
    }
    mPersonImage.setImageResource(R.mipmap.app_refresh_people_0);
    if (mGoodsImage.getVisibility() == View.VISIBLE) {
        mGoodsImage.setVisibility(View.INVISIBLE);
    }
}
           

就是把我们加载时的动画关掉。

10. 设置提示

@Override
public void setPullLabel(CharSequence pullLabel) {
    mPullLabel = pullLabel;
}

@Override
public void setRefreshingLabel(CharSequence refreshingLabel) {
    mRefreshingLabel = refreshingLabel;
}

@Override
public void setReleaseLabel(CharSequence releaseLabel) {
    mReleaseLabel = releaseLabel;
}
           

这三个方法不实现也可以,因为我们是通过以下方式初始提示的

mPullLabel = context.getString(R.string.jingdong_pull_label);
mRefreshingLabel = context.getString(R.string.jingdong_refreshing_label);
mReleaseLabel = context.getString(R.string.jingdong_release_label);
           

之所以抽象出来这三个方法,是可以让大家更灵活地改变提示语。

三、设置自定义样式

    一行代码搞定

mPullToRefreshRecyclerView.setHeaderLayout(new JingDongHeaderLayout(this));
           

当然也可以设置底部样式:

mPullToRefreshRecyclerView.setFooterLayout(xxx);
           

四、源码

    给大家提供一个github的地址:PullToRefresh-demo     另外,欢迎 star or f**k me on github!

Android PullToRefresh 分析之五、扩展刷新加载样式一、 闲扯二、 样式扩展基类封装三、设置自定义样式四、源码五、结语

五、结语

         在该篇中,我们通过修改PullToRefresh框架实现了简单扩展刷新加载头部尾部的样式配置。那么关于PullToRefresh的五篇文章就完结啦。