Android刷新加载框架详解

作者:Sun2Ming 时间:2022-11-20 05:41:10 

本文实例为大家分享了Android刷新加载框架的具体代码,供大家参考,具体内容如下

1.定义一个接口控制下拉和上拉


public interface Pullable {

/**
  * 是否可下拉
  */
 boolean canPullDown();

/**
  * 是否可上拉
  */
 boolean canPullUp();
}

2.定义一个刷新加载布局


public class PullToRefreshLayout extends RelativeLayout {

/**
  * 头
  */
 private View headView;//头视图
 private ImageView headIv;//头图标
 private TextView headTv;//头文字
 private int headHeight;//头高度
 private float headBorder;//头临界
 /**
  * 拉
  */
 private View pullView;//拉视图
 private int pullHeight;//拉高度
 private int pullWidth;//拉宽度

/**
  * 尾
  */
 private View footView;//尾视图
 private ImageView footIv;//尾图标
 private TextView footTv;//尾文字
 private int footHeight;//尾高度
 private float footBorder;//尾临界

/**
  * 状态
  */
 public static final int INIT = 0;//初始
 public static final int RELEASE_TO_REFRESH = 1;//释放刷新
 public static final int REFRESHING = 2;//正在刷新
 public static final int RELEASE_TO_LOAD = 3;//释放加载
 public static final int LOADING = 4;//正在加载
 public static final int DONE = 5;//完成
 private int state = INIT;

/**
  * 接口
  */
 private OnRefreshListener mListener;

private float downY;//按下时Y坐标
 private float lastY;//上一个Y坐标
 private float pullDownY = 0;//下拉偏移量
 private float pullUpY = 0;//上拉偏移量
 private int offset;//偏移量

/**
  * 动画
  */
 private RotateAnimation rotateAnimation;

/**
  * 线程
  */
 private Handler handler = new Handler() {
   @Override
   public void handleMessage(Message msg) {
     super.handleMessage(msg);
     if (msg != null) {
       switch (msg.what) {
         case 1:
           headIv.clearAnimation();
           break;
         case 2:
           footIv.clearAnimation();
           break;
         default:
           break;
       }
       pullDownY = 0;
       pullUpY = 0;
       requestLayout();
       state = INIT;
       refreshViewByState();
       isTouch = true;
     }
   }
 };

/**
  * 第一次执行布局
  */
 private boolean isLayout = false;
 /**
  * 在刷新过程中滑动操作
  */
 private boolean isTouch = false;
 /**
  * 手指滑动距离与下拉头的滑动距离比,中间会随正切函数变化
  */
 private float radio = 2;
 /**
  * 过滤多点触碰
  */
 private int mEvents;
 /**
  * 这两个变量用来控制pull的方向,如果不加控制,当情况满足可上拉又可下拉时没法下拉
  */
 private boolean canPullDown = true;
 private boolean canPullUp = true;

public void setOnRefreshListener(OnRefreshListener listener) {
   mListener = listener;
 }

public PullToRefreshLayout(Context context) {
   super(context);
   initView(context);
 }

public PullToRefreshLayout(Context context, AttributeSet attrs) {
   super(context, attrs);
   initView(context);
 }

public PullToRefreshLayout(Context context, AttributeSet attrs, int defStyle) {
   super(context, attrs, defStyle);
   initView(context);
 }

private void initView(Context context) {
   rotateAnimation = (RotateAnimation) AnimationUtils.loadAnimation(context, R.anim.rotating);
 }

private void refreshViewByState() {
   switch (state) {
     case INIT:
       // 下拉布局初始状态
       headIv.setImageResource("下拉刷新显示的图片");
       headTv.setText("下拉刷新");
       // 上拉布局初始状态
       footIv.setImageResource("上拉加载显示的图片");
       footTv.setText("上拉加载");
       break;
     case RELEASE_TO_REFRESH:
       // 释放刷新状态
       headIv.setImageResource("释放刷新显示的图片");
       headTv.setText("释放刷新");
       break;
     case REFRESHING:
       // 正在刷新状态
       headIv.setImageResource("正在刷新显示的图片");
       headTv.setText("正在刷新");
       break;
     case RELEASE_TO_LOAD:
       // 释放加载状态
       footIv.setImageResource("释放加载显示的图片");
       footTv.setText("释放加载");
       break;
     case LOADING:
       // 正在加载状态
       footIv.setImageResource("正在加载显示的图片");
       footTv.setText("正在加载");
       break;
     case DONE:
       // 刷新或加载完毕,啥都不做
       break;
   }
 }

/**
  * 不限制上拉或下拉
  */
 private void releasePull() {
   canPullDown = true;
   canPullUp = true;
 }

@Override
 public boolean dispatchTouchEvent(MotionEvent ev) {
   switch (ev.getActionMasked()) {
     case MotionEvent.ACTION_DOWN:
       downY = ev.getY();
       lastY = downY;
       mEvents = 0;
       releasePull();
       if (state != REFRESHING && state != LOADING) {
         isTouch = true;
       }
       break;
     case MotionEvent.ACTION_POINTER_DOWN:
     case MotionEvent.ACTION_POINTER_UP:
       // 过滤多点触碰
       mEvents = -1;
       break;
     case MotionEvent.ACTION_MOVE:
       if (mEvents == 0) {
         if (pullDownY > 0 || (((Pullable) pullView).canPullDown()
             && canPullDown && state != LOADING && state != REFRESHING)) {
           // 可以下拉,正在加载时不能下拉
           // 对实际滑动距离做缩小,造成用力拉的感觉
           pullDownY = pullDownY + (ev.getY() - lastY) / radio;
           if (pullDownY < 0) {
             pullDownY = 0;
             canPullDown = false;
             canPullUp = true;
           }
           if (pullDownY > getMeasuredHeight()) {
             pullDownY = getMeasuredHeight();
           }
           if (state == REFRESHING) {
             // 正在刷新的时候触摸移动
             isTouch = false;
           }
         } else if (pullUpY < 0
             || (((Pullable) pullView).canPullUp() && canPullUp && state != REFRESHING && state != LOADING)) {
           // 可以上拉,正在刷新时不能上拉
           pullUpY = pullUpY + (ev.getY() - lastY) / radio;

if (pullUpY > 0) {
             pullUpY = 0;
             canPullDown = true;
             canPullUp = false;
           }
           if (pullUpY < -getMeasuredHeight()) {
             pullUpY = -getMeasuredHeight();
           }
           if (state == LOADING) {
             // 正在加载的时候触摸移动
             isTouch = false;
           }
         }
       }
       if (isTouch) {
         lastY = ev.getY();
         if (pullDownY > 0 || pullUpY < 0) {
           requestLayout();
         }
         if (pullDownY > 0) {
           if (pullDownY <= headBorder && (state == RELEASE_TO_REFRESH || state == DONE)) {
             // 如果下拉距离没达到刷新的距离且当前状态是释放刷新,改变状态为下拉刷新
             state = INIT;
             refreshViewByState();
           }
           if (pullDownY >= headBorder && state == INIT) {
             // 如果下拉距离达到刷新的距离且当前状态是初始状态刷新,改变状态为释放刷新
             state = RELEASE_TO_REFRESH;
             refreshViewByState();
           }
         } else if (pullUpY < 0) {
           // 下面是判断上拉加载的,同上,注意pullUpY是负值
           if (-pullUpY <= footBorder && (state == RELEASE_TO_LOAD || state == DONE)) {
             state = INIT;
             refreshViewByState();
           }
           // 上拉操作
           if (-pullUpY >= footBorder && state == INIT) {
             state = RELEASE_TO_LOAD;
             refreshViewByState();
           }
         }
         // 因为刷新和加载操作不能同时进行,所以pullDownY和pullUpY不会同时不为0,因此这里用(pullDownY +
         // Math.abs(pullUpY))就可以不对当前状态作区分了
         if ((pullDownY + Math.abs(pullUpY)) > 8) {
           // 防止下拉过程中误触发长按事件和点击事件
           ev.setAction(MotionEvent.ACTION_CANCEL);
         }
       }
       break;
     case MotionEvent.ACTION_UP:
       if (pullDownY > headBorder || -pullUpY > footBorder) {
         // 正在刷新时往下拉(正在加载时往上拉),释放后下拉头(上拉头)不隐藏
         isTouch = false;
       }
       if (state == RELEASE_TO_REFRESH) {
         state = REFRESHING;
         refreshViewByState();
         // 刷新操作
         if (mListener != null) {
           canPullDown = false;
           pullDownY = headBorder;
           pullUpY = 0;
           requestLayout();
           headIv.startAnimation(rotateAnimation);
           mListener.onRefresh(this);
         }
       } else if (state == RELEASE_TO_LOAD) {
         state = LOADING;
         refreshViewByState();
         // 加载操作
         if (mListener != null) {
           canPullUp = false;
           pullDownY = 0;
           pullUpY = -footBorder;
           requestLayout();
           footIv.startAnimation(rotateAnimation);
           mListener.onLoadMore(this);
         }
       } else {
         pullDownY = 0;
         pullUpY = 0;
         requestLayout();
       }
     default:
       break;
   }
   // 事件分发交给父类
   super.dispatchTouchEvent(ev);
   return true;
 }

public void hideHeadView() {
   handler.sendEmptyMessage(1);
 }

public void hideFootView() {
   handler.sendEmptyMessage(2);
 }

private void initView() {
   // 初始化下拉布局
   headIv = (ImageView) headView.findViewById(R.id.iv_head);
   headTv = (TextView) headView.findViewById(R.id.tv_head);
   //初始化上拉布局
   footIv = (ImageView) footView.findViewById(R.id.iv_foot);
   footTv = (TextView) footView.findViewById(R.id.tv_foot);
   refreshViewByState();
 }

@Override
 protected void onLayout(boolean changed, int l, int t, int r, int b) {
   if (!isLayout) {
     // 这里是第一次进来的时候做一些初始化
     headView = getChildAt(0);
     pullView = getChildAt(1);
     footView = getChildAt(2);
     headBorder = ((ViewGroup) headView).getChildAt(0).getMeasuredHeight();
     footBorder = ((ViewGroup) footView).getChildAt(0).getMeasuredHeight();
     headHeight = headView.getMeasuredHeight();
     pullHeight = pullView.getMeasuredHeight();
     footHeight = footView.getMeasuredHeight();
     pullWidth = pullView.getMeasuredWidth();
     initView();
     isLayout = true;
   }
   // 改变子控件的布局,这里直接用(pullDownY + pullUpY)作为偏移量,这样就可以不对当前状态作区分
   offset = (int) (pullDownY + pullUpY);
   headView.layout(0, -headHeight + offset, pullWidth, offset);
   pullView.layout(0, offset, pullWidth, pullHeight + offset);
   footView.layout(0, pullHeight + offset, pullWidth, pullHeight + footHeight + offset);
 }

public interface OnRefreshListener {
   void onRefresh(PullToRefreshLayout pullToRefreshLayout);

void onLoadMore(PullToRefreshLayout pullToRefreshLayout);
 }

}

