美團下拉刷新效果實現

在上一次【http://www.cnblogs.com/webor2006/p/7989766.html】完成了文字箭頭的下拉刷新效果,實際上是一個通用下拉刷新方案,此次接着這個刷新方案實現一下美團外賣下拉的效果,這個我想用過美團的親們確定都比較熟了,仍是看一下本尊:html

而此次實現的最終效果以下:android

 

效果基本上跟本尊差很少,並且只是換了個頭部效果,其核心下拉的代碼全是上一次我們寫好的,因此經過這個框架就能實現萬能下拉刷新的效果,因此接下來看一下如何在儘可能小的改動前提下達到咱們更換頭的下拉的效果,在正式實現美團效果以前先須要對代碼進行一些調整,仍是基於上次寫的萬能下拉刷新的代碼進行,下面開始:web

內容視圖變爲其它視圖後的處理:網絡

既然我們打造的是一個萬能的下拉刷新,而上一次內容區域是用的RecyclerView,以下:框架

那此時將內容進行更換一下,測試下是否都能正常的下拉刷新,首先將內容換成一個文本,以下:ide

因爲將RecyclerView從佈局中註釋掉了,那在MainActivity中關於它的初始化也得暫且註釋掉,不然會編譯錯誤的:佈局

//加入RecyclerView以後的事件處理
public class MainActivity extends AppCompatActivity implements RefreshLayout.OnRefreshingListener {

    private RefreshLayout lay_refresh_layout;
    private RecyclerView lay_rlv;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        lay_refresh_layout = (RefreshLayout) findViewById(R.id.lay_refresh_layout);
        lay_refresh_layout.setSelfHeaderViewManager(new SelfHeaderViewManager(this));
        lay_refresh_layout.setOnRefreshingListener(this);
//        initRecyclerView();
    }

    @Override
    public void onRefresh() {
        //獲取網絡數據
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                //當獲取完數據後通知RefreshLayout還原
                lay_refresh_layout.endRefreshing();
            }
        }, 2000);
    }

    private void initRecyclerView() {
        lay_rlv = (RecyclerView) findViewById(R.id.lay_rlv);
        lay_rlv.setLayoutManager(new LinearLayoutManager(this));
        List<String> datas = new ArrayList<>();
        for (int i = 0; i < 30; i++) {
            datas.add("條目" + i);
        }
        MainActivity.MyAdapter adapter = new MainActivity.MyAdapter(datas);
        lay_rlv.setAdapter(adapter);
    }


    private class MyAdapter extends RecyclerView.Adapter<MainActivity.MyAdapter.MyViewHolder> {
        private List<String> datas;

        public MyAdapter(List<String> datas) {
            this.datas = datas;
        }

        @Override
        public MainActivity.MyAdapter.MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            View view = LayoutInflater.from(parent.getContext()).inflate(android.R.layout.simple_list_item_1, null);//TODO
            return new MainActivity.MyAdapter.MyViewHolder(view);
        }

        @Override
        public void onBindViewHolder(MainActivity.MyAdapter.MyViewHolder holder, int position) {
            holder.tv.setText(datas.get(position));
        }

        @Override
        public int getItemCount() {
            return datas.size();
        }

        class MyViewHolder extends RecyclerView.ViewHolder {
            private TextView tv;

            public MyViewHolder(View itemView) {
                super(itemView);
                tv = (TextView) itemView.findViewById(android.R.id.text1);
            }
        }
    }
}

編譯運行:post

嗯~~木問題,那接下來再將內容由文本換成ScrollView試試,修改佈局文件:測試

<?xml version="1.0" encoding="utf-8"?>
<com.pulltorefresh.test.meituan.RefreshLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/lay_refresh_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.pulltorefresh.test.meituan.MainActivity">

    <!--
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="測試文本" />

        <android.support.v7.widget.RecyclerView
            android:id="@+id/lay_rlv"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
    -->

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

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical">

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="150dp"
                android:text="測試文本"
                android:textSize="30dp" />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="150dp"
                android:text="測試文本"
                android:textSize="30dp" />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="150dp"
                android:text="測試文本"
                android:textSize="30dp" />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="150dp"
                android:text="測試文本"
                android:textSize="30dp" />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="150dp"
                android:text="測試文本"
                android:textSize="30dp" />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="150dp"
                android:text="測試文本"
                android:textSize="30dp" />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="150dp"
                android:text="測試文本"
                android:textSize="30dp" />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="150dp"
                android:text="測試文本"
                android:textSize="30dp" />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="150dp"
                android:text="測試文本"
                android:textSize="30dp" />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="150dp"
                android:text="測試文本"
                android:textSize="30dp" />
        </LinearLayout>
    </ScrollView>

</com.pulltorefresh.test.meituan.RefreshLayout>

這時編譯運行:動畫

呃~~有問題,竟然換成ScrollView以後下拉刷新出不來了,這是什麼緣由形成的呢?照理來講若是發現滑動到頂部以後,再往下拉則應該拉出頭部的,那我們先看一下這塊的代碼邏輯:

而此時已經木有recyclerView這個控件了,因此RefreshScollingUtil.isRecyclerViewToTop(null)參數就爲null了,這時看一下它裏面的具體邏輯:

因此~~解決之道就是須要加一個ScollView的判斷,具體以下:

/**
 * 下拉刷新控件
 */
public class RefreshLayout extends LinearLayout {
    //constants
    /* 頭部視圖超出最大範圍的係數 */
    public static final float MAX_WHOLE_HEADER_VIEW_PADDING_TOP_RADIO = 0.3f;
    /* 阻尼效果的拉出係數 */
    public static final float DRAG_RADIO = 1.8f;

    /* 刷新狀態 */
    public enum RefreshStatus {
        IDLE/*靜止*/, PULL_DOWN/*下拉*/, RELEASE_REFRESH/*釋放刷新*/, REFRESHING/*刷新*/
    }

    //views
    /* 頭部根佈局 */
    private LinearLayout wholeHeaderView;

    //variables
    /* 具體頭部管理器 */
    private SelfHeaderViewManager selfHeaderViewManager;
    /* 頭部視圖的最大上邊距,也就是默認需經過它來將頭部隱藏掉 */
    private int minWholeHeaderViewPaddingTop;
    /* 頭部視圖的最大上邊距=頭部視圖的高度*頭部視圖超出最大範圍的係數,也就是下拉頭部顯示高度的最大值 */
    private int maxWholeHeaderViewPaddingTop;
    private int downY;
    private RefreshLayout.RefreshStatus currentStatus = RefreshLayout.RefreshStatus.IDLE;
    /* 刷新回調監聽 */
    private RefreshLayout.OnRefreshingListener onRefreshingListener;
    private float interceptDownX;
    private float interceptDownY;
    private RecyclerView recyclerView;
    private ScrollView scrollView;

    public void setOnRefreshingListener(RefreshLayout.OnRefreshingListener onRefreshingListener) {
        this.onRefreshingListener = onRefreshingListener;
    }

    /**
     * 設置自定義頭部管理器
     */
    public void setSelfHeaderViewManager(SelfHeaderViewManager selfHeaderViewManager) {
        this.selfHeaderViewManager = selfHeaderViewManager;
        initSelfHeaderView();
    }

    public RefreshLayout(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        this.setOrientation(LinearLayout.VERTICAL);
        init();
    }

    private void init() {
        //一、初始化頭部的視圖
        initWholeHeaderView();
    }

