(4.2.7.5)Android PullToRefresh 分析之五、扩展刷新加载样式

转载请标明出处: 
http://blog.csdn.net/xuehuayous/article/details/50394640
本文出自:【Kevin.zhou的博客】

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

一、 闲扯

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

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






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

二、 样式扩展基类封装

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

(一)、扩展京东样式

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



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

2. 实现父类构造函数



    我们在最后使用的时候是通过代码new 对象的方式创建实例,这里只实现带有Context的构造函数就可以了。
    现在我们得到了一个架子:
[java]  view plain  copy
 print ?
  1. /** 
  2.  * Created by zhouwk on 2015/12/24 0024. 
  3.  */  
  4. public class JingDongHeaderLayout extends LoadingLayoutBase{  
  5.     public JingDongHeaderLayout(Context context) {  
  6.         super(context);  
  7.     }  
  8.   
  9.     /** 
  10.      * get the LoadingLayout height or width 
  11.      * 
  12.      * @return size 
  13.      */  
  14.     @Override  
  15.     public int getContentSize() {  
  16.         return 0;  
  17.     }  
  18.   
  19.     /** 
  20.      * Call when the widget begins to slide 
  21.      */  
  22.     @Override  
  23.     public void pullToRefresh() {  
  24.   
  25.     }  
  26.   
  27.     /** 
  28.      * Call when the LoadingLayout is fully displayed 
  29.      */  
  30.     @Override  
  31.     public void releaseToRefresh() {  
  32.   
  33.     }  
  34.   
  35.     /** 
  36.      * Call when the LoadingLayout is sliding 
  37.      * 
  38.      * @param scaleOfLayout scaleOfLayout 
  39.      */  
  40.     @Override  
  41.     public void onPull(float scaleOfLayout) {  
  42.   
  43.     }  
  44.   
  45.     /** 
  46.      * Call when the LoadingLayout is fully displayed and the widget has released. 
  47.      * Used to prompt the user loading data 
  48.      */  
  49.     @Override  
  50.     public void refreshing() {  
  51.   
  52.     }  
  53.   
  54.     /** 
  55.      * Call when the data has loaded 
  56.      */  
  57.     @Override  
  58.     public void reset() {  
  59.   
  60.     }  
  61.   
  62.     /** 
  63.      * Set Text to show when the Widget is being Pulled 
  64.      * <code>setPullLabel(releaseLabel, Mode.BOTH)</code> 
  65.      * 
  66.      * @param pullLabel - CharSequence to display 
  67.      */  
  68.     @Override  
  69.     public void setPullLabel(CharSequence pullLabel) {  
  70.   
  71.     }  
  72.   
  73.     /** 
  74.      * Set Text to show when the Widget is refreshing 
  75.      * <code>setRefreshingLabel(releaseLabel, Mode.BOTH)</code> 
  76.      * 
  77.      * @param refreshingLabel - CharSequence to display 
  78.      */  
  79.     @Override  
  80.     public void setRefreshingLabel(CharSequence refreshingLabel) {  
  81.   
  82.     }  
  83.   
  84.     /** 
  85.      * Set Text to show when the Widget is being pulled, and will refresh when 
  86.      * released. This is the same as calling 
  87.      * <code>setReleaseLabel(releaseLabel, Mode.BOTH)</code> 
  88.      * 
  89.      * @param releaseLabel - CharSequence to display 
  90.      */  
  91.     @Override  
  92.     public void setReleaseLabel(CharSequence releaseLabel) {  
  93.   
  94.     }  
  95. }  
看起来一大坨还是不少的,其实就如下几个方法,然后给大家写成人话:


