Android自定義控件之仿美團(tuán)下拉刷新

作者:阿拉燈神燈
原文地址:http://blog.csdn.net/nugongahou110

美團(tuán)的下拉刷新分為三個(gè)狀態(tài):

  • 第一個(gè)狀態(tài)為下拉刷新狀態(tài)(pull to refresh),在這個(gè)狀態(tài)下是一個(gè)綠色的橢圓隨著下拉的距離動(dòng)態(tài)改變其大小。
  • 第二個(gè)部分為放開刷新狀態(tài)(release to refresh)泰鸡,在這個(gè)狀態(tài)下是一個(gè)幀動(dòng)畫,效果為從躺著變?yōu)檎酒饋淼膭?dòng)畫条摸。
  • 第三個(gè)部分為刷新狀態(tài)(refreshing)沈堡,在這個(gè)狀態(tài)下也是一個(gè)幀動(dòng)畫,是搖頭的動(dòng)畫蓬网。

其中第二和第三個(gè)狀態(tài)很簡單,就是兩個(gè)幀動(dòng)畫鹉勒,第一個(gè)狀態(tài)我們可以用自定義View來實(shí)現(xiàn)帆锋。

第一個(gè)狀態(tài)的實(shí)現(xiàn):

我們的思路是:當(dāng)前這個(gè)橢圓形有一個(gè)進(jìn)度值,這個(gè)進(jìn)度值從0變?yōu)?禽额,然后對這個(gè)橢圓形進(jìn)行縮放锯厢,我們可以使用自定義View來實(shí)現(xiàn)這個(gè)效果,我們先來用一個(gè)SeekBar來模仿一下下拉距離的進(jìn)度 脯倒。

我們解壓美團(tuán)apk后拿到這張圖片:

public class MeiTuanRefreshFirstStepView extends View{

    private Bitmap initialBitmap;
    private int measuredWidth;
    private int measuredHeight;
    private Bitmap endBitmap;
    private float mCurrentProgress;
    private Bitmap scaledBitmap;

    public MeiTuanRefreshFirstStepView(Context context, AttributeSet attrs,
            int defStyle) {
        super(context, attrs, defStyle);
        init(context);
    }

    public MeiTuanRefreshFirstStepView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }

    public MeiTuanRefreshFirstStepView(Context context) {
        super(context);
        init(context);
    }

    private void init(Context context) {
        //這個(gè)就是那個(gè)橢圓形圖片
        initialBitmap = Bitmap.createBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.pull_image));
        //這個(gè)是第二個(gè)狀態(tài)娃娃的圖片实辑,之所以要這張圖片,是因?yàn)榈诙€(gè)狀態(tài)和第三個(gè)狀態(tài)的圖片的大小是一致的藻丢,而第一階段
        //橢圓形圖片的大小與第二階段和第三階段不一致剪撬,因此我們需要根據(jù)這張圖片來決定第一張圖片的寬高,來保證
        //第一階段和第二悠反、三階段的View的寬高一致
        endBitmap = Bitmap.createBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.pull_end_image_frame_05));
    }

    /**
     * 重寫onMeasure方法主要是設(shè)置wrap_content時(shí) View的大小
     * @param widthMeasureSpec
     * @param heightMeasureSpec
     */
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        //根據(jù)設(shè)置的寬度來計(jì)算高度  設(shè)置為符合第二階段娃娃圖片的寬高比例
        setMeasuredDimension(measureWidth(widthMeasureSpec),measureWidth(widthMeasureSpec)*endBitmap.getHeight()/endBitmap.getWidth());
    }

    /**
     * 當(dāng)wrap_content的時(shí)候残黑,寬度即為第二階段娃娃圖片的寬度
     * @param widMeasureSpec
     * @return
     */
    private int measureWidth(int widMeasureSpec){
        int result = 0;
        int size = MeasureSpec.getSize(widMeasureSpec);
        int mode = MeasureSpec.getMode(widMeasureSpec);
        if (mode == MeasureSpec.EXACTLY){
            result = size;
        }else{
            result = endBitmap.getWidth();
            if (mode == MeasureSpec.AT_MOST){
                result = Math.min(result,size);
            }
        }
        return result;
        }

    /**
     * 在onLayout里面獲得測量后View的寬高
     * @param changed
     * @param left
     * @param top
     * @param right
     * @param bottom
     */
    @Override
    protected void onLayout(boolean changed, int left, int top, int right,
            int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        measuredWidth = getMeasuredWidth();
        measuredHeight = getMeasuredHeight();
        //根據(jù)第二階段娃娃寬高  給橢圓形圖片進(jìn)行等比例的縮放
        scaledBitmap = Bitmap.createScaledBitmap(initialBitmap, measuredWidth,measuredWidth*initialBitmap.getHeight()/initialBitmap.getWidth(), true);
    }


    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //這個(gè)方法是對畫布進(jìn)行縮放,從而達(dá)到橢圓形圖片的縮放斋否,第一個(gè)參數(shù)為寬度縮放比例梨水,第二個(gè)參數(shù)為高度縮放比例,
        canvas.scale(mCurrentProgress, mCurrentProgress, measuredWidth/2, measuredHeight/2);
        //將等比例縮放后的橢圓形畫在畫布上面
        canvas.drawBitmap(scaledBitmap,0,measuredHeight/4,null);

    }

    /**
     * 設(shè)置縮放比例茵臭,從0到1  0為最小 1為最大
     * @param currentProgress
     */
    public void setCurrentProgress(float currentProgress){
        mCurrentProgress = currentProgress;
    }