    private void initWholeHeaderView() {
        //動態添加一個頭部的根部局,以便將來能夠動態更換頭部:好比上下箭頭效果、美團效果等
        wholeHeaderView = new LinearLayout(getContext());
        wholeHeaderView.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
        wholeHeaderView.setBackgroundColor(Color.parseColor("#FF4081"));
        addView(wholeHeaderView);
    }

    //初始化具體的頭部內容
    private void initSelfHeaderView() {
        View selfHeaderView = selfHeaderViewManager.getSelfHeaderView();
        int selfHeaderViewMeasuredHeight = this.selfHeaderViewManager.getSelfHeaderViewHeight();
        minWholeHeaderViewPaddingTop = -selfHeaderViewMeasuredHeight;
        //最大邊界定義爲頭部高度的30%
        maxWholeHeaderViewPaddingTop = (int) (selfHeaderViewMeasuredHeight * MAX_WHOLE_HEADER_VIEW_PADDING_TOP_RADIO);
        //利用給頭部根佈局設置padding爲負達到隱藏它裏面子視圖的效果
        wholeHeaderView.setPadding(0, minWholeHeaderViewPaddingTop, 0, 0);
        wholeHeaderView.addView(selfHeaderView);
    }

    //處理滑動衝突
    @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                interceptDownX = event.getX();
                interceptDownY = event.getY();
                break;
            case MotionEvent.ACTION_MOVE:
                float dy = event.getY() - interceptDownY;
                //一、y方向的變化:y方向的變化量>x方向的變化量
                if (Math.abs(event.getX() - interceptDownX) < Math.abs(dy)) {
                    //二、y方向向下滑動
                    if (dy > 0 && handleRefresh()) {
                        return true;
                    }
                }
                break;
            case MotionEvent.ACTION_UP:
                break;
        }
        return super.onInterceptTouchEvent(event);
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        View contentView = getChildAt(1);
        if (contentView instanceof RecyclerView) {
            this.recyclerView = (RecyclerView) contentView;
        } else if (contentView instanceof ScrollView) {
            this.scrollView = (ScrollView) contentView; }
    }

    //判斷是否RecyclerListView滑到了頂端,只有在頂端的時候再向下滑纔會出現刷新頭部
    private boolean handleRefresh() {
        if (RefreshScrollingUtil.isRecyclerViewToTop(recyclerView)) {
            return true;
        }
        if (RefreshScrollingUtil.isScrollViewOrWebViewToTop(scrollView)) {
            return true;
        }
        return false;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                downY = (int) event.getY();
                Log.e("cexo", "onTouchEvent() ACTION_DOWN downY=" + downY);
                return true;
            case MotionEvent.ACTION_MOVE:
                if (handleActionMove(event))
                    return true;
                break;
            case MotionEvent.ACTION_UP:
                if (handleActionUp(event))
                    return true;
                break;
        }
        return super.onTouchEvent(event);//注意:因爲未來會套在ListView上面,因此這裏不能一股腦的將它返回true
    }

    private boolean handleActionMove(MotionEvent event) {
        if (currentStatus == RefreshLayout.RefreshStatus.REFRESHING)//若是是刷新狀態了,則不容許移動了
            return false;
        //因爲在down的時候,當前控件並無攔截事件,down事件被recyclerview消費掉了,
        // 當前控件直接進入move狀態,因爲當前控件的onTouchEvent的down事件未執行,形成downY爲0,咱們要在move的時候給downY從新賦值
        if (downY == 0)
            downY = (int) event.getY();
        int moveY = (int) event.getY();
        int dY = moveY - downY;
        Log.e("cexo", "dY:" + dY + ";moveY:" + moveY + "downY:" + downY);
        //只有向下移動才能拉出頭部
        if (dY > 0) {
//            int paddingTop = minWholeHeaderViewPaddingTop + dY;
            //阻尼效果:就是相似彈簧的效果,隨距離愈來愈長,拉動愈來愈難,讓dy除以一個係數,不讓它是線性變化
            int paddingTop = (int) (minWholeHeaderViewPaddingTop + dY / DRAG_RADIO);
            Log.e("cexo", "paddingTop:" + paddingTop);
            if (paddingTop < 0 && currentStatus != RefreshLayout.RefreshStatus.PULL_DOWN) {
                currentStatus = RefreshLayout.RefreshStatus.PULL_DOWN;
                //改變文字爲下拉刷新
                handleRefreshStatusChanged();
            } else if (paddingTop >= 0 && currentStatus != RefreshLayout.RefreshStatus.RELEASE_REFRESH) {
                currentStatus = RefreshLayout.RefreshStatus.RELEASE_REFRESH;
                //改變文字爲釋放刷新,並箭頭進行旋轉
                handleRefreshStatusChanged();
            }
            //判斷若是paddingTop>maxWholeHeaderViewPaddingTop,就不能再滑動了
            paddingTop = Math.min(paddingTop, maxWholeHeaderViewPaddingTop);
            wholeHeaderView.setPadding(0, paddingTop, 0, 0);
            return true;
        }
        return false;
    }

    private boolean handleActionUp(MotionEvent event) {
        downY = 0;//解決有時後現拉拉不動的bug
        if (currentStatus == RefreshLayout.RefreshStatus.PULL_DOWN) {
            //若是是下拉刷新狀態則鬆開手時直接讓頭部隱藏
            hiddenRefreshView();
            currentStatus = RefreshLayout.RefreshStatus.IDLE;
            //若是換爲美團下拉刷新等,當頭部回到初始狀態時須要作一些還原操做
            handleRefreshStatusChanged();
        } else if (currentStatus == RefreshLayout.RefreshStatus.RELEASE_REFRESH) {
            beginRefreshing();
        }
        //只要將頭部拉出一點點UP事件就由當前控件處理
        return wholeHeaderView.getPaddingTop() > minWholeHeaderViewPaddingTop;
    }

    private void hiddenRefreshView() {
        ValueAnimator valueAnimator = ValueAnimator.ofInt(wholeHeaderView.getPaddingTop(), minWholeHeaderViewPaddingTop);
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {
                //獲取值動畫在動畫變化過程當中的值
                int currentPaddingTop = (int) valueAnimator.getAnimatedValue();
                wholeHeaderView.setPadding(0, currentPaddingTop, 0, 0);
            }
        });
        valueAnimator.setDuration(300);
        valueAnimator.start();
    }

    /**
     * 開始刷新
     */
    private void beginRefreshing() {
        currentStatus = RefreshLayout.RefreshStatus.REFRESHING;
        changeHeaderViewPaddingTopToZero();
        handleRefreshStatusChanged();
        if (onRefreshingListener != null)
            onRefreshingListener.onRefresh();
    }

    /**
     * 結束刷新
     */
    public void endRefreshing() {
        hiddenRefreshView();
        currentStatus = RefreshLayout.RefreshStatus.IDLE;
        //作SelfHeaderView的還原處理
        selfHeaderViewManager.endRefreshing();
    }

    /**
     * 將頭部的paddingTop改變爲0,也就是還原成頭部的高度
     */
    private void changeHeaderViewPaddingTopToZero() {
        ValueAnimator valueAnimator = ValueAnimator.ofInt(wholeHeaderView.getPaddingTop(), 0);
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {
                //獲取值動畫在動畫變化過程當中的值
                int currentPaddingTop = (int) valueAnimator.getAnimatedValue();
                wholeHeaderView.setPadding(0, currentPaddingTop, 0, 0);
            }
        });
        valueAnimator.setDuration(300);
        valueAnimator.start();
    }

    /**
     * 根據當前的下拉狀態來作界面刷新,具體實現由manager處理
     */
    private void handleRefreshStatusChanged() {
        switch (currentStatus) {
            case IDLE:
                selfHeaderViewManager.changeToIdle();
                break;
            case PULL_DOWN:
                selfHeaderViewManager.changeToPullDown();
                break;
            case RELEASE_REFRESH:
                selfHeaderViewManager.changeToReleaseRefresh();
                break;
            case REFRESHING:
                selfHeaderViewManager.changeToRefreshing();
                break;
        }
    }

    public interface OnRefreshingListener {
        void onRefresh();
    }
}

