快速仿寫京東、天貓下拉刷新

fangcao 8年前發布 | 7K 次閱讀 安卓開發 Android開發 移動開發

好久沒輸出文章了,最近研發任務比較忙,果然計劃趕不上變化,之前還希望能一周一輸出,好吧,我還是承認自己比較懶好了,=.=## 這次跟大家分享一下下拉刷新,之前我們的公司的項目一直都是使用SwipeRefreshLayout,官方的Md風格,好用少Bug。閑話不多說了,咱們現在分析一下哪種下拉刷新比較多,有的是直接包在ListView或者RecyclerView的頭部,有得則是像SwipeRefreshLayout一樣,包在視圖的最外層,個人建議使用包在最外層的做法,可擴展性比較強,另一種的話,可以借鑒一下XRecyclerView,具體的就不多說了,咱們使用最外層的方法,快速的構建一個類似像京東、天貓的下拉刷新。

1.使用框架android-Ultra-Pull-To-Refresh

大家感興趣的可以去看一下這個下拉刷新框架,非常強大,可擴展性非常強,兼容各種view的下拉刷新事件。

2.京東下拉刷新

先放一張效果來看一下,京東的下拉刷新動畫:

jd_origin.gif

通俗的來說,就是一個快遞小哥,跑過來,接住商品,然后刷新的時候,自己拿著商品跑起來。(解釋有點牽強啊)

咱們來分析一下整一個過程:

1.快遞小哥從遠處跑過去拿商品,可以拆成兩個部分.

Paste_Image.png

當拿到商品之后,就跑起來,在我們程序里,就是動畫:

Paste_Image.png

(你去解壓一下京東的apk,你也能拿到這些圖片)

其實就是圖片之間的切換,以上的準備工作都完成了,看看這個xml應該怎么寫:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">


    <FrameLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_toLeftOf="@+id/layout_tx">

        <ImageView
            android:id="@+id/iv_man"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="@mipmap/a2a" />

        <ImageView
            android:id="@+id/iv_goods"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="right|center"
            android:src="@mipmap/a29" />
    </FrameLayout>

    <LinearLayout
        android:id="@+id/layout_tx"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:layout_marginLeft="5dp"
        android:gravity="center_vertical"
        android:orientation="vertical"
        android:padding="5dp">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="讓購物更便捷"
            android:textSize="14sp" />

        <TextView
            android:id="@+id/tv_remain"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="5dp"
            android:text="松開刷新"
            android:textSize="12sp" />
    </LinearLayout>


</RelativeLayout>

根據第一個人跟商品,咱們先分開兩個ImageView,使用FrameLayout布局,然后讓這兩個ImageView放進去,商品的這個ImageView設置layoutgravity=right.

咱們再看看android-Ultra-Pull-To-Refresh這個框架給我們帶來什么。

public interface PtrUIHandler {

    /**
     * When the content view has reached top and refresh has been completed, view will be reset.
     *
     * @param frame
     */
    public void onUIReset(PtrFrameLayout frame);

    /**
     * prepare for loading
     *
     * @param frame
     */
    public void onUIRefreshPrepare(PtrFrameLayout frame);

    /**
     * perform refreshing UI
     */
    public void onUIRefreshBegin(PtrFrameLayout frame);

    /**
     * perform UI after refresh
     */
    public void onUIRefreshComplete(PtrFrameLayout frame);

    public void onUIPositionChange(PtrFrameLayout frame, boolean isUnderTouch, byte status, PtrIndicator ptrIndicator);
}

這是一下下拉刷新事件的接口,只要實現以上接口就能得到相應的,得到以上接口

至于各個接口有什么,看接口名大家應該就知道了。

直接貼代碼更直觀:

/**
 * 下拉刷新header
 * Created by shenminjie on 2016/12/6.
 */
public class JdRefreshHeader extends FrameLayout implements PtrUIHandler {

    /**
     * 提醒文本
     */
    private TextView mTvRemind;

    /**
     * 快遞員logo
     */
    private ImageView mIvMan;

    /**
     * 商品logo
     */
    private ImageView mIvGoods;

    /**
     * 狀態識別
     */
    private int mState;

    /**
     * 重置
     * 準備刷新
     * 開始刷新
     * 結束刷新
     */
    public static final int STATE_RESET = -1;
    public static final int STATE_PREPARE = 0;
    public static final int STATE_BEGIN = 1;
    public static final int STATE_FINISH = 2;

    public static final int MARGIN_RIGHT = 100;

    /**
     * 動畫
     */
    private AnimationDrawable mAnimation;


    public JdRefreshHeader(Context context) {
        super(context);
        initView();
    }

    public JdRefreshHeader(Context context, AttributeSet attrs) {
        super(context, attrs);
        initView();
    }

    public JdRefreshHeader(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initView();
    }

