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 用于重复刷新
}
}
xml的代码为:
<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,手势动作有三个:手势按下,手势移动,手势抬起。
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 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();
}
}); } }
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 lists = new ArrayList<>();
private Context context;
public MyAdapter(Context context, List 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
}
}
源码下载地址:安卓开发技术之下拉刷新的实现