這時再編譯運行:

嗯~~此次算是解決了一個小缺陷,在繼續實現以前仍是將佈局還原成RecyclerView。

SelfHeaderViewManager的基類抽象:

接下來則開始要着手實現美團的下拉刷新效果啦,不過在實現以前還得對代碼進行一個抽象重構,由於目前代碼的框架還不太靈活,爲了打造「萬能」,因此這個抽象是很是有必要的,怎麼個抽象法呢? 還記得SelfHeaderViewManager這個頭部管理器類吧,當時之因此設計這個類也就是爲了適應將來不一樣的頭部下拉效果,因此照理它應該是一個抽象的類,封裝了通用的處理邏輯,而不一樣的頭部效果則由具體的頭部管理器去提供,而目前對於我們的這個帶文字箭頭的頭部管理端是直接寫死的,以下:

顯然這是不合理的,因此接下來就得將其合理化,首先我們定義一個默認的頭部管理器,直接從SelfHeaderViewManager類中進行拷貝重命名,其效果就是我們目前看到的帶箭頭的這個效果,以下:

這時NormalSelfHeaderViewManager和SelfHeaderViewManager的代碼確定是如出一轍的,此時將NormalSelfHeaderViewManager去繼承SelfHeaderViewManager,固然此時會報錯:

接下來就是要將通用的行爲所有提到SelfHeaderViewManager抽象類中啦,因此下面開始:

先分析一下哪些是通用的:

因此將這些行爲進行抽象化,都是須要由具體的管理器來提供的,具體以下:

此時則須要對NormalSelfHeaderViewManager基於這個基類進行相應的改造,對抽象方法進行實現,而且將公共的部分給刪除,此時它就變成這樣了:

/**
 * 默認的頭部視圖管理器
 */
public class NormalSelfHeaderViewManager extends SelfHeaderViewManager {

    /* 箭頭旋轉向上動畫 */
    private RotateAnimation upAnimation;
    /* 箭頭旋轉向下動畫 */
    private RotateAnimation downAnimation;
    /* Loading動畫 */
    private AnimationDrawable animationDrawable;

    /* 提示文本 */
    private TextView tv_normal_refresh_header_status;
    /* 箭頭 */
    private ImageView iv_normal_refresh_header_arrow;
    /* loadingView */
    private ImageView iv_normal_refresh_header_loading;

    public NormalSelfHeaderViewManager(Context context) {
        super(context);
        initAnimation();
    }

    private void initAnimation() {
        upAnimation = new RotateAnimation(0, -180, RotateAnimation.RELATIVE_TO_SELF, 0.5f, RotateAnimation.RELATIVE_TO_SELF, 0.5f);
        upAnimation.setDuration(100);
        //動畫執行完成後不會回到原點
        upAnimation.setFillAfter(true);

        downAnimation = new RotateAnimation(-180, 0, RotateAnimation.RELATIVE_TO_SELF, 0.5f, RotateAnimation.RELATIVE_TO_SELF, 0.5f);
        downAnimation.setDuration(100);
        //動畫執行完成後不會回到原點
        downAnimation.setFillAfter(true);
    }

    @Override
    protected View getSelfHeaderView() {
        if (selfHeaderView == null) {
            selfHeaderView = View.inflate(context, R.layout.view_refresh_header_normal, null);
            selfHeaderView.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT));
            tv_normal_refresh_header_status = selfHeaderView.findViewById(R.id.tv_normal_refresh_header_status);
            iv_normal_refresh_header_arrow = selfHeaderView.findViewById(R.id.iv_normal_refresh_header_arrow);
            iv_normal_refresh_header_loading = selfHeaderView.findViewById(R.id.iv_normal_refresh_header_loading);
            animationDrawable = (AnimationDrawable) iv_normal_refresh_header_loading.getDrawable();
        }
        return selfHeaderView;
    }

    @Override
    protected void changeToIdle() {
        //TODO
    }

    @Override
    protected void changeToPullDown() {
        tv_normal_refresh_header_status.setText("下拉刷新");
        iv_normal_refresh_header_arrow.startAnimation(downAnimation);
    }

    @Override
    protected void changeToReleaseRefresh() {
        tv_normal_refresh_header_status.setText("釋放刷新");
        iv_normal_refresh_header_arrow.startAnimation(upAnimation);
    }

    @Override
    protected void changeToRefreshing() {
        tv_normal_refresh_header_status.setText("加載中...");
        //因爲iv_normal_refresh_header_arrow以前設置過動畫,因此在隱藏以前先要清除動畫以後再設置隱藏這樣隱藏纔會生效
        iv_normal_refresh_header_arrow.clearAnimation();
        iv_normal_refresh_header_arrow.setVisibility(View.INVISIBLE);
        iv_normal_refresh_header_loading.setVisibility(View.VISIBLE);
        animationDrawable.start();
    }

    @Override
    protected void endRefreshing() {
        tv_normal_refresh_header_status.setText("下拉刷新");
        iv_normal_refresh_header_loading.setVisibility(View.INVISIBLE);
        iv_normal_refresh_header_arrow.setVisibility(View.VISIBLE);
        downAnimation.setDuration(0);//讓箭頭當即旋轉至向下方向
        iv_normal_refresh_header_arrow.startAnimation(downAnimation);
    }
}

最後在設置頭部管理器時就得更改成:

再去編譯運行,保證運行跟以前的效果同樣。

MeiTuan的SelfHeaderViewManager基本實現:

有了前期的準備工做以後,接下來就能夠真正開始實現美團的頭部效果啦,下面來看一下在萬能刷新框架之下要想加入一個新的下拉效果是何等的簡單:

首先新建一個美團的頭部管理器並繼承管理器基類,並重寫父類的抽象方法,以下:

/**
 * 美團的頭部視圖管理器,分爲三個階段:
 * 一、縮放階段:PULL_DOWN;
 * 二、小人跳出來的階段 RELEASE_REFRESH;
 * 三、小人頭部左右搖晃階段:REFRESHING;
 */
public class MeiTuanSelfHeaderViewManager extends SelfHeaderViewManager {

    public MeiTuanSelfHeaderViewManager(Context context) {
        super(context);
    }

    @Override
    protected View getSelfHeaderView() {
        return null;
    }

    @Override
    protected void changeToIdle() {

    }

    @Override
    protected void changeToPullDown() {

    }

    @Override
    protected void changeToReleaseRefresh() {

    }

    @Override
    protected void changeToRefreshing() {

    }

    @Override
    protected void endRefreshing() {

    }
}

而後將它設置到管理器中,以下:

接下來要作的就是填充這些方法,首先固然得提供美團頭部視圖的View啦,先準備佈局文件,以下:

view_refresh_header_meituan.xml:

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

    <FrameLayout
        android:layout_width="60dp"
        android:layout_height="60dp"
        android:layout_marginBottom="10dp"
        android:layout_marginTop="10dp">

        <ImageView
            android:id="@+id/iv_meituan_pull_down"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:src="@mipmap/refresh_mt_pull_down" />

        <ImageView
            android:id="@+id/iv_meituan_release_refreshing"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:visibility="invisible" />
    </FrameLayout>
</LinearLayout>

其佈局文件比較簡單中,下拉刷新一個ImageView,釋放刷新一個ImageView,而對於下拉刷新而言其實就是一張表態的圖片refresh_mt_pull_down.png,以下:

那咱們看效果在下拉時這個圖片是會隨着咱們手指的滑動不斷伸縮的嘛,那直接對圖片進行縮放既可,這塊不須要額外的資源圖。

另外對於拉出頭部以後,會有一個小人跳出的動畫,而釋放刷新以後小人會左右搖晃腦殼的動畫,實際上都是用圖片一幀幀播放出來的,也就是執行一個幀動畫,因此下面將這兩個動畫文件定義好:

小人跳出動畫:release_mt_refresh.xml:

<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
    android:oneshot="true">
    <item
        android:drawable="@mipmap/refresh_mt_change_to_release_refresh_01"
        android:duration="100" />
    <item
        android:drawable="@mipmap/refresh_mt_change_to_release_refresh_02"
        android:duration="100" />
    <item
        android:drawable="@mipmap/refresh_mt_change_to_release_refresh_03"
        android:duration="100" />
    <item
        android:drawable="@mipmap/refresh_mt_change_to_release_refresh_04"
        android:duration="100" />
    <item
        android:drawable="@mipmap/refresh_mt_change_to_release_refresh_05"
        android:duration="100" />
</animation-list>

其涉及到的圖片按順序以下:

小人左右搖晃動畫:refresh_mt_refreshing.xml:

<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
    android:oneshot="false">
    <item
        android:drawable="@mipmap/refresh_mt_refreshing_01"
        android:duration="100" />
    <item
        android:drawable="@mipmap/refresh_mt_refreshing_02"
        android:duration="100" />
    <item
        android:drawable="@mipmap/refresh_mt_refreshing_03"
        android:duration="100" />
    <item
        android:drawable="@mipmap/refresh_mt_refreshing_04"
        android:duration="100" />
    <item
        android:drawable="@mipmap/refresh_mt_refreshing_05"
        android:duration="100" />
    <item
        android:drawable="@mipmap/refresh_mt_refreshing_06"
        android:duration="100" />
    <item
        android:drawable="@mipmap/refresh_mt_refreshing_07"
        android:duration="100" />
    <item
        android:drawable="@mipmap/refresh_mt_refreshing_08"
        android:duration="100" />
</animation-list>

其涉及到的圖片按順序以下:

接下來先填充佈局到管理器當中,以下:

/**
 * 美團的頭部視圖管理器,分爲三個階段:
 * 一、縮放階段:PULL_DOWN;
 * 二、小人跳出來的階段 RELEASE_REFRESH;
 * 三、小人頭部左右搖晃階段:REFRESHING;
 */
public class MeiTuanSelfHeaderViewManager extends SelfHeaderViewManager {

    /* 下拉狀態View */
    private ImageView iv_meituan_pull_down;
    /* 釋放刷新View */
    private ImageView iv_meituan_release_refreshing;

    public MeiTuanSelfHeaderViewManager(Context context) {
        super(context);
    }

    @Override
    protected View getSelfHeaderView() {
        if (selfHeaderView == null) {
            selfHeaderView = View.inflate(context, R.layout.view_refresh_header_meituan, null);
            iv_meituan_pull_down = selfHeaderView.findViewById(R.id.iv_meituan_pull_down);
            iv_meituan_release_refreshing = selfHeaderView.findViewById(R.id.iv_meituan_release_refreshing);
        }
        return selfHeaderView;
    }

    @Override
    protected void changeToIdle() {

    }

    @Override
    protected void changeToPullDown() {

    }

    @Override
    protected void changeToReleaseRefresh() {

    }

    @Override
    protected void changeToRefreshing() {

    }

    @Override
    protected void endRefreshing() {

    }
}

接下來先來處理拉出頭部小人跳出來的效果,也就是changeToReleaseRefresh()方法:

接下來看一下效果:

嗯~~有那點意思了~~不過如今不居中,是啥緣由呢?由於沒有給View設置佈局參數,以下:

再次編譯運行:

不過狀態有點問題,那是由於還有不少方法還木有處理嘛,接下來再處理下拉時的狀態changeToPullDown(),這時應該就顯示那張靜止的圖,以下:

這時再運行:

嗯~~效果好一些了,不過還得處理其它狀態,這裏處理釋放刷新時小人左右搖晃的效,也就是處理changeToRefreshing()方法,以下:

編譯運行:

另外還有一個細節須要處理,就是當數據加載完成以後,應該將小人的狀態回到那個靜止的圖片上來,也就是在endRefreshing()方法上處理,以下:

縮放和收尾處理:

接下來就得處理下拉時那個靜止的圖片有個縮放的效果,那如何作呢?先來找處處理下拉的這塊代碼:

很明顯目前這個條件裏面的代碼在下拉時只會執行一遍,由於加了狀態判斷,而跟須要根據下拉距離不斷讓圖片進行縮放目標有點違背了,因此這裏將條件進行以下調整:

而爲了擴展,因此這裏將其處理下拉時View的狀態轉由Manager去處理,以下:

這時固然具體子類得重寫這個新定義的方法啦,這裏就不貼代碼了。

那問題的焦點就回到了如何去計算這個縮放比的值,其實比較簡單:用當前的paddingTop/頭部視圖的最大上邊距minWholeHeaderViewPaddingTop不就能夠得出一個比例了麼?具體以下:

那下面運行觀察一下這個值的變化規律:

其值的變化爲:

