自定义HomeScrollview,增加下拉刷新,上拉加载更多功能

没有废话,直接撸代码~

首先是自定义控件:

/**
 * Created by HXY on 2018/9/25.
 * Be used for : 自定义首页Scrollview主布局,添加下拉刷新子Recycleview,滑动事件监听
 */

public class HomeScrollview extends ScrollView {

    private int down_y; //按下时候的y坐标
    private int scroll_y;  //ScrollView的滑动距离
    private View headViewRefresh;  //头布局
    private RefreshListener listsner;  //刷新加载数据监听
    private boolean b_down; //是否可以刷新
    private int viewWidth;  //scrollView宽度
    private int headViewHeight;  //头布局刷新时的高度


    public HomeScrollview(Context context) {
        super(context,null);
    }

    public HomeScrollview(Context context, AttributeSet attrs) {
        super(context, attrs,0);
    }

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

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        viewWidth = getWidth(); //获取ScrollView的宽度用来设置头布局的宽度
    }

    /**
     * 设置刷新头布局
     * @param view
     */
    public void setHeadView(View view){  //在引用此自定义ScrollView的activity中传入初始化完成的头布局文件
        this.headViewRefresh = view;
        this.headViewHeight = UIUtil.dip2px(getContext(),50); //dp转px,px转dp工具,下文给出
        LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) headViewRefresh.getLayoutParams();
        params.width = viewWidth;
        params.height = 0;
        headViewRefresh.setLayoutParams(params); //将headView的高度重新设置为0,也就是不可见,为什么这么设置?下文会介绍
    }

    /**
     * 提供给调用scrollView的页面的刷新加载回调方法
     * @param listsner
     */
    public void setRefreshListener(RefreshListener listsner){ //回调接口下文给出
        this.listsner = listsner;
    }

    @Override
    protected void onScrollChanged(int scrollX, int scrollY, int oldScrollX, int oldScrollY) {
        super.onScrollChanged(scrollX, scrollY, oldScrollX, oldScrollY);
        scroll_y = scrollY;  //监听赋值,监听scrollView的滑动状态,当滑动到顶部的时候才可以下拉刷新
        if(scrollY == 0){

        }else if(scrollY+this.getMeasuredHeight() == this.getChildAt(0).getMeasuredHeight()){  //滑动距离+scrollView的高度如果等于scrollView的内部子view的高度则证明滑动到了底部,则自动加载更多数据
            listsner.loadMore();  //加载更多
        }
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent event) { //重写dispatchTouchEvent,敲黑板,这是重点!
        if(event.getAction() == MotionEvent.ACTION_DOWN){   //获取手指初次触摸位置
            down_y = (int) event.getY();  //记录下手指点下的纵坐标
        }
        if(event.getAction() == MotionEvent.ACTION_MOVE){   //滑动事件
            if(scroll_y == 0){   //如果scroll_y == 0,在顶部,可以刷新
                if(event.getY() - down_y > 0){  //手势判断:向下滑动,可以刷新
                    //event.getY()-down_y是手指滑动的纵向距离,为什么乘1/3?为了让下拉刷新更肉一点,这样手指下滑300像素,头布局高度增高100像素,可根据个人喜好做出调整
                    int downRange = (int) ((event.getY()-down_y)*1/3);   //给headView动态设置高度,动态高度是手指向下滑动距离的1/3
//                    headViewRefresh.setVisibility(View.VISIBLE);  //显示刷新的根视图  显示headView控件
                    b_down = false;    //刚开始滑动,松手还不可以刷新
                    LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) headViewRefresh.getLayoutParams();  //将滑动距离转化后的值用来给headView动态设置高度
                    params.width = viewWidth;
                    params.height = downRange;
                    headViewRefresh.setLayoutParams(params);
                    if(downRange >= headViewHeight){   //当动态设置的高度大于初始高度的时候,变换hint,此时松手可刷新;headViewHeight是我设置的初始高度50dp,也用来判断下拉到什么程度才能刷新
                        listsner.hintChange("松开刷新");//超过了设定的高度,可以刷新,如果不设置这个或者设置的值太小,轻轻一拉就刷新,体验不好
                        b_down = true; //可以刷新,如果此时抬起手指就可以刷新了
                    }else{     //当动态设置的高度不大于初始高度的时候,变换hint,此时松手不可刷新
                        listsner.hintChange("下拉刷新");
                        b_down = false; //不可以刷新
                    }
                    listsner.setWidthX((int)event.getX()); //设置触摸点的横坐标,用来优化头布局效果,不是非必须的
                    return true;   //拦截触摸事件,scrollView不可响应触摸事件,否则会造成松手滑动跳动错位
                }else{ //手势判断:小于0则是上滑,此时按正常程序走
                    b_down = false; //不可以刷新
                    return super.dispatchTouchEvent(event);  //向上滑动,不拦截
                }
            }else{ //scroll_y不等于0则是上滑,此时按正常程序走
                b_down = false; //不可以刷新
                return super.dispatchTouchEvent(event);   //scrollView不在顶部,不拦截
            }
        }
        if(event.getAction() == MotionEvent.ACTION_UP){   //抬起手指
            if(b_down){    //如果可以刷新
                LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) headViewRefresh.getLayoutParams();   //设置headView为原始高度
                params.width = viewWidth;
                params.height = headViewHeight;
                headViewRefresh.setLayoutParams(params);
                listsner.hintChange("正在刷新");
                listsner.startRefresh();
            }else{    //如果不可以刷新,停止刷新
                stopRefresh();
            }
        }
        return super.dispatchTouchEvent(event);
    }

    /**
     * 刷新停止,给scrollView外部调用
     */
    public void stopRefresh() {
        listsner.hintChange("下拉刷新");  //停止刷新之后,将提示文字设置成初始值,时刻准备着下次刷新
//        headViewRefresh.setVisibility(View.GONE);  //隐藏headView
        LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) headViewRefresh.getLayoutParams();  //将headView的高度重新设置为1
        params.width = viewWidth;
        params.height = 0;
        headViewRefresh.setLayoutParams(params); //设置头布局的高度为0,也就是隐藏头布局
    }
}