然后在Activity里面:

/**
 * Created by zhangqi on 15/11/1.
 */
public class MyActivity extends Activity {
    private MeiTuanRefreshFirstStepView mFirstView;
    private SeekBar mSeekBar;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_my);
        mSeekBar = (SeekBar) findViewById(R.id.seekbar);
        mFirstView = (MeiTuanRefreshFirstStepView) findViewById(R.id.first_view);
        mSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
            @Override
            public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
                //計(jì)算出當(dāng)前seekBar滑動(dòng)的比例結(jié)果為0到1
                float currentProgress = (float) i / (float) seekBar.getMax();
                //給我們的view設(shè)置當(dāng)前進(jìn)度值
                mFirstView.setCurrentProgress(currentProgress);
                //重畫
                mFirstView.postInvalidate();
            }

            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {

            }

            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {

            }
        });
    }
}

第二個(gè)狀態(tài)的實(shí)現(xiàn):

第二個(gè)狀態(tài)是一個(gè)幀動(dòng)畫疫诽,我們?yōu)榱吮WCView大小的統(tǒng)一,我們也進(jìn)行自定義View旦委,這個(gè)自定義View很簡單奇徒,只是為了和第一階段View的寬高保證一致即可:

public class MeiTuanRefreshSecondStepView extends View{

    private Bitmap endBitmap;

    public MeiTuanRefreshSecondStepView(Context context, AttributeSet attrs,
            int defStyle) {
        super(context, attrs, defStyle);
        init();
    }

    public MeiTuanRefreshSecondStepView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public MeiTuanRefreshSecondStepView(Context context) {
        super(context);
        init();
    }

    private void init() {
        endBitmap = Bitmap.createBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.pull_end_image_frame_05));
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        setMeasuredDimension(measureWidth(widthMeasureSpec), measureWidth(widthMeasureSpec)*endBitmap.getHeight()/endBitmap.getWidth());
    }

    private int measureWidth(int widthMeasureSpec){
        int result = 0;
        int size = MeasureSpec.getSize(widthMeasureSpec);
        int mode = MeasureSpec.getMode(widthMeasureSpec);
        if (mode == MeasureSpec.EXACTLY) {
            result = size;
        }else {
            result = endBitmap.getWidth();
            if (mode == MeasureSpec.AT_MOST) {
                result = Math.min(result, size);
            }
        }
        return result;
    }
}

我們用xml定義一組幀動(dòng)畫:

<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
    android:oneshot="true" >
    <item android:drawable="@drawable/pull_end_image_frame_01" android:duration="100"/>
    <item android:drawable="@drawable/pull_end_image_frame_02" android:duration="100"/>
    <item android:drawable="@drawable/pull_end_image_frame_03" android:duration="100"/>
    <item android:drawable="@drawable/pull_end_image_frame_04" android:duration="100"/>
    <item android:drawable="@drawable/pull_end_image_frame_05" android:duration="100"/>


</animation-list>

幀動(dòng)畫的啟動(dòng)和停止方式:

mSecondView = (MeiTuanRefreshSecondStepView) headerView.findViewById(R.id.second_view);
        mSecondView.setBackgroundResource(R.drawable.pull_to_refresh_second_anim);
        secondAnim = (AnimationDrawable) mSecondView.getBackground();