01-20 08:22:02.379 6125-6125/com.pulltorefresh.test E/cexo: scale:0.9875
01-20 08:22:02.395 6125-6125/com.pulltorefresh.test E/cexo: scale:0.975
01-20 08:22:02.411 6125-6125/com.pulltorefresh.test E/cexo: scale:0.9625
01-20 08:22:02.427 6125-6125/com.pulltorefresh.test E/cexo: scale:0.9625
01-20 08:22:02.443 6125-6125/com.pulltorefresh.test E/cexo: scale:0.95
01-20 08:22:02.463 6125-6125/com.pulltorefresh.test E/cexo: scale:0.9375
01-20 08:22:02.479 6125-6125/com.pulltorefresh.test E/cexo: scale:0.9375
01-20 08:22:02.495 6125-6125/com.pulltorefresh.test E/cexo: scale:0.9375
01-20 08:22:02.511 6125-6125/com.pulltorefresh.test E/cexo: scale:0.9125
01-20 08:22:02.531 6125-6125/com.pulltorefresh.test E/cexo: scale:0.9125
01-20 08:22:02.547 6125-6125/com.pulltorefresh.test E/cexo: scale:0.9125
01-20 08:22:02.579 6125-6125/com.pulltorefresh.test E/cexo: scale:0.9
01-20 08:22:02.611 6125-6125/com.pulltorefresh.test E/cexo: scale:0.9
01-20 08:22:02.627 6125-6125/com.pulltorefresh.test E/cexo: scale:0.9
01-20 08:22:02.643 6125-6125/com.pulltorefresh.test E/cexo: scale:0.8875
01-20 08:22:02.659 6125-6125/com.pulltorefresh.test E/cexo: scale:0.8875
01-20 08:22:02.679 6125-6125/com.pulltorefresh.test E/cexo: scale:0.8875
01-20 08:22:02.711 6125-6125/com.pulltorefresh.test E/cexo: scale:0.875
01-20 08:22:02.731 6125-6125/com.pulltorefresh.test E/cexo: scale:0.875
01-20 08:22:02.743 6125-6125/com.pulltorefresh.test E/cexo: scale:0.875
01-20 08:22:02.759 6125-6125/com.pulltorefresh.test E/cexo: scale:0.85
01-20 08:22:02.779 6125-6125/com.pulltorefresh.test E/cexo: scale:0.85
01-20 08:22:02.795 6125-6125/com.pulltorefresh.test E/cexo: scale:0.85
01-20 08:22:02.811 6125-6125/com.pulltorefresh.test E/cexo: scale:0.8375
01-20 08:22:02.827 6125-6125/com.pulltorefresh.test E/cexo: scale:0.825
01-20 08:22:02.843 6125-6125/com.pulltorefresh.test E/cexo: scale:0.8125
01-20 08:22:02.859 6125-6125/com.pulltorefresh.test E/cexo: scale:0.8
01-20 08:22:02.879 6125-6125/com.pulltorefresh.test E/cexo: scale:0.7875
01-20 08:22:02.895 6125-6125/com.pulltorefresh.test E/cexo: scale:0.7875
01-20 08:22:02.911 6125-6125/com.pulltorefresh.test E/cexo: scale:0.775
01-20 08:22:02.931 6125-6125/com.pulltorefresh.test E/cexo: scale:0.7625
01-20 08:22:02.959 6125-6125/com.pulltorefresh.test E/cexo: scale:0.7625
01-20 08:22:02.979 6125-6125/com.pulltorefresh.test E/cexo: scale:0.75
01-20 08:22:02.995 6125-6125/com.pulltorefresh.test E/cexo: scale:0.7375
01-20 08:22:03.015 6125-6125/com.pulltorefresh.test E/cexo: scale:0.725
01-20 08:22:03.027 6125-6125/com.pulltorefresh.test E/cexo: scale:0.7375
01-20 08:22:03.055 6125-6125/com.pulltorefresh.test E/cexo: scale:0.725
01-20 08:22:03.079 6125-6125/com.pulltorefresh.test E/cexo: scale:0.725
01-20 08:22:03.099 6125-6125/com.pulltorefresh.test E/cexo: scale:0.725
01-20 08:22:03.111 6125-6125/com.pulltorefresh.test E/cexo: scale:0.7125
01-20 08:22:03.127 6125-6125/com.pulltorefresh.test E/cexo: scale:0.7125
01-20 08:22:03.147 6125-6125/com.pulltorefresh.test E/cexo: scale:0.7
01-20 08:22:03.179 6125-6125/com.pulltorefresh.test E/cexo: scale:0.6625
01-20 08:22:03.207 6125-6125/com.pulltorefresh.test E/cexo: scale:0.6625
01-20 08:22:03.227 6125-6125/com.pulltorefresh.test E/cexo: scale:0.65
01-20 08:22:03.255 6125-6125/com.pulltorefresh.test E/cexo: scale:0.6375
01-20 08:22:03.267 6125-6125/com.pulltorefresh.test E/cexo: scale:0.6375
01-20 08:22:03.279 6125-6125/com.pulltorefresh.test E/cexo: scale:0.625
01-20 08:22:03.295 6125-6125/com.pulltorefresh.test E/cexo: scale:0.625
01-20 08:22:03.311 6125-6125/com.pulltorefresh.test E/cexo: scale:0.6125
01-20 08:22:03.327 6125-6125/com.pulltorefresh.test E/cexo: scale:0.6
01-20 08:22:03.347 6125-6125/com.pulltorefresh.test E/cexo: scale:0.6
01-20 08:22:03.363 6125-6125/com.pulltorefresh.test E/cexo: scale:0.5875
01-20 08:22:03.379 6125-6125/com.pulltorefresh.test E/cexo: scale:0.575
01-20 08:22:03.395 6125-6125/com.pulltorefresh.test E/cexo: scale:0.575
01-20 08:22:03.423 6125-6125/com.pulltorefresh.test E/cexo: scale:0.575
01-20 08:22:03.427 6125-6125/com.pulltorefresh.test E/cexo: scale:0.55
01-20 08:22:03.443 6125-6125/com.pulltorefresh.test E/cexo: scale:0.55
01-20 08:22:03.463 6125-6125/com.pulltorefresh.test E/cexo: scale:0.5375
01-20 08:22:03.479 6125-6125/com.pulltorefresh.test E/cexo: scale:0.5375
01-20 08:22:03.495 6125-6125/com.pulltorefresh.test E/cexo: scale:0.5375
01-20 08:22:03.511 6125-6125/com.pulltorefresh.test E/cexo: scale:0.525
01-20 08:22:03.531 6125-6125/com.pulltorefresh.test E/cexo: scale:0.5125
01-20 08:22:03.543 6125-6125/com.pulltorefresh.test E/cexo: scale:0.5125
01-20 08:22:03.563 6125-6125/com.pulltorefresh.test E/cexo: scale:0.5125
01-20 08:22:03.579 6125-6125/com.pulltorefresh.test E/cexo: scale:0.5125
01-20 08:22:03.595 6125-6125/com.pulltorefresh.test E/cexo: scale:0.5
01-20 08:22:03.611 6125-6125/com.pulltorefresh.test E/cexo: scale:0.4875
01-20 08:22:03.627 6125-6125/com.pulltorefresh.test E/cexo: scale:0.475
01-20 08:22:03.643 6125-6125/com.pulltorefresh.test E/cexo: scale:0.475
01-20 08:22:03.659 6125-6125/com.pulltorefresh.test E/cexo: scale:0.4625
01-20 08:22:03.679 6125-6125/com.pulltorefresh.test E/cexo: scale:0.4625
01-20 08:22:03.695 6125-6125/com.pulltorefresh.test E/cexo: scale:0.45
01-20 08:22:03.711 6125-6125/com.pulltorefresh.test E/cexo: scale:0.45
01-20 08:22:03.727 6125-6125/com.pulltorefresh.test E/cexo: scale:0.425
01-20 08:22:03.763 6125-6125/com.pulltorefresh.test E/cexo: scale:0.4
01-20 08:22:03.779 6125-6125/com.pulltorefresh.test E/cexo: scale:0.4
01-20 08:22:03.795 6125-6125/com.pulltorefresh.test E/cexo: scale:0.3875
01-20 08:22:03.811 6125-6125/com.pulltorefresh.test E/cexo: scale:0.3875
01-20 08:22:03.831 6125-6125/com.pulltorefresh.test E/cexo: scale:0.375
01-20 08:22:03.843 6125-6125/com.pulltorefresh.test E/cexo: scale:0.375
01-20 08:22:03.859 6125-6125/com.pulltorefresh.test E/cexo: scale:0.3625
01-20 08:22:03.879 6125-6125/com.pulltorefresh.test E/cexo: scale:0.35
01-20 08:22:03.895 6125-6125/com.pulltorefresh.test E/cexo: scale:0.3375
01-20 08:22:03.911 6125-6125/com.pulltorefresh.test E/cexo: scale:0.3375
01-20 08:22:03.927 6125-6125/com.pulltorefresh.test E/cexo: scale:0.325
01-20 08:22:03.947 6125-6125/com.pulltorefresh.test E/cexo: scale:0.3125
01-20 08:22:03.963 6125-6125/com.pulltorefresh.test E/cexo: scale:0.3125
01-20 08:22:03.995 6125-6125/com.pulltorefresh.test E/cexo: scale:0.275
01-20 08:22:04.011 6125-6125/com.pulltorefresh.test E/cexo: scale:0.2625
01-20 08:22:04.027 6125-6125/com.pulltorefresh.test E/cexo: scale:0.25
01-20 08:22:04.047 6125-6125/com.pulltorefresh.test E/cexo: scale:0.2375
01-20 08:22:04.059 6125-6125/com.pulltorefresh.test E/cexo: scale:0.2375
01-20 08:22:04.079 6125-6125/com.pulltorefresh.test E/cexo: scale:0.225
01-20 08:22:04.095 6125-6125/com.pulltorefresh.test E/cexo: scale:0.2125
01-20 08:22:04.111 6125-6125/com.pulltorefresh.test E/cexo: scale:0.2125
01-20 08:22:04.127 6125-6125/com.pulltorefresh.test E/cexo: scale:0.2125
01-20 08:22:04.143 6125-6125/com.pulltorefresh.test E/cexo: scale:0.2
01-20 08:22:04.163 6125-6125/com.pulltorefresh.test E/cexo: scale:0.2
01-20 08:22:04.179 6125-6125/com.pulltorefresh.test E/cexo: scale:0.1875
01-20 08:22:04.207 6125-6125/com.pulltorefresh.test E/cexo: scale:0.1625
01-20 08:22:04.227 6125-6125/com.pulltorefresh.test E/cexo: scale:0.1625
01-20 08:22:04.247 6125-6125/com.pulltorefresh.test E/cexo: scale:0.15
01-20 08:22:04.263 6125-6125/com.pulltorefresh.test E/cexo: scale:0.15
01-20 08:22:04.279 6125-6125/com.pulltorefresh.test E/cexo: scale:0.1375
01-20 08:22:04.295 6125-6125/com.pulltorefresh.test E/cexo: scale:0.1375
01-20 08:22:04.311 6125-6125/com.pulltorefresh.test E/cexo: scale:0.125
01-20 08:22:04.347 6125-6125/com.pulltorefresh.test E/cexo: scale:0.1
01-20 08:22:04.363 6125-6125/com.pulltorefresh.test E/cexo: scale:0.1
01-20 08:22:04.383 6125-6125/com.pulltorefresh.test E/cexo: scale:0.075
01-20 08:22:04.423 6125-6125/com.pulltorefresh.test E/cexo: scale:0.075
01-20 08:22:04.443 6125-6125/com.pulltorefresh.test E/cexo: scale:0.0625
01-20 08:22:04.463 6125-6125/com.pulltorefresh.test E/cexo: scale:0.0625
01-20 08:22:04.479 6125-6125/com.pulltorefresh.test E/cexo: scale:0.0625
01-20 08:22:04.495 6125-6125/com.pulltorefresh.test E/cexo: scale:0.0375
01-20 08:22:04.511 6125-6125/com.pulltorefresh.test E/cexo: scale:0.0375
01-20 08:22:04.527 6125-6125/com.pulltorefresh.test E/cexo: scale:0.0375
01-20 08:22:04.543 6125-6125/com.pulltorefresh.test E/cexo: scale:0.025
01-20 08:22:04.563 6125-6125/com.pulltorefresh.test E/cexo: scale:0.025
01-20 08:22:04.579 6125-6125/com.pulltorefresh.test E/cexo: scale:0.0125
01-20 08:22:04.595 6125-6125/com.pulltorefresh.test E/cexo: scale:0.0125