以上是我们的自定义ScrollView ,其中涉及到两个类:

/**
 * Created by HXY on 2018/9/25.
 * Be used for : HomeScrollview设置头布局所需工具类
 */

public class UIUtil {

    /**
     * dip转换px
     */
    public static int dip2px(Context context, int dip) {
        final float scale = context.getResources().getDisplayMetrics().density;
        return (int) (dip * scale + 0.5f);
    }
    /**
     * pxz转换dip
     */
    public static int px2dip(Context context, int px) {
        final float scale = context.getResources().getDisplayMetrics().density;
        return (int) (px / scale + 0.5f);
    }

}
/**
 * Created by HXY on 2018/9/25.
 * Be used for : HomeScrollview监听接口
 */

public interface RefreshListener {

    void startRefresh(); //刷新
    void loadMore();  //加载
    void hintChange(String hint);  //提示文字
    void setWidthX(int x);  //设置x

}

准备就绪,就可以在XML中引用了(去除自带滚动条,如果需要保留,直接把最后一条属性删掉即可):

<com.example.XXXXX.sxt.view.customview.HomeScrollview
        android:id="@+id/homeScrollView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:scrollbars="none">
</com.example.XXXXX.sxt.view.customview.HomeScrollview>

外部调用监听:

homeSC = view.findViewById(R.id.homeScrollView);
homeSC.setRefreshListener(new RefreshListener() {
            @Override
            public void startRefresh() {
                //以下为测试OK工具类的代码
                HashMap<String, String> dataMap = new HashMap<>();
                dataMap.put("phone","185XXXXXXXX");

                OKUtils.getInstance().postJson(API.GET_VERIFICATION_CODE,dataMap, new Callback() {
                    @Override
                    public void onFailure(Call call, IOException e) {

                    }

                    @Override
                    public void onResponse(Call call, Response response) throws IOException {
                        final String string = response.body().string();
                        Log.d("------MainActivity", string);
                        getActivity().runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                homeSC.stopRefresh();
                            }
                        });
                    }
                });
            }

            @Override
            public void loadMore() {

            }

            @Override
            public void hintChange(String hint) {
                headHint.setText(hint);
            }

            @Override
            public void setWidthX(int x) {

            }
        });

我们还可以自定义添加头布局,头布局的话其实就是加载一个layout,用到什么就添加什么,我这里是自定义了一个弧形:

<RelativeLayout
                android:id="@+id/HomeScrollview_headView"
                android:layout_width="match_parent"
                android:layout_height="1px">

                <ImageView
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:scaleType="centerCrop"
                    android:layout_alignParentBottom="true"
                    android:src="@mipmap/home"/>
                
                <com.example.XXXXX.sxt.view.customview.HomeScrollview_HeadbgView
                    android:id="@+id/head_bg"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent" />

                <TextView
                    android:id="@+id/headHint"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_centerInParent="true"
                    android:text="下拉刷新"
                    android:textColor="#fff"/>

            </RelativeLayout>

自定义弧形view:

/**
 * Created by HXY on 2018/9/26.
 * Be used for : 自定义首页头布局,弧形下拉效果
 */

public class HomeScrollview_HeadbgView extends View {

    private int startdp;
    private int width,height,length;
    private Paint paint;
    private Paint p;
    private int widthX;
    private boolean useX;

    public HomeScrollview_HeadbgView(Context context) {
        this(context,null);
    }
    public HomeScrollview_HeadbgView(Context context, AttributeSet attrs) {
        this(context, attrs,0);
    }
    public HomeScrollview_HeadbgView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        paint = new Paint();
        paint.setStyle(Paint.Style.FILL);
        paint.setColor(getResources().getColor(R.color.colorSilver_Transparent));
        paint.setAntiAlias(true);
        p = new Paint();
        p.setColor(getResources().getColor(R.color.colorSilver_Transparent));
        p.setStyle(Paint.Style.FILL);
        startdp = UIUtil.dip2px(getContext(),50);
    }
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        width = getWidth();
        height = getHeight();
    }
    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        width = getWidth();
        height = getHeight();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        Path mPath = new Path();
        canvas.drawColor(getResources().getColor(R.color.colorSilver_Transparent));
        if(height>startdp){
            //高度达到一顶高度才显示弧线
            mPath.moveTo(0, startdp);
            // path的用法,时间问题,这里就不细说了
            if(useX){ //判断有没有设置X,也就是setWidth(x)方法
                mPath.rQuadTo(widthX, (height-startdp)*2, width, 0);
            }else{
                mPath.rQuadTo(width/2, (height-startdp)*2, width, 0);
            }
            canvas.drawPath(mPath, paint);
            canvas.drawRect(0,0,width,startdp,paint);  //填充空白
        }else{
            canvas.drawRect(0,0,width,height,paint);
        }
    }

    public void setWidthX(int x){
        useX = true;
        widthX = x;
    }
}

OK,搞完收工~

猜你喜欢

转载自blog.csdn.net/qq_19681347/article/details/82853884