3. 写"加载头部"布局
[html]  view plain  copy
 print ?
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <merge xmlns:android="http://schemas.android.com/apk/res/android" >  
  3.   
  4.     <FrameLayout  
  5.         android:id="@+id/fl_inner"  
  6.         android:layout_width="match_parent"  
  7.         android:layout_height="wrap_content"  
  8.         android:paddingBottom="@dimen/header_footer_top_bottom_padding"  
  9.         android:paddingLeft="@dimen/header_footer_left_right_padding"  
  10.         android:paddingRight="@dimen/header_footer_left_right_padding"  
  11.         android:paddingTop="@dimen/header_footer_top_bottom_padding" >  
  12.   
  13.         <LinearLayout  
  14.             android:layout_width="wrap_content"  
  15.             android:layout_height="wrap_content"  
  16.             android:layout_gravity="center"  
  17.             android:gravity="center_horizontal"  
  18.             android:orientation="vertical" >  
  19.   
  20.             <TextView  
  21.                 android:id="@+id/pull_to_refresh_text"  
  22.                 android:layout_width="wrap_content"  
  23.                 android:layout_height="wrap_content"  
  24.                 android:singleLine="true"  
  25.                 android:text="让购物更便捷"  
  26.                 android:textColor="#5b5b5b"  
  27.                 android:textAppearance="?android:attr/textAppearance" />  
  28.   
  29.             <TextView  
  30.                 android:id="@+id/pull_to_refresh_sub_text"  
  31.                 android:layout_width="wrap_content"  
  32.                 android:layout_height="wrap_content"  
  33.                 android:singleLine="true"  
  34.                 android:text="下拉刷新"  
  35.                 android:textColor="#5b5b5b"  
  36.                 android:textAppearance="?android:attr/textAppearanceSmall"/>  
  37.         </LinearLayout>  
  38.   
  39.         <RelativeLayout  
  40.             android:layout_width="match_parent"  
  41.             android:layout_height="match_parent">  
  42.   
  43.             <ImageView  
  44.                 android:id="@+id/pull_to_refresh_people"  
  45.                 android:layout_width="wrap_content"  
  46.                 android:layout_height="wrap_content"  
  47.                 android:layout_marginLeft="40dp"  
  48.                 android:src="@mipmap/app_refresh_people_0" />  
  49.   
  50.             <ImageView  
  51.                 android:id="@+id/pull_to_refresh_goods"  
  52.                 android:layout_width="wrap_content"  
  53.                 android:layout_height="wrap_content"  
  54.                 android:layout_alignRight="@id/pull_to_refresh_people"  
  55.                 android:layout_centerVertical="true"  
  56.                 android:src="@mipmap/app_refresh_goods_0" />  
  57.   
  58.         </RelativeLayout>  
  59.   
  60.     </FrameLayout>  
  61.   
  62. </merge>  
布局比较简单,效果是这样的:


4. 初始化布局视图
[java]  view plain  copy
 print ?
  1. public JingDongHeaderLayout(Context context) {  
  2.     super(context);  
  3.   
  4.     LayoutInflater.from(context).inflate(R.layout.jingdong_header_loadinglayout, this);  
  5.   
  6.     mInnerLayout = (FrameLayout) findViewById(R.id.fl_inner);  
  7.     mHeaderText = (TextView) mInnerLayout.findViewById(R.id.pull_to_refresh_text);  
  8.     mSubHeaderText = (TextView) mInnerLayout.findViewById(R.id.pull_to_refresh_sub_text);  
  9.     mGoodsImage = (ImageView) mInnerLayout.findViewById(R.id.pull_to_refresh_goods);  
  10.     mPersonImage = (ImageView) mInnerLayout.findViewById(R.id.pull_to_refresh_people);  
  11.   
  12.     LayoutParams lp = (LayoutParams) mInnerLayout.getLayoutParams();  
  13.     lp.gravity = Gravity.BOTTOM;  
  14.   
  15.     // Load in labels  
  16.     mPullLabel = context.getString(R.string.jingdong_pull_label);  
  17.     mRefreshingLabel = context.getString(R.string.jingdong_refreshing_label);  
  18.     mReleaseLabel = context.getString(R.string.jingdong_release_label);  
  19.   
  20.     reset();  
  21. }  
    大家要注意的是初始化布局,"刷新头部"的时候要加上这个:
[java]  view plain  copy
 print ?
  1. LayoutParams lp = (LayoutParams) mInnerLayout.getLayoutParams();  
  2. lp.gravity = Gravity.BOTTOM;  
如果是"加载尾部",就是这样的:
[java]  view plain  copy
 print ?
  1. LayoutParams lp = (LayoutParams) mInnerLayout.getLayoutParams();  
  2. lp.gravity = Gravity.TOP;  

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

[java]  view plain  copy
 print ?
  1. // 获取"加载头部"高度  
  2. @Override  
  3. public int getContentSize() {  
  4.     return mInnerLayout.getHeight();  
  5. }  

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

    可以发现在下拉的时候的动画比较简单,就是京东小哥和包裹都由小变大。我们就想到要在下拉开始的回调以及下拉过程的回调中去写。
[java]  view plain  copy
 print ?
  1. // 开始下拉时的回调  
  2. @Override  
  3. public void pullToRefresh() {  
  4.     mSubHeaderText.setText(mPullLabel);  
  5. }  