從0.9到0.0,顯然這個比值跟我們預期的恰好相反,比值應該是隨着下拉距離的增大而增大,因此這裏我們手動將其按我們的意圖來走,也就是從0.0到0.9,以下:

這時再來看一下比值變化:

01-20 08:25:48.875 6199-6199/com.pulltorefresh.test E/cexo: scale:0.024999976
01-20 08:25:48.879 6199-6199/com.pulltorefresh.test E/cexo: scale:0.024999976
01-20 08:25:49.863 6199-6199/com.pulltorefresh.test E/cexo: scale:0.024999976
01-20 08:25:49.879 6199-6199/com.pulltorefresh.test E/cexo: scale:0.037500024
01-20 08:25:49.895 6199-6199/com.pulltorefresh.test E/cexo: scale:0.050000012
01-20 08:25:49.911 6199-6199/com.pulltorefresh.test E/cexo: scale:0.0625
01-20 08:25:49.927 6199-6199/com.pulltorefresh.test E/cexo: scale:0.0625
01-20 08:25:49.943 6199-6199/com.pulltorefresh.test E/cexo: scale:0.087499976
01-20 08:25:49.959 6199-6199/com.pulltorefresh.test E/cexo: scale:0.100000024
01-20 08:25:49.979 6199-6199/com.pulltorefresh.test E/cexo: scale:0.11250001
01-20 08:25:49.999 6199-6199/com.pulltorefresh.test E/cexo: scale:0.11250001
01-20 08:25:50.015 6199-6199/com.pulltorefresh.test E/cexo: scale:0.11250001
01-20 08:25:50.027 6199-6199/com.pulltorefresh.test E/cexo: scale:0.125
01-20 08:25:50.043 6199-6199/com.pulltorefresh.test E/cexo: scale:0.125
01-20 08:25:50.059 6199-6199/com.pulltorefresh.test E/cexo: scale:0.125
01-20 08:25:50.079 6199-6199/com.pulltorefresh.test E/cexo: scale:0.14999998
01-20 08:25:50.095 6199-6199/com.pulltorefresh.test E/cexo: scale:0.14999998
01-20 08:25:50.111 6199-6199/com.pulltorefresh.test E/cexo: scale:0.16250002
01-20 08:25:50.127 6199-6199/com.pulltorefresh.test E/cexo: scale:0.16250002
01-20 08:25:50.143 6199-6199/com.pulltorefresh.test E/cexo: scale:0.17500001
01-20 08:25:50.159 6199-6199/com.pulltorefresh.test E/cexo: scale:0.17500001
01-20 08:25:50.179 6199-6199/com.pulltorefresh.test E/cexo: scale:0.17500001
01-20 08:25:50.195 6199-6199/com.pulltorefresh.test E/cexo: scale:0.1875
01-20 08:25:50.211 6199-6199/com.pulltorefresh.test E/cexo: scale:0.1875
01-20 08:25:50.227 6199-6199/com.pulltorefresh.test E/cexo: scale:0.19999999
01-20 08:25:50.243 6199-6199/com.pulltorefresh.test E/cexo: scale:0.21249998
01-20 08:25:50.259 6199-6199/com.pulltorefresh.test E/cexo: scale:0.21249998
01-20 08:25:50.279 6199-6199/com.pulltorefresh.test E/cexo: scale:0.22500002
01-20 08:25:50.295 6199-6199/com.pulltorefresh.test E/cexo: scale:0.23750001
01-20 08:25:50.311 6199-6199/com.pulltorefresh.test E/cexo: scale:0.25
01-20 08:25:50.327 6199-6199/com.pulltorefresh.test E/cexo: scale:0.27499998
01-20 08:25:50.343 6199-6199/com.pulltorefresh.test E/cexo: scale:0.2625
01-20 08:25:50.363 6199-6199/com.pulltorefresh.test E/cexo: scale:0.27499998
01-20 08:25:50.379 6199-6199/com.pulltorefresh.test E/cexo: scale:0.27499998
01-20 08:25:50.395 6199-6199/com.pulltorefresh.test E/cexo: scale:0.28750002
01-20 08:25:50.411 6199-6199/com.pulltorefresh.test E/cexo: scale:0.3
01-20 08:25:50.427 6199-6199/com.pulltorefresh.test E/cexo: scale:0.3
01-20 08:25:50.447 6199-6199/com.pulltorefresh.test E/cexo: scale:0.3
01-20 08:25:50.463 6199-6199/com.pulltorefresh.test E/cexo: scale:0.3125
01-20 08:25:50.479 6199-6199/com.pulltorefresh.test E/cexo: scale:0.33749998
01-20 08:25:50.495 6199-6199/com.pulltorefresh.test E/cexo: scale:0.33749998
01-20 08:25:50.511 6199-6199/com.pulltorefresh.test E/cexo: scale:0.33749998
01-20 08:25:50.527 6199-6199/com.pulltorefresh.test E/cexo: scale:0.35000002
01-20 08:25:50.543 6199-6199/com.pulltorefresh.test E/cexo: scale:0.3625
01-20 08:25:50.563 6199-6199/com.pulltorefresh.test E/cexo: scale:0.3625
01-20 08:25:50.579 6199-6199/com.pulltorefresh.test E/cexo: scale:0.3875
01-20 08:25:50.595 6199-6199/com.pulltorefresh.test E/cexo: scale:0.39999998
01-20 08:25:50.611 6199-6199/com.pulltorefresh.test E/cexo: scale:0.39999998
01-20 08:25:50.627 6199-6199/com.pulltorefresh.test E/cexo: scale:0.41250002
01-20 08:25:50.647 6199-6199/com.pulltorefresh.test E/cexo: scale:0.425
01-20 08:25:50.659 6199-6199/com.pulltorefresh.test E/cexo: scale:0.45
01-20 08:25:50.679 6199-6199/com.pulltorefresh.test E/cexo: scale:0.46249998
01-20 08:25:50.695 6199-6199/com.pulltorefresh.test E/cexo: scale:0.46249998
01-20 08:25:50.711 6199-6199/com.pulltorefresh.test E/cexo: scale:0.47500002
01-20 08:25:50.727 6199-6199/com.pulltorefresh.test E/cexo: scale:0.4875
01-20 08:25:50.743 6199-6199/com.pulltorefresh.test E/cexo: scale:0.4875
01-20 08:25:50.763 6199-6199/com.pulltorefresh.test E/cexo: scale:0.4875
01-20 08:25:50.779 6199-6199/com.pulltorefresh.test E/cexo: scale:0.5
01-20 08:25:50.795 6199-6199/com.pulltorefresh.test E/cexo: scale:0.5125
01-20 08:25:50.811 6199-6199/com.pulltorefresh.test E/cexo: scale:0.525
01-20 08:25:50.827 6199-6199/com.pulltorefresh.test E/cexo: scale:0.5375
01-20 08:25:50.847 6199-6199/com.pulltorefresh.test E/cexo: scale:0.5375
01-20 08:25:50.867 6199-6199/com.pulltorefresh.test E/cexo: scale:0.55
01-20 08:25:50.907 6199-6199/com.pulltorefresh.test E/cexo: scale:0.5625
01-20 08:25:50.931 6199-6199/com.pulltorefresh.test E/cexo: scale:0.5875
01-20 08:25:50.943 6199-6199/com.pulltorefresh.test E/cexo: scale:0.6
01-20 08:25:50.963 6199-6199/com.pulltorefresh.test E/cexo: scale:0.6
01-20 08:25:50.995 6199-6199/com.pulltorefresh.test E/cexo: scale:0.6125
01-20 08:25:51.011 6199-6199/com.pulltorefresh.test E/cexo: scale:0.625
01-20 08:25:51.027 6199-6199/com.pulltorefresh.test E/cexo: scale:0.625
01-20 08:25:51.047 6199-6199/com.pulltorefresh.test E/cexo: scale:0.6375
01-20 08:25:51.063 6199-6199/com.pulltorefresh.test E/cexo: scale:0.65
01-20 08:25:51.079 6199-6199/com.pulltorefresh.test E/cexo: scale:0.6625
01-20 08:25:51.103 6199-6199/com.pulltorefresh.test E/cexo: scale:0.6625
01-20 08:25:51.131 6199-6199/com.pulltorefresh.test E/cexo: scale:0.675
01-20 08:25:51.159 6199-6199/com.pulltorefresh.test E/cexo: scale:0.6875
01-20 08:25:51.179 6199-6199/com.pulltorefresh.test E/cexo: scale:0.6875
01-20 08:25:51.195 6199-6199/com.pulltorefresh.test E/cexo: scale:0.6875
01-20 08:25:51.211 6199-6199/com.pulltorefresh.test E/cexo: scale:0.7125
01-20 08:25:51.227 6199-6199/com.pulltorefresh.test E/cexo: scale:0.725
01-20 08:25:51.243 6199-6199/com.pulltorefresh.test E/cexo: scale:0.725
01-20 08:25:51.263 6199-6199/com.pulltorefresh.test E/cexo: scale:0.7375
01-20 08:25:51.279 6199-6199/com.pulltorefresh.test E/cexo: scale:0.75
01-20 08:25:51.295 6199-6199/com.pulltorefresh.test E/cexo: scale:0.75
01-20 08:25:51.311 6199-6199/com.pulltorefresh.test E/cexo: scale:0.75
01-20 08:25:51.331 6199-6199/com.pulltorefresh.test E/cexo: scale:0.7625
01-20 08:25:51.347 6199-6199/com.pulltorefresh.test E/cexo: scale:0.775
01-20 08:25:51.363 6199-6199/com.pulltorefresh.test E/cexo: scale:0.7875
01-20 08:25:51.379 6199-6199/com.pulltorefresh.test E/cexo: scale:0.7875
01-20 08:25:51.395 6199-6199/com.pulltorefresh.test E/cexo: scale:0.7875
01-20 08:25:51.411 6199-6199/com.pulltorefresh.test E/cexo: scale:0.8
01-20 08:25:51.427 6199-6199/com.pulltorefresh.test E/cexo: scale:0.8125
01-20 08:25:51.443 6199-6199/com.pulltorefresh.test E/cexo: scale:0.8125
01-20 08:25:51.463 6199-6199/com.pulltorefresh.test E/cexo: scale:0.8125
01-20 08:25:51.479 6199-6199/com.pulltorefresh.test E/cexo: scale:0.8375
01-20 08:25:51.495 6199-6199/com.pulltorefresh.test E/cexo: scale:0.8375
01-20 08:25:51.511 6199-6199/com.pulltorefresh.test E/cexo: scale:0.85
01-20 08:25:51.531 6199-6199/com.pulltorefresh.test E/cexo: scale:0.85
01-20 08:25:51.547 6199-6199/com.pulltorefresh.test E/cexo: scale:0.85
01-20 08:25:51.579 6199-6199/com.pulltorefresh.test E/cexo: scale:0.8625
01-20 08:25:51.595 6199-6199/com.pulltorefresh.test E/cexo: scale:0.8625
01-20 08:25:51.631 6199-6199/com.pulltorefresh.test E/cexo: scale:0.875
01-20 08:25:51.647 6199-6199/com.pulltorefresh.test E/cexo: scale:0.875
01-20 08:25:51.659 6199-6199/com.pulltorefresh.test E/cexo: scale:0.875
01-20 08:25:51.679 6199-6199/com.pulltorefresh.test E/cexo: scale:0.875
01-20 08:25:51.695 6199-6199/com.pulltorefresh.test E/cexo: scale:0.9
01-20 08:25:51.715 6199-6199/com.pulltorefresh.test E/cexo: scale:0.9
01-20 08:25:51.727 6199-6199/com.pulltorefresh.test E/cexo: scale:0.9
01-20 08:25:51.747 6199-6199/com.pulltorefresh.test E/cexo: scale:0.9125
01-20 08:25:51.763 6199-6199/com.pulltorefresh.test E/cexo: scale:0.9125
01-20 08:25:51.783 6199-6199/com.pulltorefresh.test E/cexo: scale:0.9125
01-20 08:25:51.795 6199-6199/com.pulltorefresh.test E/cexo: scale:0.925
01-20 08:25:51.811 6199-6199/com.pulltorefresh.test E/cexo: scale:0.925
01-20 08:25:51.827 6199-6199/com.pulltorefresh.test E/cexo: scale:0.925
01-20 08:25:51.847 6199-6199/com.pulltorefresh.test E/cexo: scale:0.925
01-20 08:25:51.859 6199-6199/com.pulltorefresh.test E/cexo: scale:0.9375
01-20 08:25:51.879 6199-6199/com.pulltorefresh.test E/cexo: scale:0.9375
01-20 08:25:51.895 6199-6199/com.pulltorefresh.test E/cexo: scale:0.9375
01-20 08:25:51.911 6199-6199/com.pulltorefresh.test E/cexo: scale:0.9625
01-20 08:25:51.931 6199-6199/com.pulltorefresh.test E/cexo: scale:0.95
01-20 08:25:51.947 6199-6199/com.pulltorefresh.test E/cexo: scale:0.95
01-20 08:25:51.963 6199-6199/com.pulltorefresh.test E/cexo: scale:0.9625
01-20 08:25:51.979 6199-6199/com.pulltorefresh.test E/cexo: scale:0.9625
01-20 08:25:51.995 6199-6199/com.pulltorefresh.test E/cexo: scale:0.9625
01-20 08:25:52.011 6199-6199/com.pulltorefresh.test E/cexo: scale:0.9625
01-20 08:25:52.027 6199-6199/com.pulltorefresh.test E/cexo: scale:0.9625
01-20 08:25:52.043 6199-6199/com.pulltorefresh.test E/cexo: scale:0.9625
01-20 08:25:52.059 6199-6199/com.pulltorefresh.test E/cexo: scale:0.9625
01-20 08:25:52.079 6199-6199/com.pulltorefresh.test E/cexo: scale:0.975
01-20 08:25:52.095 6199-6199/com.pulltorefresh.test E/cexo: scale:0.975
01-20 08:25:52.115 6199-6199/com.pulltorefresh.test E/cexo: scale:0.975
01-20 08:25:52.127 6199-6199/com.pulltorefresh.test E/cexo: scale:0.9875
01-20 08:25:52.159 6199-6199/com.pulltorefresh.test E/cexo: scale:0.9875
01-20 08:25:52.179 6199-6199/com.pulltorefresh.test E/cexo: scale:0.9875

