支持listview、GridView、普通布局、scrollview和viewpager的下拉刷新控件

自定义下拉刷新组件
本文介绍了一款适用于ListView、GridView、ScrollView和ViewPager的自定义下拉刷新组件,支持多种视图的刷新效果,并提供了详细的使用说明及源码。

支持listview、GridView、scrollview和viewpager的下拉刷新(经测试未发现bug),但是不支持viewpager中嵌套listview、GridView等可上下滑动组件的情况,本来想作支持的,但是鉴于viewpager的嵌套的可能性太过复杂,最后还是放弃了。以下为scrollview刷新效果图


用法如下:

1.布局文件如下,只需将需要刷新效果的控件包裹在该控件中即可,注意只能有一个子控件,如果使用普通布局的情况,可使用布局进行包裹。

<com.zp.scrolltest.RefreshView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/refresh_view">



        <!--<GridView-->
            <!--android:layout_width="match_parent"-->
            <!--android:layout_height="match_parent"-->
            <!--android:numColumns="1"-->
            <!--android:visibility="gone"-->
            <!--android:id="@+id/grid_view"/>-->

        <!--<ListView-->
            <!--android:layout_width="match_parent"-->
            <!--android:layout_height="match_parent"-->
            <!--android:background="#ff9900"-->
            <!--android:id="@+id/list_view"-->
            <!--android:visibility="gone"-->
            <!--android:text="content"/>-->

        <ScrollView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="#00ff00">

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

                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="300dp"
                    android:text="hhhhhhh"/>

                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="300dp"
                    android:text="hhhhhhh"/>

                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="300dp"
                    android:text="hhhhhhh"/>

                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="300dp"
                    android:text="hhhhhhh"/>

            </LinearLayout>

        </ScrollView>

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

            <!--<TextView-->
                <!--android:layout_width="wrap_content"-->
                <!--android:layout_height="300dp"-->
                <!--android:text="hhhhhhh"/>-->

        <!--</LinearLayout>-->

    </com.zp.scrolltest.RefreshView>

Java代码中使用:

refresh_view = (RefreshView)findViewById(R.id.refresh_view);
        refresh_view.setOnRefshing(new RefreshView.OnRefshing() {
            @Override
            public void refresh() {
//                Toast.makeText(MainActivity.this, "hahah you are me", Toast.LENGTH_SHORT).show();
            }
        });
        refresh_view.setTime("2017-1-9 12:12:56");

可对刷新事件进行监听,可设置刷新时间(如果不设置则不显示该效果),可手动触发刷新(startRefresh())和停止刷新效果(stopRefresh())。


源码如下:

package com.zp.scrolltest;

import android.content.Context;
import android.os.Handler;
import android.os.Message;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.GridView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.ScrollView;
import android.widget.Scroller;
import android.widget.TextView;

import java.util.Calendar;

/**
 * Created by ez on 2017/5/8.
 */

public class RefreshView extends LinearLayout {

    TextView status_tv, refresh_time;
    ProgressBar progressBar;
    Context context;
    float lastY;
    int SCREEN_HEIGHT;
    Scroller scroller;
    int mHeight = 200;
    public static final int PREPARE = 0;
    public static final int WILLREFRESH = 1;
    public static final int REFRESHING = 2;
    public static final int END = 3;
    private int status;
    private int delayTime = 2000;
    boolean inControl;//临界点过度控制,由listview或者其他部分下拉到顶,触发滑动效果时用到
    OnRefshing onRefshing;
    boolean isTopHidden;