//啟動(dòng)
secondAnim.start();
//停止
secondAnim.stop();

第三個(gè)狀態(tài)的實(shí)現(xiàn):

和第二個(gè)狀態(tài)同理,我們也通過自定義View來確保三個(gè)狀態(tài)的View的寬高保持一致:

public class MeiTuanRefreshThirdStepView extends View{

    private Bitmap endBitmap;

    public MeiTuanRefreshThirdStepView(Context context, AttributeSet attrs,
            int defStyle) {
        super(context, attrs, defStyle);
        init();
    }

    public MeiTuanRefreshThirdStepView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public MeiTuanRefreshThirdStepView(Context context) {
        super(context);
        init();
    }

    private void init() {
        endBitmap = Bitmap.createBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.pull_end_image_frame_05));
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        setMeasuredDimension(measureWidth(widthMeasureSpec), measureWidth(widthMeasureSpec)*endBitmap.getHeight()/endBitmap.getWidth());
    }

    private int measureWidth(int widthMeasureSpec){
        int result = 0;
        int size = MeasureSpec.getSize(widthMeasureSpec);
        int mode = MeasureSpec.getMode(widthMeasureSpec);
        if (mode == MeasureSpec.EXACTLY) {
            result = size;
        }else {
            result = endBitmap.getWidth();
            if (mode == MeasureSpec.AT_MOST) {
                result = Math.min(result, size);
            }
        }
        return result;
    }

我們在xml中定義一組幀動(dòng)畫:

<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
    android:oneshot="false" >
    <item android:drawable="@drawable/refreshing_image_frame_01" android:duration="100"/>
    <item android:drawable="@drawable/refreshing_image_frame_02" android:duration="100"/>
    <item android:drawable="@drawable/refreshing_image_frame_03" android:duration="100"/>
    <item android:drawable="@drawable/refreshing_image_frame_04" android:duration="100"/>
    <item android:drawable="@drawable/refreshing_image_frame_05" android:duration="100"/>
    <item android:drawable="@drawable/refreshing_image_frame_06" android:duration="100"/>
    <item android:drawable="@drawable/refreshing_image_frame_07" android:duration="100"/>
    <item android:drawable="@drawable/refreshing_image_frame_08" android:duration="100"/>


</animation-list>

幀動(dòng)畫的啟動(dòng)和停止方式和第二個(gè)狀態(tài)的一樣社证。

下拉刷新的實(shí)現(xiàn):

首先我們要定義好幾個(gè)狀態(tài)逼龟,下拉刷新有這樣幾個(gè)狀態(tài):

  • DONE:隱藏的狀態(tài);
  • PULL_TO_REFRESH:下拉刷新的狀態(tài)追葡;
  • RELEASE_TO_REFRESH:松開刷新的狀態(tài)腺律;
  • REFRESHING:正在刷新的狀態(tài)奕短。
/**
 * Created by zhangqi on 15/10/18.
 */
public class MeiTuanListView extends ListView implements AbsListView.OnScrollListener{
    private static final int DONE = 0;
    private static final int PULL_TO_REFRESH = 1;
    private static final int RELEASE_TO_REFRESH = 2;
    private static final int REFRESHING = 3;
    private static final int RATIO = 3;
    private LinearLayout headerView;
    private int headerViewHeight;
    private float startY;
    private float offsetY;
    private TextView tv_pull_to_refresh;
    private OnMeiTuanRefreshListener mOnRefreshListener;
    private int state;
    private int mFirstVisibleItem;
    private boolean isRecord;
    private boolean isEnd;
    private boolean isRefreable;
    private FrameLayout mAnimContainer;
    private Animation animation;
    private SimpleDateFormat format;
    private MeiTuanRefreshFirstStepView mFirstView;
    private MeiTuanRefreshSecondStepView mSecondView;
    private AnimationDrawable secondAnim;
    private MeiTuanRefreshThirdStepView mThirdView;
    private AnimationDrawable thirdAnim;

    public MeiTuanListView(Context context) {
        super(context);
        init(context);
    }