3.自定义View
ListView


public class PullableListView extends ListView implements Pullable {

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

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

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

@Override
 public boolean canPullDown() {
   if (getCount() == 0) {
     // 没有item的时候也可以下拉刷新
     return false;
   } else if (getFirstVisiblePosition() == 0 && getChildAt(0).getTop() >= 0) {
     // 滑到ListView的顶部了
     return true;
   } else
     return false;
 }

@Override
 public boolean canPullUp() {
   if (getCount() == 0) {
     // 没有item的时候也可以上拉加载
     return false;
   } else if (getLastVisiblePosition() == (getCount() - 1)) {
     // 滑到底部了
     if (getChildAt(getLastVisiblePosition() - getFirstVisiblePosition()) != null
         && getChildAt(getLastVisiblePosition() - getFirstVisiblePosition())
         .getBottom() <= getMeasuredHeight())
       return true;
   }
   return false;
 }
}

GridView


public class PullableGridView extends GridView implements Pullable {

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

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

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

@Override
 public boolean canPullDown() {
   if (getCount() == 0) {
     // 没有item的时候也可以下拉刷新
     return false;
   } else if (getFirstVisiblePosition() == 0
       && getChildAt(0).getTop() >= 0) {
     // 滑到顶部了
     return true;
   } else
     return false;
 }

@Override
 public boolean canPullUp() {
   if (getCount() == 0) {
     // 没有item的时候也可以上拉加载
     return false;
   } else if (getLastVisiblePosition() == (getCount() - 1)) {
     // 滑到底部了
     if (getChildAt(getLastVisiblePosition() - getFirstVisiblePosition()) != null
         && getChildAt(
         getLastVisiblePosition()
             - getFirstVisiblePosition()).getBottom() <= getMeasuredHeight())
       return true;
   }
   return false;
 }

}

