该准备的东西都已经准备好了。在这篇文章里,我们就开始实现下拉刷新功能吧。

一、大体的逻辑分析

我们来简单分析一下需要做的逻辑吧。首先分析头布局有几种状态。不下拉时,为正常状态,此时头布局隐藏。下拉到一定高度,提示信息变为“下拉刷新”,箭头朝下,此为下拉状态。再往下拉,提示信息变为“松开刷新”,箭头朝上,此为提示刷新状态。而此时松开手指,则执行刷新操作,头布局变为进度条显示,箭头消失,此为正在刷新状态。相反的,其他状态下松开手指,都不执行刷新操作,应该将头布局恢复到正常状态。因为可确定头布局的状态有四种。

我们根据这四种状态,确定我们要做的事情。要监听ListView的滚动,故要实现OnScrollListener接口。还要监听手指触摸事件,根据手指的下拉移动来改变头布局的显示效果,根据手指的抬起来判断是否进行刷新操作,因为要实现onTouchEvent方法。也就是说,头布局状态的改变应该随着手指的移动而改变,因此在onTouchEvent里面我们要实现上面分析的四种状态的改变。当然,状态改变就意味着头布局显示效果的改变,这里可以嵌套在onTouchEvent方法里面。但考虑到避免方法臃肿,以及其他地方可能也需要改变头布局界面,比如数据加载完成后等情况,因此专门将头布局界面的改变抽取出来,凝聚为一个方法。

然后就是数据刷新,刷新操作要在MyListView里执行,但是数据要在MainActivity中获取。老规矩,用接口回调即可。

好了,基本上大体的逻辑就这么多了。下面我们将上面的分析转化为代码。

二、代码编写

废话我就不多说了,上面的分析很清楚了。继续完善MyListView即可。代码如下:

 package com.fuly.load;

 import android.content.Context;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.AbsListView.OnScrollListener;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView; public class MyListView extends ListView implements OnScrollListener{ private View header;//头布局 private int headerHeight;//头布局自身的高度 private int scrollState;//当前滚动状态
private int firstVisibleItem;//当前可见的第一个item
private int startY;//刚开始触摸屏幕时的Y值 private int curState = 0;//当前header状态,默认为0
private final int NORMAL = 0;//正常状态
private final int PULL = 1;//状态下拉
private final int RELEASE = 2;//提示刷新状态
private final int RELEASING = 3;//状态正在刷新 private boolean canPull = false;//是否可以执行下拉操作 private refresfListener mListener;//回调接口 //三个构造方法都要重写
public MyListView(Context context) {
super(context);
initView( context); }
public MyListView(Context context, AttributeSet attrs) {
super(context, attrs);
initView( context); }
public MyListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initView( context); } //定义回调接口
public interface refresfListener{
void refresh();
}
public void setOnRefreshListener(refresfListener listener){
this.mListener = listener;
} public void initView(Context context){ header = LayoutInflater.from(context).inflate(R.layout.header, null);
notifyView(header);
headerHeight = header.getMeasuredHeight();//获取header的高度 // headerHeight = header.getHeight();
paddingTop(-headerHeight);
//将头布局加进去
this.addHeaderView(header); this.setOnScrollListener(this);
} /**
* 该方法为通知父布局,子布局view的宽度和高度
* @param view:子布局
*/
private void notifyView(View view){ ViewGroup.LayoutParams p = view.getLayoutParams(); if(p == null){
p = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); } //spec表示当前子view左右边距,padding表示子view的左右内边距
//childDimension:子view的宽度
int width = ViewGroup.getChildMeasureSpec(0, 0, p.width); int height;
int tempHeight = p.height;
if(tempHeight>0){
//子布局高度不为空,需要填充这个布局
height = MeasureSpec.makeMeasureSpec(tempHeight, MeasureSpec.EXACTLY);
}else{
//高度为0,则不需要填充
height = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
} //然后告诉父布局,子布局的高度和宽度
view.measure(width, height);
} //该方法设定header的paddingTop
private void paddingTop(int pt){
header.setPadding(header.getPaddingLeft(), pt, header.getPaddingRight(), header.getPaddingBottom());
header.invalidate();
} /***
* 监听当前滚动状态
* scrollState:当前滚动状态
*/
public void onScrollStateChanged(AbsListView view, int scrollState) {
//记录当前的滚动状态
this.scrollState = scrollState; } /***
* 监听当前滚动的item
* firstVisibleItem:当前可见的第一个item
* visibleItemCount:当前共有多少个item可见
* totalItemCount:总共有多少个item
*
*/
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) { this.firstVisibleItem = firstVisibleItem; } //触屏事件
public boolean onTouchEvent(MotionEvent ev) { switch(ev.getAction()){
//手指落到屏幕上时
case MotionEvent.ACTION_DOWN:
//如果当前可见的第一个item为第0号,说明ListView位于顶端,可以执行下拉刷新
if(firstVisibleItem == 0){
canPull = true;
startY = (int) ev.getY();
} break;
//手指在屏幕上拖动时
case MotionEvent.ACTION_MOVE:
if(canPull){
touchMove(ev);
}
break;
//手指离开屏幕时
case MotionEvent.ACTION_UP:
canPull = false;
if(curState == RELEASE){
curState = RELEASING;
refreshHeaderByState();
//这里添加刷新数据的逻辑
mListener.refresh();
}else{
curState = NORMAL;
refreshHeaderByState();
paddingTop(-headerHeight);
} break;
} return super.onTouchEvent(ev);
} /**
* 该方法根据触摸屏幕滑动来改变STATE,即改变当前状态
* @param ev
*/
private void touchMove(MotionEvent ev) { int tempY = (int) ev.getY();
int space = tempY -startY;//移动的距离
int topdding = space-headerHeight;
paddingTop(topdding);//即时设定头布局的隐藏高度
if(space>headerHeight&&space<headerHeight+50&&scrollState == SCROLL_STATE_TOUCH_SCROLL){
curState = PULL;//设定为下拉状态
refreshHeaderByState();
}
if(space>headerHeight+50){
curState = RELEASE;//设定为提示刷新状态
refreshHeaderByState();
} if(space<headerHeight){
curState = NORMAL;//设定为正常状态
refreshHeaderByState();
} } /**
* 根据当前状态更改header的显示界面
*
*/
private void refreshHeaderByState( ){
ProgressBar pb = (ProgressBar) header.findViewById(R.id.progress_bar);
ImageView img = (ImageView) header.findViewById(R.id.img_arrow);
TextView tv = (TextView) header.findViewById(R.id.textinfo); switch(curState){ case NORMAL:
pb.setVisibility(View.GONE);
img.setVisibility(View.VISIBLE);
img.setImageResource(R.drawable.down_arrow);
tv.setText("下拉刷新");
break;
case PULL:
pb.setVisibility(View.GONE);
img.setVisibility(View.VISIBLE);
img.setImageResource(R.drawable.down_arrow);
tv.setText("下拉刷新");
break;
case RELEASE:
pb.setVisibility(View.GONE);
img.setVisibility(View.VISIBLE);
img.setImageResource(R.drawable.up_arrow);
tv.setText("松开刷新");
break;
case RELEASING:
pb.setVisibility(View.VISIBLE);
img.setVisibility(View.GONE);
tv.setText("正在刷新");
break; } } //数据刷新完成后的操作
public void refreshFinish(){ curState = NORMAL;
paddingTop(-headerHeight);
refreshHeaderByState();
} }