    public MeiTuanListView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }

    public MeiTuanListView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context);
    }

    public interface OnMeiTuanRefreshListener{
        void onRefresh();
    }

    /**
     * 回調(diào)接口,想實(shí)現(xiàn)下拉刷新的listview實(shí)現(xiàn)此接口
     * @param onRefreshListener
     */
    public void setOnMeiTuanRefreshListener(OnMeiTuanRefreshListener onRefreshListener){
        mOnRefreshListener = onRefreshListener;
        isRefreable = true;
    }

    /**
     * 刷新完畢匀钧,從主線程發(fā)送過來翎碑,并且改變headerView的狀態(tài)和文字動(dòng)畫信息
     */
    public void setOnRefreshComplete(){
        //一定要將isEnd設(shè)置為true,以便于下次的下拉刷新
        isEnd = true;
        state = DONE;

        changeHeaderByState(state);
    }

    private void init(Context context) {
        setOverScrollMode(View.OVER_SCROLL_NEVER);
        setOnScrollListener(this);

        headerView = (LinearLayout) LayoutInflater.from(context).inflate(R.layout.meituan_item, null, false);
        mFirstView = (MeiTuanRefreshFirstStepView) headerView.findViewById(R.id.first_view);
        tv_pull_to_refresh = (TextView) headerView.findViewById(R.id.tv_pull_to_refresh);
        mSecondView = (MeiTuanRefreshSecondStepView) headerView.findViewById(R.id.second_view);
        mSecondView.setBackgroundResource(R.drawable.pull_to_refresh_second_anim);
        secondAnim = (AnimationDrawable) mSecondView.getBackground();
        mThirdView = (MeiTuanRefreshThirdStepView) headerView.findViewById(R.id.third_view);
        mThirdView.setBackgroundResource(R.drawable.pull_to_refresh_third_anim);
        thirdAnim = (AnimationDrawable) mThirdView.getBackground();

        measureView(headerView);
        addHeaderView(headerView);
        headerViewHeight = headerView.getMeasuredHeight();
        headerView.setPadding(0, -headerViewHeight, 0, 0);
        Log.i("zhangqi","headerViewHeight="+headerViewHeight);

        state = DONE;
        isEnd = true;
        isRefreable = false;
    }




    @Override
    public void onScrollStateChanged(AbsListView absListView, int i) {
    }
    @Override
    public void onScroll(AbsListView absListView, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
        mFirstVisibleItem = firstVisibleItem;
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        if (isEnd) {//如果現(xiàn)在時(shí)結(jié)束的狀態(tài)之斯,即刷新完畢了日杈,可以再次刷新了,在onRefreshComplete中設(shè)置
            if (isRefreable) {//如果現(xiàn)在是可刷新狀態(tài)   在setOnMeiTuanListener中設(shè)置為true
                switch (ev.getAction()){
                    //用戶按下
                case MotionEvent.ACTION_DOWN:
                    //如果當(dāng)前是在listview頂部并且沒有記錄y坐標(biāo)
                    if (mFirstVisibleItem == 0 && !isRecord) {
                        //將isRecord置為true佑刷,說明現(xiàn)在已記錄y坐標(biāo)
                        isRecord = true;
                        //將當(dāng)前y坐標(biāo)賦值給startY起始y坐標(biāo)
                        startY = ev.getY();
                    }
                    break;
                //用戶滑動(dòng)
                case MotionEvent.ACTION_MOVE:
                    //再次得到y(tǒng)坐標(biāo)莉擒,用來和startY相減來計(jì)算offsetY位移值
                    float tempY = ev.getY();
                    //再起判斷一下是否為listview頂部并且沒有記錄y坐標(biāo)
                    if (mFirstVisibleItem == 0 && !isRecord) {
                        isRecord = true;
                        startY = tempY;
                    }
                    //如果當(dāng)前狀態(tài)不是正在刷新的狀態(tài),并且已經(jīng)記錄了y坐標(biāo)
                    if (state!=REFRESHING && isRecord ) {
                        //計(jì)算y的偏移量
                        offsetY = tempY - startY;
                        //計(jì)算當(dāng)前滑動(dòng)的高度
                        float currentHeight = (-headerViewHeight+offsetY/3);
                        //用當(dāng)前滑動(dòng)的高度和頭部headerView的總高度進(jìn)行比 計(jì)算出當(dāng)前滑動(dòng)的百分比 0到1
                        float currentProgress = 1+currentHeight/headerViewHeight;
                        //如果當(dāng)前百分比大于1了瘫絮,將其設(shè)置為1涨冀,目的是讓第一個(gè)狀態(tài)的橢圓不再繼續(xù)變大
                        if (currentProgress>=1) {
                            currentProgress = 1;
                        }
                        //如果當(dāng)前的狀態(tài)是放開刷新,并且已經(jīng)記錄y坐標(biāo)
                        if (state == RELEASE_TO_REFRESH && isRecord) {
                            setSelection(0);
                            //如果當(dāng)前滑動(dòng)的距離小于headerView的總高度
                            if (-headerViewHeight+offsetY/RATIO<0) {
                                //將狀態(tài)置為下拉刷新狀態(tài)
                                state = PULL_TO_REFRESH;
                                //根據(jù)狀態(tài)改變headerView麦萤,主要是更新動(dòng)畫和文字等信息
                                changeHeaderByState(state);
                                //如果當(dāng)前y的位移值小于0鹿鳖,即為headerView隱藏了
                            }else if (offsetY<=0) {
                                //將狀態(tài)變?yōu)閐one
                                state = DONE;
                                //根據(jù)狀態(tài)改變headerView,主要是更新動(dòng)畫和文字等信息
                                changeHeaderByState(state);
                            }
                        }
                        //如果當(dāng)前狀態(tài)為下拉刷新并且已經(jīng)記錄y坐標(biāo)
                        if (state == PULL_TO_REFRESH && isRecord) {
                            setSelection(0);
                            //如果下拉距離大于等于headerView的總高度
                            if (-headerViewHeight+offsetY/RATIO>=0) {
                                //將狀態(tài)變?yōu)榉砰_刷新
                                state = RELEASE_TO_REFRESH;
                                //根據(jù)狀態(tài)改變headerView壮莹,主要是更新動(dòng)畫和文字等信息
                                changeHeaderByState(state);
                                //如果當(dāng)前y的位移值小于0翅帜,即為headerView隱藏了
                            }else if (offsetY<=0) {
                                //將狀態(tài)變?yōu)閐one
                                state = DONE;
                                //根據(jù)狀態(tài)改變headerView,主要是更新動(dòng)畫和文字等信息
                                changeHeaderByState(state);
                            }
                        }
                        //如果當(dāng)前狀態(tài)為done并且已經(jīng)記錄y坐標(biāo)
                        if (state == DONE && isRecord) {
                            //如果位移值大于0
                            if (offsetY>=0) {
                                //將狀態(tài)改為下拉刷新狀態(tài)
                                state = PULL_TO_REFRESH;
                            }
                        }
                        //如果為下拉刷新狀態(tài)
                        if (state == PULL_TO_REFRESH) {
                            //則改變headerView的padding來實(shí)現(xiàn)下拉的效果
                            headerView.setPadding(0,(int)(-headerViewHeight+offsetY/RATIO) ,0,0);
                            //給第一個(gè)狀態(tài)的View設(shè)置當(dāng)前進(jìn)度值
                            mFirstView.setCurrentProgress(currentProgress);
                            //重畫
                            mFirstView.postInvalidate();
                        }
                        //如果為放開刷新狀態(tài)
                        if (state == RELEASE_TO_REFRESH) {
                            //改變headerView的padding值
                            headerView.setPadding(0,(int)(-headerViewHeight+offsetY/RATIO) ,0, 0);
                            //給第一個(gè)狀態(tài)的View設(shè)置當(dāng)前進(jìn)度值
                            mFirstView.setCurrentProgress(currentProgress);
                            //重畫
                            mFirstView.postInvalidate();
                        }
                    }
                    break;
                //當(dāng)用戶手指抬起時(shí)
                case MotionEvent.ACTION_UP:
                    //如果當(dāng)前狀態(tài)為下拉刷新狀態(tài)
                    if (state == PULL_TO_REFRESH) {
                        //平滑的隱藏headerView
                        this.smoothScrollBy((int)(-headerViewHeight+offsetY/RATIO)+headerViewHeight, 500);
                        //根據(jù)狀態(tài)改變headerView
                        changeHeaderByState(state);
                    }
                    //如果當(dāng)前狀態(tài)為放開刷新
                    if (state == RELEASE_TO_REFRESH) {
                        //平滑的滑到正好顯示headerView
                        this.smoothScrollBy((int)(-headerViewHeight+offsetY/RATIO), 500);
                        //將當(dāng)前狀態(tài)設(shè)置為正在刷新
                        state = REFRESHING;
                        //回調(diào)接口的onRefresh方法
                        mOnRefreshListener.onRefresh();
                        //根據(jù)狀態(tài)改變headerView
                        changeHeaderByState(state);
                    }
                    //這一套手勢執(zhí)行完命满,一定別忘了將記錄y坐標(biāo)的isRecord改為false涝滴,以便于下一次手勢的執(zhí)行
                    isRecord = false;
                    break;
                }

            }
        }
        return super.onTouchEvent(ev);
    }

    /**
     * 根據(jù)狀態(tài)改變headerView的動(dòng)畫和文字顯示
     * @param state
     */
    private void changeHeaderByState(int state){
        switch (state) {
        case DONE://如果的隱藏的狀態(tài)
            //設(shè)置headerView的padding為隱藏
            headerView.setPadding(0, -headerViewHeight, 0, 0);
            //第一狀態(tài)的view顯示出來
            mFirstView.setVisibility(View.VISIBLE);
            //第二狀態(tài)的view隱藏起來
            mSecondView.setVisibility(View.GONE);
            //停止第二狀態(tài)的動(dòng)畫
            secondAnim.stop();
            //第三狀態(tài)的view隱藏起來
            mThirdView.setVisibility(View.GONE);
            //停止第三狀態(tài)的動(dòng)畫
            thirdAnim.stop();
            break;
        case RELEASE_TO_REFRESH://當(dāng)前狀態(tài)為放開刷新
            //文字顯示為放開刷新
            tv_pull_to_refresh.setText("放開刷新");
            //第一狀態(tài)view隱藏起來
            mFirstView.setVisibility(View.GONE);
            //第二狀態(tài)view顯示出來
            mSecondView.setVisibility(View.VISIBLE);
            //播放第二狀態(tài)的動(dòng)畫
            secondAnim.start();
            //第三狀態(tài)view隱藏起來
            mThirdView.setVisibility(View.GONE);
            //停止第三狀態(tài)的動(dòng)畫
            thirdAnim.stop();
            break;
        case PULL_TO_REFRESH://當(dāng)前狀態(tài)為下拉刷新
            //設(shè)置文字為下拉刷新
            tv_pull_to_refresh.setText("下拉刷新");
            //第一狀態(tài)view顯示出來
            mFirstView.setVisibility(View.VISIBLE);
            //第二狀態(tài)view隱藏起來
            mSecondView.setVisibility(View.GONE);
            //第二狀態(tài)動(dòng)畫停止
            secondAnim.stop();
            //第三狀態(tài)view隱藏起來
            mThirdView.setVisibility(View.GONE);
            //第三狀態(tài)動(dòng)畫停止
            thirdAnim.stop();
            break;
        case REFRESHING://當(dāng)前狀態(tài)為正在刷新
            //文字設(shè)置為正在刷新
            tv_pull_to_refresh.setText("正在刷新");
            //第一狀態(tài)view隱藏起來
            mFirstView.setVisibility(View.GONE);
            //第三狀態(tài)view顯示出來
            mThirdView.setVisibility(View.VISIBLE);
            //第二狀態(tài)view隱藏起來
            mSecondView.setVisibility(View.GONE);
            //停止第二狀態(tài)動(dòng)畫
            secondAnim.stop();
            //啟動(dòng)第三狀態(tài)view
            thirdAnim.start();
            break;
        default:
            break;
        }
    }


    private void measureView(View child) {
        ViewGroup.LayoutParams p = child.getLayoutParams();
        if (p == null) {
            p = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                    ViewGroup.LayoutParams.WRAP_CONTENT);
        }
        int childWidthSpec = ViewGroup.getChildMeasureSpec(0, 0 + 0, p.width);
        int lpHeight = p.height;
        int childHeightSpec;
        if (lpHeight > 0) {
            childHeightSpec = MeasureSpec.makeMeasureSpec(lpHeight,
                    MeasureSpec.EXACTLY);
        } else {
            childHeightSpec = MeasureSpec.makeMeasureSpec(0,
                    MeasureSpec.UNSPECIFIED);
        }
        child.measure(childWidthSpec, childHeightSpec);
    }


}