RecyclerView


public class PullableRecyclerView extends RecyclerView implements Pullable {

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

public PullableRecyclerView(Context context, @Nullable AttributeSet attrs) {
   super(context, attrs);
 }

public PullableRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
   super(context, attrs, defStyle);
 }

@Override
 public boolean canPullDown() {
   RecyclerView.LayoutManager layoutManager = getLayoutManager();
   if (layoutManager instanceof LinearLayoutManager) {
     LinearLayoutManager linearLayoutManager = (LinearLayoutManager) layoutManager;
     if (linearLayoutManager.getItemCount() == 0) {
       return false;
     } else if (linearLayoutManager.findFirstVisibleItemPosition() == 0 && linearLayoutManager.getChildAt(0).getTop() >= 0) {
       return true;
     } else {
       return false;
     }
   } else if (layoutManager instanceof StaggeredGridLayoutManager) {
     StaggeredGridLayoutManager staggeredGridLayoutManager = (StaggeredGridLayoutManager) layoutManager;
     if (staggeredGridLayoutManager.getItemCount() == 0) {
       return false;
     } else {
       int[] firstVisibleItems = null;
       firstVisibleItems = staggeredGridLayoutManager.findFirstVisibleItemPositions(firstVisibleItems);
       if (firstVisibleItems != null && firstVisibleItems.length > 0) {
         if (staggeredGridLayoutManager.getChildCount() + firstVisibleItems[0] == staggeredGridLayoutManager.getItemCount()) {
           return true;
         }
       }
     }
   }
   return false;
 }

