Android开发技术学习之下拉刷新功能的实现
来源:互联网 发布:openwrt 源码 多大 编辑:程序博客网 时间:2024/05/21 19:45
Android开发技术学习之下拉刷新功能的实现
- 好久没有写博客了,最近都在忙。有时候即使是有时间也会很懒,就会想玩一玩,放松放松!一直都没有什么时间更新我这个菜鸟的博客了。不过今天不一样,我要给大家讲讲怎么实现许多app中下拉刷新的功能。比如腾讯的QQ、新浪微博等等。为什么我会写这篇技术博客,是因为我热爱技术,平时喜欢学习一些比较好玩的demo。故而有了这篇技术博客的诞生。好了,废话不多说了,下面开始进入正题吧!
- 下拉刷新的功能,相信大家一定都接触过。比如一个界面上显示了一些内容,你只要按住手机的屏幕下拉一定的距离就可以实现刷新的功能,当然你也可以下拉一段距离后抬起你的手,此时就不会刷新了,继续回到界面上。
- 首先需要你新建一个项目,默认有MainActivity和activity_main.xml布局。且activity_main.xml布局如下所示:
- 该布局的代码很简单,就引用到自定义的一个view,在项目中的名字叫做MyListView,该类继承了ListView,需要重写构造方法。MyListView类的代码如下所示: -
package com.example.zq.pullfresh;import android.content.Context;import android.util.AttributeSet;import android.view.MotionEvent;import android.view.View;import android.widget.ImageView;import android.widget.ListView;import android.widget.ProgressBar;import android.widget.TextView;import java.text.SimpleDateFormat;import java.util.Date;/** * Created by zq on 2016/4/26. */public class MyListView extends ListView { private final View viewHeader; private static final byte DONE = 1; private static final byte PULL = 2; private static final byte RELEASE = 3; private static final byte RELEASE_NOT_FRESHING = 4; private byte currentState; private final int height; private TextView tvState, tvTime; private ImageView ivArrow; private ProgressBar pBar; private int downY, moveY, showY; private onRefreshingListener onRefreshingListener; // TODO: 2016/4/26 构造方法 public MyListView(Context context, AttributeSet attrs) { super(context, attrs); // TODO: 2016/4/26 构建下拉刷新的布局 viewHeader = View.inflate(context, R.layout.layout_header, null); setViews(viewHeader);// TODO: 2016/4/26 控件初始化 this.addHeaderView(viewHeader);// TODO: 2016/4/26 添加头部刷新布局 viewHeader.measure(0, 0);// TODO: 2016/4/26 精确测量 height = viewHeader.getMeasuredHeight();// TODO: 2016/4/26 获取布局的高度 viewHeader.setPadding(0, -height, 0, 0);// TODO: 2016/4/26 隐藏刷新布局 String strCurrentDate = GetCurrentDate(); tvTime.setText(strCurrentDate);// TODO: 2016/4/26 设置系统当前时间 currentState = DONE; } private void setViews(View viewHeader) { tvState = (TextView) viewHeader.findViewById(R.id.tv_state);// TODO: 2016/4/26 下拉状态 tvTime = (TextView) viewHeader.findViewById(R.id.tv_updateTime);// TODO: 2016/4/26 刷新时间 ivArrow = (ImageView) viewHeader.findViewById(R.id.iv_arrow);// TODO: 2016/4/26 下拉时箭头 pBar = (ProgressBar) viewHeader.findViewById(R.id.progressBar);// TODO: 2016/4/26 进度条 } @Override public boolean onTouchEvent(MotionEvent ev) { int action = ev.getAction(); switch (action) { case MotionEvent.ACTION_DOWN:// TODO: 2016/4/26 手势按下 if (currentState == DONE) { currentState = PULL; downY = (int) ev.getY();// TODO: 2016/4/26 获取按下时的Y坐标 } break; case MotionEvent.ACTION_MOVE:// TODO: 2016/4/27 手势移动 if (currentState == PULL) { moveY = (int) ev.getY();// TODO: 2016/4/26 移动时的Y坐标 showY = moveY - downY + (-height);// TODO: 2016/4/26 获取下拉显示的长度 viewHeader.setPadding(0, showY, 0, 0);//// TODO: 2016/4/26 下拉的时候慢慢的将布局显示出来 // TODO: 2016/4/26 判断向下拉了很久 if (showY > height) { ivArrow.setImageResource(R.mipmap.ic_launcher);// TODO: 2016/4/26 将下拉箭头设置为上拉箭头 tvState.setText("松开刷新"); currentState = RELEASE; } } break; case MotionEvent.ACTION_UP:// TODO: 2016/4/27 手势抬起 // TODO: 2016/4/26 下拉了一段距离但是没有刷新的情况 if (showY < height) { ivArrow.setImageResource(R.drawable.arrow);// TODO: 2016/4/26 设置下拉箭头 ivArrow.setVisibility(View.VISIBLE);// TODO: 2016/4/26 设置下拉箭头可见 pBar.setVisibility(View.GONE); tvState.setText("下拉刷新"); viewHeader.setPadding(0, -height, 0, 0);// TODO: 2016/4/26 隐藏下拉刷新布局 currentState = DONE; } if (currentState == RELEASE) { tvState.setText("正在刷新"); pBar.setVisibility(View.VISIBLE); ivArrow.setVisibility(View.GONE); String strCurrentDate = GetCurrentDate(); tvTime.setText(strCurrentDate);// TODO: 2016/4/26 设置刷新时间,为系统当前时间 // TODO: 2016/4/26 比如联网获取数据,可以让别人调接口 if (onRefreshingListener != null) { // TODO: 2016/4/27 主界面回调该方法,但在主界面对该方法做具体的实现 this.onRefreshingListener.onRefreshing(this); } } break; } return super.onTouchEvent(ev); } // TODO: 2016/4/26 获取系统当前的日期 格式为:yyyy-MM-dd HH:mm:ss private String GetCurrentDate() { SimpleDateFormat format = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss"); Date currentData = new Date(System.currentTimeMillis());//// TODO: 2016/4/26 获取系统当前时间 String strDate = format.format(currentData);// TODO: 2016/4/26 格式化系统当前时间 return strDate;// TODO: 2016/4/26 返回格式化后的日期 } // TODO: 2016/4/26 接收接口实现类 public void setOnRefreshingListener(onRefreshingListener onRefreshingListener) { this.onRefreshingListener = onRefreshingListener; } public void completeRefresh() { ivArrow.setImageResource(R.drawable.arrow); ivArrow.setVisibility(VISIBLE); pBar.setVisibility(View.GONE); tvState.setText("下拉刷新"); viewHeader.setPadding(0, -height, 0, 0);// TODO: 2016/4/26 隐藏下拉刷新布局 currentState = DONE;// TODO: 2016/4/26 用于重复刷新 }}
- 代码很简单,就是自定义了一个view,MyListView继承ListView,重写构造方法。在构造方法中,构建下拉刷新的布局,在代码中构建的布局为viewHeader,即下拉刷新的布局;下拉刷新的xml布局如下所示:
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="wrap_content" android:background="#00000000" android:orientation="horizontal"> <!-- 内容 --> <RelativeLayout android:id="@+id/head_contentLayout" android:layout_width="match_parent" android:layout_height="wrap_content" android:paddingLeft="30dp"> <!-- 箭头图像、进度条 --> <FrameLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentLeft="true" android:layout_centerVertical="true"> <!-- 箭头 --> <ImageView android:id="@+id/iv_arrow" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:src="@drawable/arrow" /> <!-- 进度条 --> <ProgressBar android:id="@+id/progressBar" style="?android:attr/progressBarStyleSmall" android:layout_width="50dp" android:layout_height="50dp" android:layout_gravity="center" android:visibility="gone" /> </FrameLayout> <!-- 提示、最近更新 --> <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerHorizontal="true" android:gravity="center" android:orientation="vertical"> <!-- 提示 --> <TextView android:id="@+id/tv_state" android:layout_width="wrap_content" android:layout_height="wrap_content" android:paddingTop="8dp" android:text="下拉刷新" android:textColor="#FF000000" android:textSize="20sp" /> <!-- 最近更新 --> <TextView android:id="@+id/tv_updateTime" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="上次更新" android:textColor="#FFFF0000" android:textSize="15sp" /> </LinearLayout> </RelativeLayout> </LinearLayout>
代码很简单,就是一个线性布局里面放置两个布局,两个布局再分别放置一些控件。其中第一个布局用的是帧布局,里面有一个下拉箭头的图片和一个进度条,此时需要将进度条隐藏,设置android:visibility=”gone”就可以了。第二个布局用的是一个线性布局,里面有两个TextView控件,一个是用于当前状态提示显示,另一个是用于最近更新提示显示。在MyListView类的构造方法中将下拉刷新布局构建好了之后,进行控件的一些初始化操作,就可以将该布局添加到头部了,this.addHeaderView(viewHeader);添加到头部的时候需要精确的测量布局的高度。高度测量完成后,设置padding,可以隐藏下拉刷新的布局,viewHeader.setPadding(0,-height, 0, 0);这里为什么要将top参数设置为-height,因为设置在头部需要将其隐藏。一些当前状态提示和时间提示的赋值我就不多说了,大家都看的懂,只要给对应的控件设置你需要让其显示的内容就可以了。下面开始关键的代码讲解:在MyListView类中重写了屏幕的触摸事件,为什么要重写该方法,因为下拉刷新这个功能是对手势做判断,执行相应操作实现的。如图方法:
即重写
public boolean onTouchEvent(MotionEvent ev) { ......
方法。
return super.onTouchEvent(ev); }在该方法中,首先获取手势动作action,手势动作有三个:手势按下,手势移动,手势抬起。
- 当手势按下的时候,currentState赋值为PULL,获取按下时的Y坐标。currentState是用于在不同的手势之间切换的一个参数标记。此时按下就执行以上操作就可以。
- 接下来需要下拉移动了,手势动作也相应的变成了手势移动。在手势移动的时候,判断currentState是否为PULL,为PULL的话,就获取移动时的Y坐标,根据手势按下时的Y坐标和移动时的Y坐标获取下拉显示的长度showY。获取的代码是:showY = moveY - downY + (-height);将下拉的布局慢慢显示出来就设置padding的top为当前获取的下拉显示的长度。这样你移动的时候就可以慢慢的显示出来了。这时再判断是否向下下拉了很久,如果是的话,就将下拉箭头ivArrow设置为上拉箭头,由于没有上拉箭头的图片资源,我就用ic_launcher代替了。当前状态显示提示tvState就设置为“松开刷新”,最后将currentState赋值为RELEASE。
- 手势移动就结束了,此时手势抬起的动作就来了,手势抬起的时候分两种情况。第一种下拉一段距离但是没有刷新的情况;另一种就是释放刷新的情况。下拉一段距离但没有刷新的情况用条件”showY < heght”来判断,如果该条件成立,就说明你此时你下还在下拉,下拉没有结束,此时松开,即手势抬起的时候,下拉刷新布局就会再一次的隐藏。有人会问这里为什么用showY < 0来做判断?当布局刚好全部显示出来的时候就提示你可以刷新了不是更好吗?如果你这样提问,恭喜你,我为什么这样写你应该懂了,我只是让其下拉的距离再长一点再执行刷新操作。只要是大于0以上的数值就可以,看你个人的喜好了。
- 而释放刷新的情况用条件currentState == RELEASE来判断,当此条件成立,即手势移动结束后(手势移动时下拉了足够长的距离),给当前状态显示提示tvState设置为“正在刷新”,进度条设置为显示,下拉箭头设置为不可见。当前刷新时间tvTime设置为系统当前时间,这个很简单,代码有注释,大家应该都能懂。接下来就是获取数据的时候了,刷新是为了干什么,是为了获取一些网络上的数据。关键的地方来了,这里你可以写个接口,让别人回调你接口的方法,在别人那里对该方法做具体的实现。给大家说具体点吧!可能有的人不是很懂。
- 第一步:创建一个接口,如下图所示:
- 第二步:在MyListView类写一个方法用于接收接口实现类,方法名称为:setOnRefreshingListener(onRefreshingListener onRefreshingListener)该方法用于接收一个接口参数,也就是刚刚定义的接口。如下图所示: *
- 第三步:在释放刷新,比如联网获取数据,可以让别人调你的接口,如下图所示: *
- 第四步:在MainActivity中用MyListView对象调用接收接口实现类方法,当调用该方法后需要执行接口中的onRefreshingListener.onRefreshing()方法,也就相当于在MainActivity中回调了第三步当中提到的方法this.RefreshingListener.onRefreshing(this)。但是具体的操作还是在MainActivity中实现。这其实也就是android中接口的回调机制,如果还不是很懂的就自己去百度“接口回调机制 ”,弄清楚它。
- 在MainActivity中具体的实现方式如下图所示:
- 以上图片中我标记了4处重要的地方,第一处是让线程休眠1s,模拟获取数据的操作;第二处是往集合中添加获取的数据,我这里也只用了简单的添加方式;第三处是adapter更新数据源的变化,因为往集合中添加了数据,所以执行该方法可以更新adapter中内容。但是更新UI的操作需要执行在主线程,故runOnUiThread(…);第四处是执行方法myListView.completeRefresh();用于完成数据的刷新操作。此时又回到了MyListView这个类中,在该类中去实现该方法。主要是联网完成了,隐藏下拉布局。如下图所示:
- 代码很简单,就是一些图片的显示与进度条的隐藏,布局的设置padding等,这里就不多说了。代码里都有给出。
- MainActivity中的代码如下所示:
package com.example.zq.pullfresh;import android.support.v7.app.AppCompatActivity; import android.os.Bundle;import java.util.ArrayList; import java.util.List; import java.util.Random;public class MainActivity extends AppCompatActivity {private List<String> lists = new ArrayList<>();// TODO: 2016/4/26 声明一个集合保存数据 private MyListView myListView;@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // TODO: 2016/4/26 设置该Activity的使用布局 setContentView(R.layout.activity_main); lists.add("a");// TODO: 2016/4/26 象征性的添加几个数据 lists.add("b"); lists.add("c"); // TODO: 2016/4/26 创建adapter final MyAdapter adapter = new MyAdapter(MainActivity.this, lists); // TODO: 2016/4/26 获取自定义的listView myListView = (MyListView)findViewById(R.id.myListView); // TODO: 2016/4/26 设置adapter myListView.setAdapter(adapter); myListView.setOnRefreshingListener(new onRefreshingListener() { @Override public void onRefreshing(final MyListView myListView) { new Thread(){ @Override public void run() { super.run(); try { Thread.sleep(1000); lists.add(new Random().nextInt(100)+"联网获取的数据"); // TODO: 2016/4/26 主线程执行更新UI操作必须在主线程 runOnUiThread(new Runnable() { @Override public void run() { adapter.notifyDataSetChanged(); // TODO: 2016/4/26 联网完成了,隐藏下拉刷新布局 myListView.completeRefresh(); } }); } catch (InterruptedException e) { e.printStackTrace(); } } }.start(); } }); } }
- 代码很简单,获取MyListVIew对象,给对象设置adapter。这样就可以了。获取adapter只需要创建MyAdapter类的对象可以获取。MyAdapter类代码如下:
package com.example.zq.pullfresh;import android.content.Context;import android.view.View;import android.view.ViewGroup;import android.widget.BaseAdapter;import android.widget.TextView;import java.util.ArrayList;import java.util.List;/** * Created by zq on 2016/4/26. */public class MyAdapter extends BaseAdapter { private List<String> lists = new ArrayList<>(); private Context context; public MyAdapter(Context context, List<String> lists) { this.lists = lists; this.context = context; } @Override public int getCount() { return lists.size(); } @Override public Object getItem(int position) { return lists.get(position); } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { TextView tvData = new TextView(context);// TODO: 2016/4/26 新建一个TextView tvData.setText(lists.get(position));// TODO: 2016/4/26 设置数据 tvData.setTextAlignment(View.TEXT_ALIGNMENT_CENTER);// TODO: 2016/4/26 设置textView在listView中的字体显示位置 return tvData;// TODO: 2016/4/26 返回tvData }}
- 代码简单,继承BaseAdapter,重写相关方法,getView()方法中新建一个TextView,设置数据,返回TextView对象即可。
- 好了,到这里就结束了。
- 最后给大家显示一些演示的demo图片: -
每天进步一点点!加油!
源码下载地址:安卓开发技术之下拉刷新的实现
- Android开发技术学习之下拉刷新功能的实现
- Android 开发之下拉刷新+ViewPager的banner滚动
- Android开发之下拉刷新和上拉加载框架PullToRefresh的详谈
- Xamarin.Android之下拉刷新
- Xamarin.Android之下拉刷新
- Android学习系列(30)--App列表之下拉刷新
- android 5.0新特性学习总结之下拉刷新(一)
- Android学习系列(24)--App列表之下拉刷新
- Android之下拉与上拉刷新
- RecyclerView之下拉刷新、下拉加载的实现
- UI实现之下拉刷新:SwipeRefreshLayout PullToRefres
- android中listview之下拉刷新
- Android自定义控件之下拉刷新;
- Android 之下拉刷新控件XRefreshableView
- android学习笔记之下拉动态弹出图标菜单的实现
- 微信小程序开发之下拉刷新 上拉加载
- 微信小程序开发之下拉刷新 上拉加载
- 012 - 微信小程序开发之下拉刷新 上拉加载
- C#反射机制详解
- tee 日志 显示 写文件
- maven依赖问题
- Java常用排序
- 鸽巢原理
- Android开发技术学习之下拉刷新功能的实现
- 关于Chrome浏览器加载某些CSS文件超慢或超时的奇葩问题的解决办法
- 二叉树的遍历算法
- rand()%N-----随机数的获取,51级单片机
- IAA32过程调用寄存器的保护规则
- opencv中测量运行时间的函数
- C/C++字符串
- vim配置及插件安装管理(超级详细)
- 深入理解JVM内部结构(续)