    Handler handler = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            return false;
        }
    });

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

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

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

    private void init(Context context){
        this.context = context;
        SCREEN_HEIGHT = context.getResources().getDisplayMetrics().heightPixels;
        setOrientation(VERTICAL);

        LinearLayout ll = new LinearLayout(context);
        ll.setOrientation(HORIZONTAL);
        ll.setLayoutParams(new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, mHeight));
        ll.setGravity(Gravity.CENTER);
        progressBar = new ProgressBar(context);
        progressBar.setVisibility(View.GONE);
        ll.addView(progressBar);

        LinearLayout linearLayout = new LinearLayout(context);
        linearLayout.setOrientation(VERTICAL);
        linearLayout.setLayoutParams(new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
        status_tv = new TextView(context);
        LayoutParams param = new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
        param.setMargins(20, 0, 30, 0);
        status_tv.setLayoutParams(param);
        status_tv.setText("下拉刷新");
        status_tv.setTextSize(16);
        status_tv.setGravity(Gravity.CENTER);
        linearLayout.addView(status_tv);
        refresh_time = new TextView(context);
        refresh_time.setLayoutParams(param);

        refresh_time.setText("刷新时间:");
        refresh_time.setTextSize(12);
        refresh_time.setVisibility(View.GONE);
        linearLayout.addView(refresh_time);
        ll.addView(linearLayout);
        addView(ll);

        scrollTo(0, mHeight);
        scroller = new Scroller(context);
        status = PREPARE;
    }

    public void setDelayTime(int time){
        this.delayTime = time;
    }

    public void setOnRefshing(OnRefshing onRefshing){
        this.onRefshing = onRefshing;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        ViewGroup.LayoutParams params = getChildAt(1).getLayoutParams();
        params.height = getMeasuredHeight();
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                lastY = ev.getY();
                break;

            case MotionEvent.ACTION_MOVE:
                View view = getChildAt(1);
                float dy = ev.getY()-lastY;
                Log.e("zp", "坐标:"+ev.getY());
                //作用于下面的组件滑动到最顶端,重新分发事件响应刷新动作
                if(dy>0){
                    if(view instanceof ListView){
                        ListView lv = (ListView)view;
                        View c = lv.getChildAt(lv.getFirstVisiblePosition());
                        if(c!=null && c.getTop()==0 && !inControl){
                            inControl = true;
                            ev.setAction(MotionEvent.ACTION_CANCEL);
                            MotionEvent ev2 = MotionEvent.obtain(ev);
                            dispatchTouchEvent(ev);
                            ev2.setAction(MotionEvent.ACTION_DOWN);
                            return dispatchTouchEvent(ev2);
                        }
                    }else if(view instanceof ScrollView){
                        ScrollView sv = (ScrollView)view;
                        if(sv.getScrollY()==0 && !inControl){
                            inControl = true;
                            ev.setAction(MotionEvent.ACTION_CANCEL);
                            MotionEvent ev2 = MotionEvent.obtain(ev);
                            dispatchTouchEvent(ev);
                            ev2.setAction(MotionEvent.ACTION_DOWN);
                            return dispatchTouchEvent(ev2);
                        }
                    }else if(view instanceof GridView){
                        GridView gv = (GridView)view;
                        View c = gv.getChildAt(gv.getFirstVisiblePosition());
                        if(c!=null && c.getTop()==0 && !inControl){
                            inControl = true;
                            ev.setAction(MotionEvent.ACTION_CANCEL);
                            MotionEvent ev2 = MotionEvent.obtain(ev);
                            dispatchTouchEvent(ev);
                            ev2.setAction(MotionEvent.ACTION_DOWN);
                            return dispatchTouchEvent(ev2);
                        }
                    }
                }
                break;
        }
        return super.dispatchTouchEvent(ev);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        switch (ev.getAction()){
            case MotionEvent.ACTION_DOWN:
                break;

            case MotionEvent.ACTION_MOVE:
                View view = getChildAt(1);
                float dy = ev.getY()-lastY;
                Log.e("zp", "坐标1:"+ev.getY());
                //下拉且下面组件为顶端时,阻断事件向下层传递,直接触发本身的touch事件
                if(dy>0){
                    if(view instanceof ListView){
                        ListView lv = (ListView)view;
                        View c = lv.getChildAt(lv.getFirstVisiblePosition());
                        if(c!=null && c.getTop()==0 && inControl){
                            return true;
                        }
                    }else if(view instanceof ScrollView){
                        ScrollView sv = (ScrollView)view;
                        if(sv.getScrollY()==0  && inControl){
                            return true;
                        }
                    }else if(view instanceof GridView){
                        GridView gv = (GridView)view;
                        View c = gv.getChildAt(gv.getFirstVisiblePosition());
                        if(c!=null && c.getTop()==0 && inControl){
                            return true;
                        }
                    }else if(view instanceof ViewPager){
                        return true;
                    }
                }
                break;
        }

        return super.onInterceptTouchEvent(ev);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()){
            case MotionEvent.ACTION_DOWN:
                status = PREPARE;
                return true;

            case MotionEvent.ACTION_MOVE:
                //加入判断,防止多次刷新,同时也不影响界面的滑动
                if(status!=REFRESHING){
                    float disY = event.getY()-lastY;
                    lastY = event.getY();
                    Log.e("zp", "坐标2:"+event.getY());
                    int ty = getScrollY();

                    scrollBy(0, -(int)disY/2);
                    if(getScrollY()<10){
                        status_tv.setText("松开手刷新");
                        status = WILLREFRESH;
                    }else{
                        status_tv.setText("下拉刷新");
                        status = PREPARE;
                    }

                    //作用于刷新动作上滑取消,重新分发事件,响应下面组件的滑动效果
                    if(disY<0 && mHeight == getScrollY()){
                        event.setAction(MotionEvent.ACTION_DOWN);
                        dispatchTouchEvent(event);
                        inControl = false;
                    }
                }
                break;

            case MotionEvent.ACTION_UP:
                if(status==PREPARE){
                    scroller.startScroll(0, getScrollY(), 0, mHeight-getScrollY(), 200);
                    status_tv.setText("下拉刷新");
                    status = END;
                }
                else if(status == WILLREFRESH){
                    status = REFRESHING;
                    scroller.startScroll(0, getScrollY(), 0, -getScrollY(), 200);
                    progressBar.setVisibility(View.VISIBLE);
                    status_tv.setText("正在刷新...");
                    if(onRefshing!=null){
                        onRefshing.refresh();
                    }
                    handler.postDelayed(new Runnable() {
                        @Override
                        public void run() {
                            stopRefresh();
                        }
                    }, delayTime);
                }
                inControl = false;
                break;

            case MotionEvent.ACTION_CANCEL:
                if(scroller.isFinished()){
                    scroller.abortAnimation();
                }
                break;
        }
        return super.onTouchEvent(event);
    }

    @Override
    public void computeScroll() {
        if (scroller.computeScrollOffset()) {
            scrollTo(0, scroller.getCurrY());
        }
        invalidate();
    }

    /**
     * scrollBy最终还是依靠scrollTo实现,这里的条件可以屏蔽上滑导致整个页面往上走,下面出现空白
     * 便于处理事件的重新分发过程
     * @param x
     * @param y
     */
    @Override
    public void scrollTo(int x, int y) {

        if (y > mHeight) {
            y = mHeight;
        }
        if (y != getScrollY()) {
            super.scrollTo(x, y);
        }

        isTopHidden = getScrollY() == mHeight;

    }

    private String getTime(){
        Calendar calendar=Calendar.getInstance();
        return "上次刷新:"+calendar.get(Calendar.MONTH)+"-"+calendar.get(Calendar.DAY_OF_MONTH)+" "+calendar.get(Calendar.HOUR_OF_DAY)+":"+calendar.get(Calendar.MINUTE)+":"+calendar.get(Calendar.SECOND);
    }

    public void setTime(String time){
        refresh_time.setVisibility(View.VISIBLE );
        refresh_time.setText(time);
    }

    public void startRefresh(){
        if(status!=REFRESHING){
            status = REFRESHING;
            scroller.startScroll(0, getScrollY(), 0, -getScrollY(), 200);
            status_tv.setText("正在刷新...");
            progressBar.setVisibility(View.VISIBLE);
            if(onRefshing!=null){
                onRefshing.refresh();
            }
            handler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    stopRefresh();
                }
            }, delayTime);
        }
    }

    public void stopRefresh(){
        if(status==REFRESHING){
            progressBar.setVisibility(View.GONE);
            scroller.startScroll(0, getScrollY(), 0, mHeight-getScrollY(), 200);
            status_tv.setText("下拉刷新");
            status = END;
            handler.removeCallbacksAndMessages(null);
        }
    }

    public interface OnRefshing{
        void refresh();
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值