接下来就是MainActivity中的代码了。如下:

 package com.fuly.load;

 import java.util.ArrayList;
import java.util.List; import com.fuly.load.MyListView.refresfListener; import android.os.Bundle;
import android.os.Handler;
import android.app.Activity; public class MainActivity extends Activity implements refresfListener{ private MyListView lv;
private List<MyData> mDatas = new ArrayList<MyData>();
private MyAdapter mAdapter; protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); initData();//该方法初始化数据
lv = (MyListView) findViewById(R.id.list_view);
lv.setOnRefreshListener(this);//设定回调接口
mAdapter = new MyAdapter(this, mDatas);
lv.setAdapter(mAdapter); } /**
* 该方法初始化数据,即提供初始的素材
*/
private void initData() {
for(int i = 0;i<12;i++){
MyData md = new MyData("你好,我是提前设定的");
mDatas.add(md);
} }
/**
* 提供刷新数据
*/
private void getRefreshData() {
for(int i = 0;i<3;i++){
MyData md = new MyData("你好,我是刷新进来的");
mDatas.add(i, md);
} } //重写回调方法
public void refresh() { //在这里之所以使用Handler,是想让操作延迟,这样子效果看起来更
//清晰,实际项目中,是 不需要的
Handler mHandler = new Handler();
mHandler.postDelayed(new Runnable(){ @Override
public void run() {
//获得刷新数据
getRefreshData();
//刷新ListView
mAdapter.notifyDataSetChanged();
//lv.setSelection(mDatas.size()-1); //刷新后
lv.refreshFinish(); } }, 5000 ); }
}

好了,快快运行下程序,体验下拉刷新的效果吧。至此,ListView实现下拉刷新,我们讲解完毕了。

