美文网首页互联网技术
Android RecycletView ListView Gr

Android RecycletView ListView Gr

作者: 一点愁 | 来源:发表于2018-11-21 11:37 被阅读30次

    前言
    一直以来公司项目都没有下拉刷新的功能,最近换了新产品,有了这部分的需求设计。我看了下涉及到的页面所用的控件即有ListView也有RecycleView,鉴于刷新效果是自己设计的,想从网上直接找造好的轮子,不太现实,况且牵涉到两个控件,以后要是再改到用了GridView的页面呢。自己重写这三个控件,牵扯到的页面势必要重新翻一遍,怎么看工作量都很大。后来想了下,干脆重写一个可以下拉刷新的LinearLayout,包在外面,这要是之前的所有代码逻辑都不用动,也不用重新测试,重要的是可以给任何你想加的控价添加下拉刷新,工作量都省了下来,说干就干。
    首先,会遇到以下几个问题
    1、下拉刷新触发的时机,也就是第一个item完全漏出的时候
    2、事件分发,也就是滑动事件什么时候交给ListView什么时候交给刷新LinearLayout
    以上两个问题,想详细了解的,直接看代码注释很清晰,在这里就不细说了。
    废话不多说,直接上代码。
    一、源码
    1、头部布局文件 header_refresh.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="100dip"
        android:background="#EFEFF4">
        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="100dip"
            android:orientation="horizontal">
            <ImageView
                android:id="@+id/iv_refresh"
                android:layout_width="25dip"
                android:layout_height="25dip"
                android:layout_toLeftOf="@+id/ll_refresh_right"
                android:layout_marginRight="10dip"
                android:layout_centerVertical="true"
                android:src="@drawable/refresh1"/>
            <LinearLayout
                android:id="@+id/ll_refresh_right"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_centerHorizontal="true"
                android:layout_centerVertical="true"
                android:gravity="center"
                android:orientation="vertical">
                <TextView
                    android:id="@+id/tv_refresh"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:textColor="#D7D6D8"
                    android:textSize="14sp"
                    android:text="下拉刷新"/>
                <TextView
                    android:id="@+id/tv_last_refresh_time"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_marginTop="10dip"
                    android:textColor="#D7D6D8"
                    android:textSize="13sp"
                    android:text="最近更新:今天14:20"/>
            </LinearLayout>
    
        </RelativeLayout>
    </LinearLayout>
    

    2、刷新动画refreshing_anim.xml写在drawable里

    <?xml version="1.0" encoding="utf-8"?>
    <set android:shareInterpolator="false" xmlns:android="http://schemas.android.com/apk/res/android">
        <rotate   
            android:interpolator="@android:anim/linear_interpolator"  
            android:pivotX="50%"  
            android:pivotY="50%"  
            android:fromDegrees="0"  
            android:toDegrees="+360"  
            android:duration="300"
            android:startOffset="-1"  
            android:repeatMode="restart"  
            android:repeatCount="-1"/>  
    </set>
    

    3、重写的PullToRefreshLinearLayout .java

    import android.app.Activity;
    import android.content.Context;
    import android.content.SharedPreferences;
    import android.support.v7.widget.LinearLayoutManager;
    import android.support.v7.widget.RecyclerView;
    import android.util.AttributeSet;
    import android.view.LayoutInflater;
    import android.view.MotionEvent;
    import android.view.View;
    import android.view.ViewGroup;
    import android.view.animation.Animation;
    import android.view.animation.AnimationUtils;
    import android.widget.GridView;
    import android.widget.ImageView;
    import android.widget.LinearLayout;
    import android.widget.ListView;
    import android.widget.ScrollView;
    import android.widget.TextView;
    
    import com.xuetian.netschool.R;
    
    import java.text.SimpleDateFormat;
    import java.util.Date;
    
    /**
     * 下拉刷新控件,可以配合 RecyclerView,Scrollview,ListView等所有view使用
     *
     */
    
    public class PullToRefreshLinearLayout extends LinearLayout {
    
        private View targetRefreshView;
        int downY = 0, moveY = 0;
        int downX = 0, moveX = 0;
        boolean isRefreshStart = false;
        int hearderViewHeight = 100;//刷新头部高度dip,与布局文件中保持一致
        int instance;
        int paddingTop;
        View headerView;
        final String SHARE_KEY = "refreshTime";
        final SimpleDateFormat HHmm = new SimpleDateFormat("HH:mm");
        OnRefreshListener onRefreshListener;
        private SharedPreferences sharedPreferences;
        boolean isRefreshing = false;
    
        TextView textView, tvLastRefreshTime;
        ImageView ivRefresh;
    
        public PullToRefreshLinearLayout(Context context) {
            this(context, null);
        }
    
        public PullToRefreshLinearLayout(Context context, AttributeSet attrs) {
            this(context, attrs, 0);
        }
        Context context;
        public PullToRefreshLinearLayout(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            this.context = context;
            setOrientation(VERTICAL);
            sharedPreferences = context.getSharedPreferences(SHARE_KEY, Context.MODE_PRIVATE);
            initHeaderView();
        }
        private void initHeaderView(){
            LayoutInflater inflater = ((Activity)context).getLayoutInflater();
            headerView = inflater.inflate(R.layout.header_refresh, null);
            textView = headerView.findViewById(R.id.tv_refresh);
            ivRefresh = headerView.findViewById(R.id.iv_refresh);
            tvLastRefreshTime = headerView.findViewById(R.id.tv_last_refresh_time);
            tvLastRefreshTime.setText("最后更新:今天" + getHHmm(getRefreshTime()));
            headerView.setPadding(0, -dip2px(context, hearderViewHeight), 0, 0);
            addView(headerView,0);
        }
        @Override
        public void addView(View child) {
            if (getChildCount() > 1) {
                throw new IllegalStateException("RefreshLinearLayout can host only one direct child");
            }
            super.addView(child);
        }
    
        @Override
        public void addView(View child, int index) {
            if (getChildCount() > 1) {
                throw new IllegalStateException("RefreshLinearLayout can host only one direct child");
            }
    
            super.addView(child, index);
        }
    
        @Override
        public void addView(View child, ViewGroup.LayoutParams params) {
            if (getChildCount() > 1) {
                throw new IllegalStateException("RefreshLinearLayout can host only one direct child");
            }
    
            super.addView(child, params);
        }
    
        @Override
        public void addView(View child, int index, ViewGroup.LayoutParams params) {
            if (getChildCount() > 1) {
                throw new IllegalStateException("RefreshLinearLayout can host only one direct child");
            }
            targetRefreshView = child;
            super.addView(child, index, params);
        }
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            if(targetRefreshView == null) return super.onTouchEvent(event);
            switch (event.getAction()){
                case MotionEvent.ACTION_DOWN:
                    isRefreshStart = isStartRefresh(targetRefreshView);
                    downY = (int) event.getY();
                    downX = (int) event.getX();
                    break;
                case MotionEvent.ACTION_MOVE:
                    moveY = (int) event.getY();
                    instance = moveY - downY;
                    if (instance > 0 && isRefreshStart) {
                        paddingTop = -dip2px(context, hearderViewHeight) + instance;
                        if(paddingTop >=dip2px(context, hearderViewHeight)) paddingTop = dip2px(context, 100);
    
                        headerView.setPadding(0, paddingTop, 0, 0);
                        if(instance >= dip2px(context, hearderViewHeight)) textView.setText("松开刷新");
                        else textView.setText("下拉刷新");
                        return true;
                    }
                    break;
                case MotionEvent.ACTION_UP:
                    if(instance >= dip2px(context, hearderViewHeight)){
                        isRefreshing = true;
                        textView.setText("正在刷新...");
                        // 加载动画
                        ivRefresh.setImageResource(R.drawable.refresh1);
                        Animation hyperspaceJumpAnimation = AnimationUtils.loadAnimation(
                                context, R.anim.refreshing_anim);
                        // 使用ImageView显示动画
                        ivRefresh.startAnimation(hyperspaceJumpAnimation);
                        new Thread(new Runnable() {
                            int i = 0;
                            @Override
                            public void run() {
                                for ( i = 0; i <= 50; i++){
                                    try {
                                        Thread.sleep(5);
                                    } catch (InterruptedException e) {
                                        e.printStackTrace();
                                    }
                                    ((Activity)context).runOnUiThread(new Runnable() {
                                        @Override
                                        public void run() {
                                            headerView.setPadding(0, paddingTop - i * paddingTop/50 , 0, 0);
                                        }
                                    });
                                }
                                try {
                                    Thread.sleep(100);
                                } catch (InterruptedException e) {
                                    e.printStackTrace();
                                }
                                ((Activity)context).runOnUiThread(new Runnable() {
                                    @Override
                                    public void run() {
                                        if(onRefreshListener != null)onRefreshListener.onRefresh();
                                    }
                                });
    
                            }
                        }).start();
    
                    }else {
                        new Thread(new Runnable() {
                            int i = 0;
                            @Override
                            public void run() {
                                for ( i = 0; i <= 100; i++){
                                    try {
                                        Thread.sleep(5);
                                    } catch (InterruptedException e) {
                                        e.printStackTrace();
                                    }
                                    ((Activity)context).runOnUiThread(new Runnable() {
                                        @Override
                                        public void run() {
                                            headerView.setPadding(0,
                                                    paddingTop + i *(-dip2px(context, hearderViewHeight) - paddingTop)/100 , 0, 0);
                                        }
                                    });
                                }
                            }
                        }).start();
                    }
    
                    break;
    
            }
            return super.onTouchEvent(event);
        }
    
        @Override
        public boolean onInterceptTouchEvent(MotionEvent event) {
            if(targetRefreshView == null) return super.onInterceptTouchEvent(event);
            switch (event.getAction()){
                case MotionEvent.ACTION_DOWN:
                    isRefreshStart = isStartRefresh(targetRefreshView);
                    downY = (int) event.getY();
                    downX = (int) event.getX();
                    break;
                case MotionEvent.ACTION_MOVE:
                    moveY = (int) event.getY();
                    moveX = (int) event.getX();
                    instance = moveY - downY;
                    if (!isRefreshing && instance > 0 && isRefreshStart) {
                        if(!isRefreshing){
                            tvLastRefreshTime.setText("最后更新:今天" + getHHmm(getRefreshTime()));
                        }
                        if(Math.abs(downX - moveX) > Math.abs(downY - moveY)){
                            return false;
                        }
                        return true;
                    }else if(isRefreshing && instance < 0){
                        refreshComplete();
                    }
                    break;
                case MotionEvent.ACTION_UP:
                    break;
    
            }
            return super.onInterceptTouchEvent(event);
        }
    
        private boolean isStartRefresh(View targetRefreshView){
            if(targetRefreshView != null && (targetRefreshView instanceof ListView ||
                    targetRefreshView instanceof RecyclerView ||
                    targetRefreshView instanceof GridView || targetRefreshView instanceof ScrollView)){
                if((targetRefreshView instanceof ListView) && ((ListView)targetRefreshView).getFirstVisiblePosition() == 0
                        && ((ListView)targetRefreshView).getChildAt(0).getTop() >= targetRefreshView.getPaddingTop())
                    return true;
    
                else if ((targetRefreshView instanceof RecyclerView) && ((LinearLayoutManager)((RecyclerView)targetRefreshView)
                        .getLayoutManager()).findFirstVisibleItemPosition() == 0
                        && ((RecyclerView)targetRefreshView).getChildAt(0).getTop() >= targetRefreshView.getPaddingTop())
                    return true;
    
                else if ((targetRefreshView instanceof GridView) && ((GridView)targetRefreshView).getFirstVisiblePosition() == 0
                        && ((GridView)targetRefreshView).getChildAt(0).getTop() >= targetRefreshView.getPaddingTop())
                    return true;
    
                else if((targetRefreshView instanceof ScrollView) && ((ScrollView)targetRefreshView).getScrollY() == 0)
                    return true;
    
                else return false;
    
            }
    
            else if(targetRefreshView != null)
                return true;
    
            else return false;
        }
    
        /**
         * 将刷新头部复位
         * */
        private void headerRestoration(){
            setRefreshTime();
            if (!isRefreshing) return;
            isRefreshing = false;
            ivRefresh.clearAnimation();
            ivRefresh.setImageResource(R.drawable.refresh2);
            textView.setText("刷新成功");
            new Thread(new Runnable() {
                int i = 0;
                @Override
                public void run() {
                    for ( i = 1; i <= 100; i++){
                        try {
                            Thread.sleep(5);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        ((Activity)context).runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                headerView.setPadding(0,
                                        i * (-dip2px(context, hearderViewHeight))/100 , 0, 0);
                            }
                        });
                    }
                    ((Activity)context).runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            textView.setText("下拉刷新");
                            ivRefresh.setImageResource(R.drawable.refresh1);
                            if(onRefreshListener != null)onRefreshListener.refreshData();
                        }
                    });
    
                }
            }).start();
        }
    
    
    
        /**
         * 设置最近刷新时间
         *
         */
        private void setRefreshTime() {
            sharedPreferences.edit().putLong(context.getPackageName() + context.getClass().getName(), System.currentTimeMillis()).commit();
        }
        /**
         * 获取最近刷新时间
         */
        private Long getRefreshTime() {
            return sharedPreferences.getLong(context.getPackageName() + context.getClass().getName(), System.currentTimeMillis());
        }
    
        /**
         * 根据手机的分辨率从 dp 的单位 转成为 px(像素)
         */
        private int dip2px(Context context, float dpValue) {
            final float scale = context.getResources().getDisplayMetrics().density;
            return (int) (dpValue * scale + 0.5f);
        }
    
        /**
         * HH:mm类型的日期
         * @param milliseconds
         * @return
         */
        private String getHHmm(long milliseconds){
            if(milliseconds == 0){
                return "";
            }
            String string;
            Date date = new Date(milliseconds);
            string = HHmm.format(date);
            return string;
        }
    
    //=======================================以上代码都不用看,获取该控件实例后直接调用setOnRefreshListener即可, 会回调相应方法================================
    
        public void setOnRefreshListener(OnRefreshListener onRefreshListener){
            this.onRefreshListener = onRefreshListener;
        }
    
        //数据请求完毕,调用该方法
        public void refreshComplete(){
            headerRestoration();
        }
        /**
         * 刷新回掉接口
         * */
        public interface OnRefreshListener{
            //刷新开始,回调该方法;可在该方法中请求最新数据
            void onRefresh();
            //刷新头部复位后,会回调该方法,可在该方法里将请求到的新数据渲染到页面上
            void refreshData();
        }
    }
    

    注:刷新的头部有用到两个图片,在此就不上传了,可以根据需要自己选择
    二、使用方式
    获取到下拉刷新控件设置回调监听接口即可

    pullToRefreshLinearLayout.setOnRefreshListener(new PullToRefreshLinearLayout().OnRefreshListener() {
                @Override
                public void onRefresh() {
                    
                }
    
                @Override
                public void refreshData() {
    
                }
            });
    

    总结:只要讲该下拉刷新控件包在任何一个View或者Viewroup的外面,都可以给出下拉刷新效果

    著作:一点愁
    原创博客转载请注明出处……

    相关文章

      网友评论

        本文标题:Android RecycletView ListView Gr

        本文链接:https://www.haomeiwen.com/subject/agmqqqtx.html