Android开发技术学习之下拉刷新功能的实现

来源:互联网 发布:openwrt 源码 多大 编辑:程序博客网 时间:2024/05/21 19:45

Android开发技术学习之下拉刷新功能的实现

  • 好久没有写博客了,最近都在忙。有时候即使是有时间也会很懒,就会想玩一玩,放松放松!一直都没有什么时间更新我这个菜鸟的博客了。不过今天不一样,我要给大家讲讲怎么实现许多app中下拉刷新的功能。比如腾讯的QQ、新浪微博等等。为什么我会写这篇技术博客,是因为我热爱技术,平时喜欢学习一些比较好玩的demo。故而有了这篇技术博客的诞生。好了,废话不多说了,下面开始进入正题吧!
  • 下拉刷新的功能,相信大家一定都接触过。比如一个界面上显示了一些内容,你只要按住手机的屏幕下拉一定的距离就可以实现刷新的功能,当然你也可以下拉一段距离后抬起你的手,此时就不会刷新了,继续回到界面上。
  • 首先需要你新建一个项目,默认有MainActivity和activity_main.xml布局。且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图片:
  • 初始界面
  • 下拉刷新手势移动
  • 松开刷新
  • 正在刷新
  • 完成刷新
  • -

每天进步一点点!加油!

源码下载地址:安卓开发技术之下拉刷新的实现

0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 想从新西兰寄红酒到家运费怎么办 新买的裤子掉色把内裤染色了怎么办 白衣服变脏变黄怎么办用英文怎么说 经常宅在家里不爱出去玩怎么办 在家里呆不住老想着往外跑怎么办 新商盟网上订烟登录密码忘了怎么办 在拼多多购物质量有问题怎么办 美图手机总弹出一键加速广告怎么办 手机看小说总是喜欢弹出广告怎么办 美团外卖商家注册门头不合格怎么办 手机一打网页就自动弹出广告怎么办 美团预定的宾馆不可取消怎么办 公积金注册时的号码忘了怎么办 苹果7基带坏了修不好怎么办 苹果手机玩终结者2卡怎么办 支付宝转账转到邮箱忘记邮箱怎么办 朋友转账到我的邮箱支付宝怎么办 苹果账号密码和手机号码忘了怎么办 网上购物付款后商家不发货怎么办 夏商国际商城买到假货怎么办 重庆时时彩突然冻结账户资金怎么办 微信安全中心打不开是白色的怎么办 在淘宝买东西说好返现不给返怎么办 联通斐讯路由器返现被骗了怎么办 消费分期后退款分期账单还在怎么办 新房装修物业电梯用不了费用怎么办 在京东拼购没有拼成已付款的怎么办 微信上买东西发的货不一样怎么办 微信买东西收到货不付款怎么办 京东商品店家待出库不发货怎么办 微信二维码付款多付了怎么办 微信二维码付款付错了怎么办 身份证被移动公司拉黑了怎么办 微店申请退款卖家不同意怎么办 淘宝退货快递把我名字填错了怎么办 刚申请淘宝店信用为零怎么办 淘宝买家已付款卖家不做皮单怎么办 淘宝店铺被屏蔽7天后该怎么办 香信得登录密码忘了怎么办 苹果手机迅雷下载不了的资源怎么办 快手官方私信你的作品违规了怎么办