    /**
     * 初始化view
     */
    private void initView() {
        View view = LayoutInflater.from(getContext()).inflate(R.layout.jd_refresh_header_view, this, false);
        mTvRemind = (TextView) view.findViewById(R.id.tv_remain);
        mIvMan = (ImageView) view.findViewById(R.id.iv_man);
        mIvGoods = (ImageView) view.findViewById(R.id.iv_goods);
        addView(view);

    }


    @Override
    public void onUIReset(PtrFrameLayout frame) {
        mState = STATE_RESET;
    }

    @Override
    public void onUIRefreshPrepare(PtrFrameLayout frame) {
        mState = STATE_PREPARE;
    }

    @Override
    public void onUIRefreshBegin(PtrFrameLayout frame) {
        mState = STATE_BEGIN;
        //隱藏商品logo,開啟跑步動畫
        mIvGoods.setVisibility(View.GONE);
        mIvMan.setBackgroundResource(R.drawable.runningman);
        mAnimation = (AnimationDrawable) mIvMan.getBackground();
        if (!mAnimation.isRunning()) {
            mAnimation.start();
        }
    }

    @Override
    public void onUIRefreshComplete(PtrFrameLayout frame) {
        mState = STATE_FINISH;
        mIvGoods.setVisibility(View.VISIBLE);
        //停止動畫
        if (mAnimation.isRunning()) {
            mAnimation.stop();
        }
        mIvMan.setBackgroundResource(R.mipmap.a2a);
    }

    @Override
    public void onUIPositionChange(PtrFrameLayout frame, boolean isUnderTouch, byte status, PtrIndicator ptrIndicator) {
        //處理提醒字體
        switch (mState) {
            case STATE_PREPARE:
                //logo設置
                mIvMan.setAlpha(ptrIndicator.getCurrentPercent());
                mIvGoods.setAlpha(ptrIndicator.getCurrentPercent());
                FrameLayout.LayoutParams mIvManLayoutParams = (LayoutParams) mIvMan.getLayoutParams();
                if (ptrIndicator.getCurrentPercent() <= 1) {
                    mIvMan.setScaleX(ptrIndicator.getCurrentPercent());
                    mIvMan.setScaleY(ptrIndicator.getCurrentPercent());
                    mIvGoods.setScaleX(ptrIndicator.getCurrentPercent());
                    mIvGoods.setScaleY(ptrIndicator.getCurrentPercent());
                    int marginRight = (int) (MARGIN_RIGHT - MARGIN_RIGHT * ptrIndicator.getCurrentPercent());
                    mIvManLayoutParams.setMargins(0, 0, marginRight, 0);
                    mIvMan.setLayoutParams(mIvManLayoutParams);
                }
                if (ptrIndicator.getCurrentPercent() < 1.2) {
                    mTvRemind.setText("下拉刷新...");
                } else {
                    mTvRemind.setText("松開刷新...");
                }
                break;
            case STATE_BEGIN:
                mTvRemind.setText("更新中...");
                break;
            case STATE_FINISH:
                mTvRemind.setText("加載完成...");
                break;
        }
    }
}

創建一個成員變量mState,用于保存下拉刷新的時候,每一個狀態,然后根據保存好的狀態在UiPositionChange的接口中,對UI進行相應的修改,保存每個狀態文本的提示,在下拉的過程中,通過UiPositionChange的回調,獲取PtrIndicator中,可以獲取下拉的百分比,根據這個百分比我們可以做很多東西,例如京東的快遞小哥從遠處跑過來拿商品,以及快遞小哥與商品之間的大小,都可以根據這個PtrIndicator百分比進行設置其大小的比例,跑過來這個過程我使用的方法是利用marginRight進行設置兩者之間的距離,當達到下拉刷新的臨界點時,快遞小哥跟商品之間的margin為0,達到了快遞小哥獲取商品的效果,然后當刷新的時候,隱藏商品,使用之前所提供的三張圖片進行效應的切換,也就是動畫:

<animation-list xmlns:android="http://schemas.android.com/apk/res/android" android:oneshot="false">
    <item
        android:drawable="@mipmap/a2b"
        android:duration="70" />
    <item
        android:drawable="@mipmap/a2c"
        android:duration="70" />
    <item
        android:drawable="@mipmap/a2d"
        android:duration="70" />
</animation-list>
@Override
public void onUIRefreshBegin(PtrFrameLayout frame) {
    mState = STATE_BEGIN;
    //隱藏商品logo,開啟跑步動畫
    mIvGoods.setVisibility(View.GONE);
    mIvMan.setBackgroundResource(R.drawable.runningman);
    mAnimation = (AnimationDrawable) mIvMan.getBackground();
    if (!mAnimation.isRunning()) {
        mAnimation.start();
    }
}

當刷新結束的時候,我們把動畫停止,并把之前的商品顯示出來:

@Override
public void onUIRefreshComplete(PtrFrameLayout frame) {
    mState = STATE_FINISH;
    mIvGoods.setVisibility(View.VISIBLE);
    //停止動畫
    if (mAnimation.isRunning()) {
        mAnimation.stop();
    }
    mIvMan.setBackgroundResource(R.mipmap.a2a);
}

很簡單,基本上難點框架都已經幫我們封裝好了,我們只要繼承PtrFrameLayout,添加相應的Header以及實現PtrUIHandler就可以實現。

/**
 *仿京東下拉刷新
 * Created by shenminjie on 2016/12/6.
 */

public class JdRefreshLayout extends PtrFrameLayout {

    /**
     * headerView
     */
     JdRefreshHeader mHeaderView;

    public JdRefreshLayout(Context context) {
        super(context);
        initView();
    }

    public JdRefreshLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        initView();
    }

    public JdRefreshLayout(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        initView();
    }


    /**
     * 初始化view
     */
    private void initView() {
        mHeaderView = new JdRefreshHeader(getContext());
        setHeaderView(mHeaderView);
        addPtrUIHandler(mHeaderView);
    }


}

my_jd.gif

2、天貓

天貓的更簡單,毫無動畫可言,說白了就是個GIF,大家可以去下載個apk,解壓后能得到其gif。

原理跟之前的是一樣,但這里我使用的是fresco進行加載gif,方法有很多,大家感興趣的可以去試試。

tmall_origin.gif

/**
 * 下拉刷新header
 * Created by shenminjie on 2016/12/6.
 */
public class TmallRefreshHeader extends FrameLayout implements PtrUIHandler {

    /**
     * 提醒文本
     */
    private TextView mTvRemind;


    /**
     * 狀態識別
     */
    private int mState;

    /**
     * 重置
     * 準備刷新
     * 開始刷新
     * 結束刷新
     */
    public static final int STATE_RESET = -1;
    public static final int STATE_PREPARE = 0;
    public static final int STATE_BEGIN = 1;
    public static final int STATE_FINISH = 2;


    public TmallRefreshHeader(Context context) {
        super(context);
        initView();
    }

    public TmallRefreshHeader(Context context, AttributeSet attrs) {
        super(context, attrs);
        initView();
    }

    public TmallRefreshHeader(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initView();
    }

    /**
     * 初始化view
     */
    private void initView() {
        View view = LayoutInflater.from(getContext()).inflate(R.layout.tmall_refresh_header_view, this, false);
        mTvRemind = (TextView) view.findViewById(R.id.tv_remind);
        SimpleDraweeView sdv = (SimpleDraweeView) view.findViewById(R.id.tm_logo);
        DraweeController mDraweeController = Fresco.newDraweeControllerBuilder()
                .setAutoPlayAnimations(true)
                //設置uri,加載本地的gif資源
                .setUri(Uri.parse("res://" + getContext().getPackageName() + "/" + R.drawable.tm_mui_bike))//設置uri
                .build();
        sdv.setController(mDraweeController);
        addView(view);
    }


    @Override
    public void onUIReset(PtrFrameLayout frame) {
        mState = STATE_RESET;
    }

    @Override
    public void onUIRefreshPrepare(PtrFrameLayout frame) {
        mState = STATE_PREPARE;
    }

    @Override
    public void onUIRefreshBegin(PtrFrameLayout frame) {
        mState = STATE_BEGIN;
    }

    @Override
    public void onUIRefreshComplete(PtrFrameLayout frame) {
        mState = STATE_FINISH;
    }

    @Override
    public void onUIPositionChange(PtrFrameLayout frame, boolean isUnderTouch, byte status, PtrIndicator ptrIndicator) {
        //處理提醒字體
        switch (mState) {
            case STATE_PREPARE:
                if (ptrIndicator.getCurrentPercent() < 1) {
                    mTvRemind.setText("下拉刷新");
                } else {
                    mTvRemind.setText("松開立即刷新");
                }
                break;
            case STATE_BEGIN:
                mTvRemind.setText("正在刷新...");
                break;
            case STATE_FINISH:
                mTvRemind.setText("加載完成...");
                break;
        }
    }
}

/**
 * 仿天貓下拉刷新view
 * Created by shenminjie on 2016/12/6.
 */

public class TmallRefreshLayout extends PtrFrameLayout {

    /**
     * headerView
     */
     TmallRefreshHeader mHeaderView;

    public TmallRefreshLayout(Context context) {
        super(context);
        initView();
    }

    public TmallRefreshLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        initView();
    }

    public TmallRefreshLayout(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        initView();
    }


    /**
     * 初始化view
     */
    private void initView() {
        mHeaderView = new TmallRefreshHeader(getContext());
        setHeaderView(mHeaderView);
        addPtrUIHandler(mHeaderView);
    }


}

my_tmall.gif

 

 

 

 

 本文由用戶 fangcao 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!