@Override
 public boolean canPullUp() {
   RecyclerView.LayoutManager layoutManager = getLayoutManager();
   if (layoutManager instanceof LinearLayoutManager) {
     LinearLayoutManager linearLayoutManager = (LinearLayoutManager) layoutManager;
     if (linearLayoutManager.getItemCount() == 0) {
       return false;
     } else if (linearLayoutManager.findLastVisibleItemPosition() == (linearLayoutManager.getItemCount() - 1)) {
       if (linearLayoutManager.getChildAt(linearLayoutManager.findLastVisibleItemPosition() - linearLayoutManager.findFirstVisibleItemPosition()) != null
           && linearLayoutManager.getChildAt(linearLayoutManager.findLastVisibleItemPosition() - linearLayoutManager.findFirstVisibleItemPosition()).getBottom() <= getMeasuredHeight()) {
         return true;
       }
     }
   } else if (layoutManager instanceof StaggeredGridLayoutManager) {
     StaggeredGridLayoutManager staggeredGridLayoutManager = (StaggeredGridLayoutManager) layoutManager;
     if (staggeredGridLayoutManager.getItemCount() == 0) {
       return false;
     } else {
       int[] lastPositions = new int[staggeredGridLayoutManager.getSpanCount()];
       lastPositions = staggeredGridLayoutManager.findLastVisibleItemPositions(lastPositions);
       if (findMax(lastPositions) >= staggeredGridLayoutManager.getItemCount() - 1) {
         return true;
       }
     }
   }
   return false;
 }

private int findMax(int[] lastPositions) {
   int max = lastPositions[0];
   for (int value : lastPositions) {
     if (value > max) {
       max = value;
     }
   }
   return max;
 }
}

ExpandableListView


public class PullableExpandableListView extends ExpandableListView implements
   Pullable {

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

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

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

@Override
 public boolean canPullDown() {
   if (getCount() == 0) {
     // 没有item的时候也可以下拉刷新
     return false;
   } else if (getFirstVisiblePosition() == 0
       && getChildAt(0).getTop() >= 0) {
     // 滑到顶部了
     return true;
   } else
     return false;
 }

@Override
 public boolean canPullUp() {
   if (getCount() == 0) {
     // 没有item的时候也可以上拉加载
     return false;
   } else if (getLastVisiblePosition() == (getCount() - 1)) {
     // 滑到底部了
     if (getChildAt(getLastVisiblePosition() - getFirstVisiblePosition()) != null
         && getChildAt(
         getLastVisiblePosition()
             - getFirstVisiblePosition()).getBottom() <= getMeasuredHeight())
       return true;
   }
   return false;
 }

}

ScrollView


public class PullableScrollView extends ScrollView implements Pullable {

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

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

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

@Override
 public boolean canPullDown() {
   if (getScrollY() == 0)
     return true;
   else
     return false;
 }

@Override
 public boolean canPullUp() {
   if (getScrollY() >= (getChildAt(0).getHeight() - getMeasuredHeight()))
     return true;
   else
     return false;
 }

}

WebView


public class PullableWebView extends WebView implements Pullable {

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

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

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

@Override
 public boolean canPullDown() {
   if (getScrollY() == 0)
     return true;
   else
     return false;
 }

@Override
 public boolean canPullUp() {
   if (getScrollY() >= getContentHeight() * getScale()
       - getMeasuredHeight())
     return true;
   else
     return false;
 }
}

ImageView


public class PullableImageView extends ImageView implements Pullable
{

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

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

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

@Override
 public boolean canPullDown()
 {
   return true;
 }

@Override
 public boolean canPullUp()
 {
   return true;
 }

}

TextView