一切準(zhǔn)備就緒,在Activity中使用:

public class MainActivity extends Activity implements OnMeiTuanRefreshListener{
    private MeiTuanListView mListView;
    private List<String> mDatas;
    private ArrayAdapter<String> mAdapter;
    private final static int REFRESH_COMPLETE = 0;
    /**
     * mHandler運(yùn)行在主線程周荐,因?yàn)閟etOnRefreshComplete需要改變ui狭莱,必須在主線程去改變ui
     * 所以在handleMessage中調(diào)用mListView.setOnRefreshComplete();
     */
    private Handler mHandler = new Handler(){
        public void handleMessage(android.os.Message msg) {
            switch (msg.what) {
            case REFRESH_COMPLETE:
                mListView.setOnRefreshComplete();
                mAdapter.notifyDataSetChanged();
                mListView.setSelection(0);
                break;

            default:
                break;
            }
        };
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mListView = (MeiTuanListView) findViewById(R.id.listview);
        String[] data = new String[]{"hello world","hello world","hello world","hello world",
                "hello world","hello world","hello world","hello world","hello world",
                "hello world","hello world","hello world","hello world","hello world",};
        mDatas = new ArrayList<String>(Arrays.asList(data));
        mAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1,mDatas);
        mListView.setAdapter(mAdapter);
        mListView.setOnMeiTuanRefreshListener(this);
    }

    @Override
    public void onRefresh() {
        new Thread(new Runnable() {

            @Override
            public void run() {
                try {
                    Thread.sleep(3000);
                    mDatas.add(0, "new data");
                    mHandler.sendEmptyMessage(REFRESH_COMPLETE);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }).start();
    }


}

完整代碼

完整代碼大家可以上我的GitHub下載僵娃,如果大家覺得還可以就star一下~哈哈概作。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市默怨,隨后出現(xiàn)的幾起案子讯榕,更是在濱河造成了極大的恐慌,老刑警劉巖匙睹,帶你破解...
    沈念sama閱讀 216,744評論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件愚屁,死亡現(xiàn)場離奇詭異,居然都是意外死亡痕檬,警方通過查閱死者的電腦和手機(jī)霎槐,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,505評論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來梦谜,“玉大人丘跌,你說我怎么就攤上這事袭景。” “怎么了闭树?”我有些...
    開封第一講書人閱讀 163,105評論 0 353
  • 文/不壞的土叔 我叫張陵耸棒,是天一觀的道長。 經(jīng)常有香客問我报辱,道長与殃,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,242評論 1 292
  • 正文 為了忘掉前任碍现,我火速辦了婚禮幅疼,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘昼接。我一直安慰自己衣屏,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,269評論 6 389
  • 文/花漫 我一把揭開白布辩棒。 她就那樣靜靜地躺著狼忱,像睡著了一般。 火紅的嫁衣襯著肌膚如雪一睁。 梳的紋絲不亂的頭發(fā)上钻弄,一...
    開封第一講書人閱讀 51,215評論 1 299
  • 那天,我揣著相機(jī)與錄音者吁,去河邊找鬼窘俺。 笑死,一個(gè)胖子當(dāng)著我的面吹牛复凳,可吹牛的內(nèi)容都是我干的瘤泪。 我是一名探鬼主播,決...
    沈念sama閱讀 40,096評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼育八,長吁一口氣:“原來是場噩夢啊……” “哼对途!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起髓棋,我...
    開封第一講書人閱讀 38,939評論 0 274
  • 序言:老撾萬榮一對情侶失蹤实檀,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后按声,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體膳犹,經(jīng)...
    沈念sama閱讀 45,354評論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,573評論 2 333
  • 正文 我和宋清朗相戀三年签则,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了须床。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,745評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡渐裂,死狀恐怖豺旬,靈堂內(nèi)的尸體忽然破棺而出余赢,到底是詐尸還是另有隱情,我是刑警寧澤哈垢,帶...
    沈念sama閱讀 35,448評論 5 344
  • 正文 年R本政府宣布妻柒,位于F島的核電站,受9級特大地震影響耘分,放射性物質(zhì)發(fā)生泄漏举塔。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,048評論 3 327
  • 文/蒙蒙 一求泰、第九天 我趴在偏房一處隱蔽的房頂上張望央渣。 院中可真熱鬧,春花似錦渴频、人聲如沸芽丹。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,683評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽拔第。三九已至,卻和暖如春场钉,著一層夾襖步出監(jiān)牢的瞬間蚊俺,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,838評論 1 269
  • 我被黑心中介騙來泰國打工逛万, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留泳猬,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,776評論 2 369
  • 正文 我出身青樓宇植,卻偏偏與公主長得像得封,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子指郁,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,652評論 2 354

推薦閱讀更多精彩內(nèi)容