Android 中 ListView 的 下拉刷新 和 上拉加载 的 重点及学习(一)
来源:互联网 发布:微信发长视频软件 编辑:程序博客网 时间:2024/05/21 08:35
(转载注明: http://blog.csdn.net/itermeng/article/details/52289929 :)
大多App中的一个必备功能:用listView实现下拉刷新和上拉加载,其实有很多大牛都写了类似的Blog,但我还想记录一下,梳理自己的思路,而且我会想之前写的轮播图博客一样,我的重点是在如何写的思路,不愿直接贴代码,想看代码的直接看文章最下面吧 :)
如上gif动图所示,接下来我们要完成下拉刷新的实现。
一. 完成 xml 文件的编写 :
1.完成listView的头布局
结合了帧布局和线性布局
<?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="match_parent" android:orientation="horizontal"> <FrameLayout android:layout_margin="5dp" android:layout_width="50dp" android:layout_height="50dp" > <ImageView android:id="@+id/iv_arrow" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:src="@drawable/common_listview_headview_red_arrow" /> <ProgressBar android:id="@+id/pb" android:layout_width="match_parent" android:layout_height="match_parent" android:indeterminateDrawable="@drawable/shape_progress" android:visibility="invisible" /> </FrameLayout> <LinearLayout android:layout_margin="5dp" android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center_horizontal" android:orientation="vertical" > <TextView android:id="@+id/tv_title" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="5dp" android:text="下拉刷新" android:textColor="#F00" android:textSize="18sp" /> <TextView android:id="@+id/tv_desc_last_refresh" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="5dp" android:singleLine="true" android:text="最后刷新时间: 2015-10-11 09:20:35" android:textColor="#666" android:textSize="14sp" /> </LinearLayout></LinearLayout>
2. 自定义ProgressBar
这里红色渐变的圆圈为自定义的xml
<?xml version="1.0" encoding="utf-8"?><rotate xmlns:android="http://schemas.android.com/apk/res/android" android:fromDegrees="0" android:pivotX="50%" android:pivotY="50%" android:toDegrees="-360" > <shape xmlns:android="http://schemas.android.com/apk/res/android" android:innerRadiusRatio="2.5" android:shape="ring" android:thicknessRatio="10" android:useLevel="false" > <gradient android:centerColor="#44FF0000" android:endColor="#00000000" android:startColor="#FF0000" android:type="sweep" /> </shape></rotate>
二. 完成逻辑编写实现下拉刷新 :
1. 自定义RefreshListView继承 Listview
继承3个构造方法,并在init()中写initHeaderView()
方法,初始化头布局
public PullToRefreshListView(Context context) { super(context); init() } public PullToRefreshListView(Context context, AttributeSet attrs) { super(context, attrs); init() } public PullToRefreshListView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init() }
2. 初始化头布局并隐藏
默认让头布局隐藏,主要就是测得我们刚写的xml文件头布局的高度,用padding将它隐藏起来即可,再设置listView添加头布局
/** * 初始化头布局 */ private void initHeaderView() { mHeaderView = View.inflate(getContext(), R.layout.layout_header_list, null); mArrowView = mHeaderView.findViewById(R.id.iv_arrow); pb = (ProgressBar) mHeaderView.findViewById(R.id.pb); mTitleText = (TextView) mHeaderView.findViewById(R.id.tv_title); mLastRefreshTime = (TextView) mHeaderView.findViewById(R.id.tv_desc_last_refresh); // 提前手动测量宽高 mHeaderView.measure(0, 0);// 按照设置的规则测量 mHeaderViewHeight = mHeaderView.getMeasuredHeight(); System.out.println(" measuredHeight: " + mHeaderViewHeight); // 设置内边距, 可以隐藏当前控件 , -自身高度 mHeaderView.setPadding(0, -mHeaderViewHeight, 0, 0); // 在设置数据适配器之前执行添加 头布局/脚布局 的方法. addHeaderView(mHeaderView); }
3. 滑动事件处理(重点)
万事俱备,头布局也已经隐藏掉,接下来我们想通过手指的滑动,让头布局显示出来,这时就涉及到 onTouchEvent 事件。
public static final int PULL_TO_REFRESH = 0;// 下拉刷新状态 public static final int RELEASE_REFRESH = 1;// 释放刷新状态 public static final int REFRESHING = 2; // 刷新中状态@Override public boolean onTouchEvent(MotionEvent ev) { // 判断滑动距离, 给Header设置paddingTop switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: downY = ev.getY(); System.out.println("downY: " + downY); break; case MotionEvent.ACTION_MOVE: moveY = ev.getY(); System.out.println("moveY: " + moveY); // 如果是正在刷新中, 就执行父类的处理 if(currentState == REFRESHING){ return super.onTouchEvent(ev); } float offset = moveY - downY; // 移动的偏移量 // 只有 偏移量>0, 并且当前第一个可见条目索引是0, 才放大头部 if(offset > 0 && getFirstVisiblePosition() == 0){ //int paddingTop = -自身高度 + 偏移量 int paddingTop = (int) (- mHeaderViewHeight + offset); mHeaderView.setPadding(0, paddingTop, 0, 0); if(paddingTop >= 0 && currentState != RELEASE_REFRESH){// 头布局完全显示 System.out.println("切换成释放刷新模式: " + paddingTop); // 切换成释放刷新模式 currentState = RELEASE_REFRESH; updateHeader(); // 根据最新的状态值更新头布局内容 }else if(paddingTop < 0 && currentState != PULL_TO_REFRESH){ // 头布局不完全显示 System.out.println("切换成下拉刷新模式: " + paddingTop); // 切换成下拉刷新模式 currentState = PULL_TO_REFRESH; updateHeader(); // 根据最新的状态值更新头布局内容 } return true; // 当前事件被我们处理并消费 } break; case MotionEvent.ACTION_UP: // 根据刚刚设置状态 if(currentState == PULL_TO_REFRESH){// - paddingTop < 0 不完全显示, 恢复 mHeaderView.setPadding(0, -mHeaderViewHeight, 0, 0); }else if(currentState == RELEASE_REFRESH){// - paddingTop >= 0 完全显示, 执行正在刷新... mHeaderView.setPadding(0, 0, 0, 0); currentState = REFRESHING; updateHeader(); } break; default: break; } return super.onTouchEvent(ev); }
首先要了解三个状态:
(1)PULL_TO_REFRESH下拉刷新:拉的过程中会进入到“释放刷新”,要是拉到一半就放手即未进入“释放刷新”,就不会去刷新数据。
(2)RELEASE_REFRESH释放刷新:拉到了一定距离,只要放手就会进入”刷新中”状态。
(3)REFRESHING刷新中:状态为正在刷新,此时就可以去请求数据了。
其次从 Down 、Move、Up这三个动作来分析逻辑:
(1) Down: 即手指刚刚点下去,在这里只需要获取当前的 y坐标值(上下滑动,只在意Y值)
(2) Move: 即手指滑动的过程,在这里需要根据移动的距离 offset 和 当前的状态 来 显示出头布局 并 改变状态。
1. 当前状态为“正在刷新”,即不处理(也不看第二点了,return出去)。 2. 只有 偏移量 offset>0, 并且当前第一个可见条目索引是0, 才显示头布局。 paddingTop : 需要隐藏的距离= - 自身高度 + 偏移量。 2.1 (paddingTop >= 0 且 状态不等于“释放刷新”):头布局完全显示,此时状态改为“释放刷新” 2.2 (paddingTop < 0 且 状态不等于“释放刷新”): 头布局不完全显示,此时状态改为“下拉刷新” 2.3 其余情况不管:
情况2.1:
情况2.2:
(3) Up: 即手指抬起,根据Move过程后改变的状态来判断是否进行刷新的逻辑 和 状态修改。
1.状态为”下拉刷新“ :即收起头布局,不需要进行刷新的逻辑。2.状态为”释放刷新“ :显示完全头布局,修改状态为”正在刷新“,进行刷新的逻辑。
(以上代码中的refreshState方法留到下下一点讲,其实就是一个UI的变化)
(监听对象mListener 采取回调,第六点讲解)
4.下拉刷新箭头旋转动画
以上完成后,接下来完善一下头布局的动画,需要在init()
中初始化initAnimation()
/** * 初始化头布局的动画 */ private void initAnimation() { // 向上转, 围绕着自己的中心, 逆时针旋转0 -> -180. rotateUpAnim = new RotateAnimation(0f, -180f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); rotateUpAnim.setDuration(300); rotateUpAnim.setFillAfter(true); // 动画停留在结束位置 // 向下转, 围绕着自己的中心, 逆时针旋转 -180 -> -360 rotateDownAnim = new RotateAnimation(-180f, -360, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); rotateDownAnim.setDuration(300); rotateDownAnim.setFillAfter(true); // 动画停留在结束位置 }
5. refreshState()修改头布局中UI的变化
第五点同第四点都是有关于UI的变化,比较简单,虽然在onTouchevent中根据不同情况修改了 头布局的显示多少 ,但是头布局中的小控件都有相应的动画或改变,需要抽取一个方法更细致的展示。
/** * 根据当前状态刷新界面 */ private void refreshState() { switch (mCurrentState) { case STATE_PULL_TO_REFRESH: tvTitle.setText("下拉刷新"); pbProgress.setVisibility(View.INVISIBLE); ivArrow.setVisibility(View.VISIBLE); ivArrow.startAnimation(animDown); break; case STATE_RELEASE_TO_REFRESH: tvTitle.setText("松开刷新"); pbProgress.setVisibility(View.INVISIBLE); ivArrow.setVisibility(View.VISIBLE); ivArrow.startAnimation(animUp); break; case STATE_REFRESHING: tvTitle.setText("正在刷新..."); ivArrow.clearAnimation();// 清除箭头动画,否则无法隐藏 pbProgress.setVisibility(View.VISIBLE); ivArrow.setVisibility(View.INVISIBLE); break; default: break; } }
6.下拉刷新监听(重点!!!!!!回调!!!!)
运用回调,使在手指抬起Up后,能够调用方法,进行刷新的逻辑。
(1)下拉刷新的回调接口
public interface OnRefreshListener { void onRefresh(); }
(2)暴露接口,设置监听
public void setOnRefreshListener(OnRefreshListener listener) { mListener = listener; }
(3)定义成员变量,接收监听对象
private OnRefreshListener mListener;
(4) 在合适的地方进行回调
(之前第三点中onTouchevent事件中进行回调)
if (mListener != null) { mListener.onRefresh(); }
(5). 前端界面设置回调(并非RefreshListView类,是使用了该listView类中写!!!)
lvList.setOnRefreshListener(new OnRefreshListener() { @Override public void onRefresh() { // 刷新数据 } });
7. 刷新结束,收起控件
最后一步,刷新完成,恢复控件原始位置,状态恢复,标题恢复,刷新完成的条件下更新时间。
public void onRefreshComplete(boolean success) { mHeaderView.setPadding(0, -mHeaderViewHeight, 0, 0); mCurrentState = STATE_PULL_TO_REFRESH; tvTitle.setText("下拉刷新"); pbProgress.setVisibility(View.INVISIBLE); ivArrow.setVisibility(View.VISIBLE); if (success) {// 只有刷新成功之后才更新时间 setCurrentTime(); } }
以上就是所有的逻辑,有关于”下拉刷新“的重点,其实各个细节都会有耦合的部分,尽量抽取出一个个小模块进行讲解,其中最重要的就是 第三点.滑动事件处理 和 第六点.下拉刷新监听回调。大致这几个步骤,讲的还是蛮细的,适合入门吧,也不知道这样分开讲好不好,总之,希望对你们有帮助 :)
后续在这里 :http://blog.csdn.net/itermeng/article/details/52297286 :)
- Android 中 ListView 的 下拉刷新 和 上拉加载 的 重点及学习(一)
- Android 中 ListView 的 下拉刷新 和 上拉加载 的 重点及学习(二)
- ListView的下拉刷新和上拉加载(一)
- Android之实现ListView的“下拉刷新”、“上拉加载”、“自动加载”功能(一)
- 下拉刷新及上拉加载更多的ListView
- 下拉刷新+上拉加载的listview
- listview的上拉刷新,下拉加载
- listview的上拉加载,下拉刷新
- ListView的上拉加载和下拉刷新的使用
- 框架学习二:ListView的下拉刷新+上拉加载
- Android ListView下拉刷新上拉加载更多的实现
- Android下拉刷新上拉加载更多的扩展ListView
- android 好用的listview上拉加载下拉刷新
- Android实现ListView的下拉刷新、上拉加载更多
- 下拉刷新和上拉加载的ListView-MutilListView
- 关于ListView的下拉刷新和上拉加载功能
- listview的下拉刷新和上拉加载更多
- listview的上拉加载更多和下拉刷新
- PIQ59: Find the missing number in the increasing sequence
- 文字换行
- 初始化方法详细
- EasyUI学习第三篇:Form表单
- hdu 3657 Game【最大权独立点集------最大流最小割Dinic】
- Android 中 ListView 的 下拉刷新 和 上拉加载 的 重点及学习(一)
- NYOJ 499.迷宫(深搜)
- c++关于线性表的基本操作
- 深入理解计算机系统 笔记(二)
- 100天土鸡饲养计划(32)
- UVA 11210 暴力枚举 + 递归(hdu 4431)
- Leetcode 2 Add Two Numbers
- 在Ubuntu中安装Circos
- 智慧医疗解决方案