public class PullableTextView extends TextView implements Pullable {

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

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

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

@Override
 public boolean canPullDown() {
   return true;
 }

@Override
 public boolean canPullUp() {
   return true;
 }

}

4.使用示例(以ListView为例)


<view.PullToRefreshLayout
 android:id="@+id/ptrl"
 android:layout_width="match_parent"
 android:layout_height="match_parent">

<include layout="@layout/head" />

<view.PullableListView
   android:id="@+id/plv"
   android:layout_width="match_parent"
   android:layout_height="match_parent" />

<include layout="@layout/foot" />

</view.PullToRefreshLayout>

head


<?xml version="1.0" encoding="UTF-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
 android:id="@+id/head_view"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:background="@android:color/darker_gray">

<RelativeLayout
   android:layout_width="match_parent"
   android:layout_height="wrap_content"
   android:layout_alignParentBottom="true"
   android:paddingBottom="20dp"
   android:paddingTop="20dp">

<RelativeLayout
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
     android:layout_centerInParent="true">

<ImageView
       android:id="@+id/iv_head"
       android:layout_width="20dp"
       android:layout_height="20dp"
       android:layout_centerVertical="true"
       android:layout_marginLeft="60dp"
       android:scaleType="fitXY" />

<TextView
       android:id="@+id/tv_head"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:layout_centerInParent="true"
       android:textColor="@android:color/white"
       android:textSize="16sp" />

</RelativeLayout>

</RelativeLayout>

</RelativeLayout>

foot


<?xml version="1.0" encoding="UTF-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
 android:id="@+id/loadmore_view"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:background="@android:color/darker_gray">

<RelativeLayout
   android:layout_width="match_parent"
   android:layout_height="wrap_content"
   android:layout_alignParentTop="true"
   android:paddingBottom="20dp"
   android:paddingTop="20dp">

<RelativeLayout
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
     android:layout_centerInParent="true">

<ImageView
       android:id="@+id/iv_foot"
       android:layout_width="20dp"
       android:layout_height="20dp"
       android:layout_centerVertical="true"
       android:layout_marginLeft="60dp"
       android:scaleType="fitXY" />

<TextView
       android:id="@+id/tv_foot"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:layout_centerInParent="true"
       android:textColor="@android:color/white"
       android:textSize="16sp" />
   </RelativeLayout>

</RelativeLayout>

</RelativeLayout>

4.注意

自定义的View跟正常的View的使用没有什么差别
如需实现刷新加载,必须使ptrl.setOnRefreshListener(PullToRefreshLayout.OnRefreshListener onRefreshListener);

来源:http://blog.csdn.net/sun2ming/article/details/78637922

标签:Android,刷新,加载
0
投稿

猜你喜欢

  • java多线程通过CompletableFuture组装异步计算单元

    2023-07-19 10:15:42
  • Android布局之表格布局TableLayout详解

    2022-09-08 08:08:06
  • C#微信公众号开发 微信事件交互

    2023-04-22 21:18:31
  • Spring Bean的线程安全问题

    2023-06-07 17:15:36
  • java实现Yaml转Json示例详解

    2023-12-06 22:58:34
  • Android ViewPager实现图片轮翻效果

    2023-08-01 00:30:57
  • C#资源释放方法实例分析

    2022-03-22 04:46:20
  • 实例解析使用Java实现基本的音频播放器的编写要点

    2023-11-29 14:54:26
  • Ubuntu安装jdk8常用方法流程解析

    2021-12-21 13:40:04
  • Java如何固定大小的线程池

    2021-08-30 06:18:19
  • android使用SoundPool播放音效的方法

    2023-07-08 01:16:29
  • Java深入了解数据结构之二叉搜索树增 插 删 创详解

    2023-02-14 08:08:00
  • C#通过反射打开相应窗体方法分享

    2023-09-21 07:37:14
  • Android项目实战之仿网易顶部导航栏效果

    2022-07-14 10:20:51
  • Java泛型的使用限制实例分析

    2023-05-07 20:14:52
  • 超详细的Spring Boot入门笔记(总结)

    2022-10-26 18:44:21
  • Android加载loading对话框的功能及实例代码(不退出沉浸式效果)

    2023-05-14 01:25:33
  • JAVA WSIMPORT生成WEBSERVICE客户端401认证过程图解

    2023-11-14 00:27:55
  • Flutter Android端启动白屏问题的解决

    2023-10-14 12:03:27
  • c#根据文件大小显示文件复制进度条实例

    2022-10-10 10:35:16
  • asp之家 软件编程 m.aspxhome.com