[java]  view plain  copy
 print ?
  1. // 下拉拖动时的回调  
  2. @Override  
  3. public void onPull(float scaleOfLayout) {  
  4.     scaleOfLayout = scaleOfLayout > 1.0f ? 1.0f : scaleOfLayout;  
  5.   
  6.     if (mGoodsImage.getVisibility() != View.VISIBLE) {  
  7.         mGoodsImage.setVisibility(View.VISIBLE);  
  8.     }  
  9.   
  10.     //透明度动画  
  11.     ObjectAnimator animAlphaP = ObjectAnimator.ofFloat(mPersonImage, "alpha", -11).setDuration(300);  
  12.     animAlphaP.setCurrentPlayTime((long) (scaleOfLayout * 300));  
  13.     ObjectAnimator animAlphaG = ObjectAnimator.ofFloat(mGoodsImage, "alpha", -11).setDuration(300);  
  14.     animAlphaG.setCurrentPlayTime((long) (scaleOfLayout * 300));  
  15.   
  16.     //缩放动画  
  17.     ViewHelper.setPivotX(mPersonImage, 0);  // 设置中心点  
  18.     ViewHelper.setPivotY(mPersonImage, 0);  
  19.     ObjectAnimator animPX = ObjectAnimator.ofFloat(mPersonImage, "scaleX"01).setDuration(300);  
  20.     animPX.setCurrentPlayTime((long) (scaleOfLayout * 300));  
  21.     ObjectAnimator animPY = ObjectAnimator.ofFloat(mPersonImage, "scaleY"01).setDuration(300);  
  22.     animPY.setCurrentPlayTime((long) (scaleOfLayout * 300));  
  23.   
  24.     ViewHelper.setPivotX(mGoodsImage, mGoodsImage.getMeasuredWidth());  
  25.     ObjectAnimator animGX = ObjectAnimator.ofFloat(mGoodsImage, "scaleX"01).setDuration(300);  
  26.     animGX.setCurrentPlayTime((long) (scaleOfLayout * 300));  
  27.     ObjectAnimator animGY = ObjectAnimator.ofFloat(mGoodsImage, "scaleY"01).setDuration(300);  
  28.     animGY.setCurrentPlayTime((long) (scaleOfLayout * 300));  
  29. }  

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

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

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

[java]  view plain  copy
 print ?
  1. // "加载头部"完全显示时的回调  
  2. @Override  
  3. public void releaseToRefresh() {  
  4.     mSubHeaderText.setText(mReleaseLabel);  
  5. }  

8. 正在加载的设置

    手指释放后,我们看到一个京东小哥在飞奔,就是在 释放后刷新时的回调 中设置的。
[java]  view plain  copy
 print ?
  1. // 释放后刷新时的回调  
  2. @Override  
  3. public void refreshing() {  
  4.     mSubHeaderText.setText(mRefreshingLabel);  
  5.   
  6.     if (animP == null) {  
  7.         mPersonImage.setImageResource(R.drawable.refreshing_anim);  
  8.         animP = (AnimationDrawable) mPersonImage.getDrawable();  
  9.     }  
  10.     animP.start();  
  11.     if (mGoodsImage.getVisibility() == View.VISIBLE) {  
  12.         mGoodsImage.setVisibility(View.INVISIBLE);  
  13.     }  
  14. }  
    这里我们使用的是帧动画,就是几张图片刷啊刷,给人的假象就是京东小哥正在卖力跑。

9. 初始化到未刷新状态

[java]  view plain  copy
 print ?
  1. // 初始化到未刷新状态  
  2. @Override  
  3. public void reset() {  
  4.     if (animP != null) {  
  5.         animP.stop();  
  6.         animP = null;  
  7.     }  
  8.     mPersonImage.setImageResource(R.mipmap.app_refresh_people_0);  
  9.     if (mGoodsImage.getVisibility() == View.VISIBLE) {  
  10.         mGoodsImage.setVisibility(View.INVISIBLE);  
  11.     }  
  12. }  

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

10. 设置提示

[java]  view plain  copy
 print ?
  1. @Override  
  2. public void setPullLabel(CharSequence pullLabel) {  
  3.     mPullLabel = pullLabel;  
  4. }  
  5.   
  6. @Override  
  7. public void setRefreshingLabel(CharSequence refreshingLabel) {  
  8.     mRefreshingLabel = refreshingLabel;  
  9. }  
  10.   
  11. @Override  
  12. public void setReleaseLabel(CharSequence releaseLabel) {  
  13.     mReleaseLabel = releaseLabel;  
  14. }  
这三个方法不实现也可以,因为我们是通过以下方式初始提示的
[java]  view plain  copy
 print ?
  1. mPullLabel = context.getString(R.string.jingdong_pull_label);  
  2. mRefreshingLabel = context.getString(R.string.jingdong_refreshing_label);  
  3. mReleaseLabel = context.getString(R.string.jingdong_release_label);  
之所以抽象出来这三个方法,是可以让大家更灵活地改变提示语。

三、设置自定义样式

    一行代码搞定
[java]  view plain  copy
 print ?
  1. mPullToRefreshRecyclerView.setHeaderLayout(new JingDongHeaderLayout(this));  
当然也可以设置底部样式:
[java]  view plain  copy
 print ?
  1. mPullToRefreshRecyclerView.setFooterLayout(xxx);  

四、源码

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


五、结语

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值