嗯~~如預期,接下來到美團的管理器根據這個縮放的比例來對ImageView進行縮放,直接調用ImageView現成的API處理既可:

編譯運行:

嗯~~完美~~不過還差最後一個細節木有收尾,那就是:

因此我們在這個endRefreshing()方法中作一些善後處理:好比小人動畫正在執行時因爲數據處理速度過快致使動畫還木有執行完就結束了,那得在結束的時候強行將動畫給停掉之類的,因此處理以下:

/**
 * 美團的頭部視圖管理器,分爲三個階段:
 * 一、縮放階段:PULL_DOWN;
 * 二、小人跳出來的階段 RELEASE_REFRESH;
 * 三、小人頭部左右搖晃階段:REFRESHING;
 */
public class MeiTuanSelfHeaderViewManager extends SelfHeaderViewManager {

    /* 下拉狀態View */
    private ImageView iv_meituan_pull_down;
    /* 釋放刷新View */
    private ImageView iv_meituan_release_refreshing;

    /* 向下刷新小人跳出動畫 */
    private AnimationDrawable releaseAnimationDrawable;
    /* 釋放刷新小人搖晃動畫 */
    private AnimationDrawable refreshingAnimationDrawable;

    public MeiTuanSelfHeaderViewManager(Context context) {
        super(context);
    }