ListView实现下拉刷新(三)实现下拉刷新的更多相关文章

  1. Android 自定义ListView实现底部分页刷新与顶部下拉刷新,androidlistview

    在项目开发中,由于数据过大时,需要进行分页加载或下拉刷新,来缓解一次性加载的过长等待.本篇博文实例讲解通过自定义的ListView实现底部分页加载和顶部下拉刷新的效果. 其效果图: 一.ListVie ...

  2. 你必须了解的RecyclerView的五大开源项目-解决上拉加载、下拉刷新和添加Header、Footer等问题

    前段时间做项目由于采用的MD设计,所以必须要使用RecyclerView全面代替ListView.但是开发中遇到了需要实现RecyclerView上拉加载.下拉刷新和添加Header以及Footer等 ...

  3. 定位和xml解析和gson解析加上拉加载,下拉刷新

    这里的上拉加载,下拉刷新用到是依赖包 Mainactivity,xml解析和定位 package com.exmple.autolayout; import java.util.List; impor ...

  4. PullToRefreshScrollView的上拉加载、下拉刷新

    eclipse中的项目: //注意:此刷新功能是使用的第三方的PullToRefreshScrollView,因此需要导入第三方library作为依赖 步骤:导入第三方library,依赖:点击你的应 ...

  5. PullToRefreshListView上拉加载、下拉刷新

    说明:此项目使用studio完成的.需要导入library作为依赖,使用了xuitls获得网络请求.使用Pull解析了XML eclipse中的项目: //注意:此刷新功能是使用的第三方的PullTo ...

  6. Vue-上拉加载与下拉刷新(mint-ui:loadmore)一个页面使用多个上拉加载后冲突问题

    所遇问题: 该页面为双选项卡联动,四个部分都需要上拉加载和下拉刷新功能,使用的mint-ui的loadmore插件,分别加上上拉加载后,只有最后一个的this.$refs.loadmore.onTop ...

  7. C#构造方法(函数) C#方法重载 C#字段和属性 MUI实现上拉加载和下拉刷新 SVN常用功能介绍(二) SVN常用功能介绍(一) ASP.NET常用内置对象之——Server sql server——子查询 C#接口 字符串的本质 AJAX原生JavaScript写法

    C#构造方法(函数)   一.概括 1.通常创建一个对象的方法如图: 通过  Student tom = new Student(); 创建tom对象,这种创建实例的形式被称为构造方法. 简述:用来初 ...

  8. vue使用vant-ui实现上拉加载、下拉刷新和返回顶部

    vue使用vant-ui实现上拉加载.下拉刷新和返回顶部 vue现在在移动端常用的ui库有vant-ui和mint-ui,上拉加载.下拉刷新和返回顶部也是移动端最基础最常见的功能.下面就用vant-u ...

  9. 使用jquery结合ajax做下拉刷新页面,上拉加载页面,俗称分页

    jquery结合iscroll.js做下拉刷新页面,上拉加载页面 先上代码,里面都有注释这就不一一说明了 <!DOCTYPE html> <html lang="en&qu ...

  10. android 支持上拉加载,下拉刷新的列表控件SwipeRefreshLayout的二次封装

    上拉加载,下拉刷新的列表控件,大家一定都封装过,或者使用过 源代码,我会在最后贴出来 这篇代码主要是为了解决两个问题 1.滑动冲突得问题 2.listview无数据时,无数据布局的展示问题 下方列出的 ...

随机推荐

  1. Redis 小结

    一.redis简介 redis是一款基于C语言编写的,开源的非关系型数据库,由于其卓越的数据处理机制(按照规则,将常用的部分数据放置缓存,其余数据序列化到硬盘),大家也通常将其当做缓存服务器来使用. ...

  2. 使用Charles为Android设备抓取https请求的包

    之前开发的Android APP使用的都是http请求,之后改成了https,就出现了以下情况,无法正常读取抓取的内容 找了好多资料说法大概差不多,照着弄,结果出现如下情况,后来发现这种情况其实是手机 ...

  3. C++命名空间使用代码

    namesp.h #pragma once #include <string> namespace pers { using namespace std; struct Person { ...

  4. ASP.NET MVC4 新手入门教程之九 ---9.查询详情和删除方法

    在本教程的这一部分,您会检查自动生成的Details和Delete方法. 检查详细信息和删除方法 打开Movie控制器并检查的Details的方法. public ActionResult Detai ...

  5. Consul 遇到的坑

    均衡负载时调用的地址 spring.cloud.consul.discovery.service-name= 当A服务调用B服务时,可以转发到注册中心进行转发调用, 应该使用这个地址,这一点和eure ...

  6. nodejs的jsonrpc调用

    记录下使用nodejs发送jsonrpc请求: var express = require('express'); var router = express.Router(); var request ...

  7. JQ(查找)

    1.由下级到上级再到下级 var aa=$("td:eq(0)").parents("tr").find("td:eq(1)"); 2.

  8. [android] 切换界面的通用处理

    实现不改变activity,只切换View 抽取View界面的基类 利用面向对象多态的思路,实现通用 TitleManager.java 管理标题 package com.tsh.lottery.vi ...

  9. VS编译完成后自动复制到远程机器

    缘起 最近在调试网络通信,每次一有点小修改,都要将程序从开发机复制到测试机,不胜烦扰.既然我们程序猿,为什么要那么死板呢,能够用代码解决的问题,就不要用手去解决. 解决过程 复制 手工复制外有没有其他 ...

  10. Tips——单页面内的多重跳转路由使用

    一.问题背景 一个路由往往代表一个地址,即一个页面.但同级网页页面的内容有很多是重复的,如果每次加载页面都要加载这些“共有”内容,会导致效率的降低.因此,单页面应用应运而生.它主张在同一页面下将“共同 ...