轉載請标明出處:
http://blog.csdn.net/xuehuayous/article/details/50394640
本文出自:【Kevin.zhou的部落格】
前言:接着上 一篇 《Android PullToRefresh 分析之四、擴充RecyclerView》 ,這一篇主要分析如何擴充重新整理加載樣式,來建立各式各樣的重新整理加載效果。
一、 閑扯
我們在《PullToRefresh 分析之二、UI結構》提到重新整理加載的樣式預設的兩種樣式如下:
、
但是我們的需求或許是這樣的:
那麼如果是這樣的會不會使使用者體驗更好些,當然這不是我們開發人員能決定的,但是我們還是要掌握快速定制出這些動畫的方法的。其實一個複雜的動畫都是一系列簡單動作的集合。 通過分析,這些動畫其實正是和我們重新整理加載的幾個狀态相對應:
二、 樣式擴充基類封裝
基于以上分析,我們隻要把幾個關鍵的時間點回調出來,讓大家去自己定義自己的樣式就可以了。這裡我寫了個LoadingLayoutBase基類,然後該類有一些抽象方法,隻要繼承了該基類實作方法,就可以啦。由于修改PullToRefresh架構的地方還是比較多的,最後會給大家提供源碼,大家可以自己看下還是比較簡單的。這裡隻是講下如何使用。 下面以京東商城為例進行說明如何擴充,當然美團和汽車之家的擴充也會給大家提供源碼的。
(一)、擴充京東樣式
1、繼承LoadingLayoutBase,實作抽象方法
大家可以把"Copy JavaDoc"勾選上,這樣就是把說明也添加過來。
2. 實作父類構造函數
我們在最後使用的時候是通過代碼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) {
}
}
看起來一大坨還是不少的,其實就如下幾個方法,然後給大家寫成人話:
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>
布局比較簡單,效果是這樣的:
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. 下拉過程動畫編寫 我們再把效果圖拿過來研究下:
可以發現在下拉的時候的動畫比較簡單,就是京東小哥和包裹都由小變大。我們就想到要在下拉開始的回調以及下拉過程的回調中去寫。
// 開始下拉時的回調
@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!
五、結語
在該篇中,我們通過修改PullToRefresh架構實作了簡單擴充重新整理加載頭部尾部的樣式配置。那麼關于PullToRefresh的五篇文章就完結啦。