    @Override
    protected View getSelfHeaderView() {
        if (selfHeaderView == null) {
            selfHeaderView = View.inflate(context, R.layout.view_refresh_header_meituan, null);
            selfHeaderView.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT));
            iv_meituan_pull_down = selfHeaderView.findViewById(R.id.iv_meituan_pull_down);
            iv_meituan_release_refreshing = selfHeaderView.findViewById(R.id.iv_meituan_release_refreshing);
        }
        return selfHeaderView;
    }

    @Override
    protected void changeToIdle() {
        iv_meituan_pull_down.setVisibility(View.VISIBLE); iv_meituan_release_refreshing.setVisibility(View.INVISIBLE); if (releaseAnimationDrawable != null)
            releaseAnimationDrawable.stop();
        if (refreshingAnimationDrawable != null) refreshingAnimationDrawable.stop();
    }

    @Override
    protected void changeToPullDown() {
        iv_meituan_pull_down.setVisibility(View.VISIBLE);
        iv_meituan_release_refreshing.setVisibility(View.INVISIBLE);
    }

    @Override
    protected void changeToReleaseRefresh() {//小人跳出來的階段
        iv_meituan_pull_down.setVisibility(View.INVISIBLE);
        iv_meituan_release_refreshing.setVisibility(View.VISIBLE);
        iv_meituan_release_refreshing.setImageResource(R.drawable.release_mt_refresh);//設置幀動畫
        releaseAnimationDrawable = (AnimationDrawable) iv_meituan_release_refreshing.getDrawable();
        releaseAnimationDrawable.start();
    }

    @Override
    protected void changeToRefreshing() {//小人頭部左右搖晃階段
        iv_meituan_release_refreshing.setImageResource(R.drawable.refresh_mt_refreshing);
        refreshingAnimationDrawable = (AnimationDrawable) iv_meituan_release_refreshing.getDrawable();
        refreshingAnimationDrawable.start();
    }

    @Override
    protected void endRefreshing() {
        iv_meituan_pull_down.setVisibility(View.VISIBLE);
        iv_meituan_release_refreshing.setVisibility(View.INVISIBLE);
    }

    @Override
    protected void handleScale(float scale) {
        iv_meituan_pull_down.setScaleX(scale);
        iv_meituan_pull_down.setScaleY(scale);
    }
}

至此美團下拉刷新效果就完美實現~ 

相關文章
相關標籤/搜索