实现思路:
1、在ListView中添加header并在开始时隐藏,通过设置header的topPadding为负的header的高度,实现该效果
2、监听手势(用onTouchEvent),先判断ListView是否已经到达顶部,到达顶部以后根据滑动幅度(手指还没有抬起来)可分为两种状态,一种是幅度不够,则松开手指后LiveView恢复原样,一种是下拉幅度够了则更改header中的View信息,第三种状态是正在刷新,在松开手指时更新为该状态
3、ListView的滑动监听,如果在滑动时为状态3,则开启线程更新ListView中的数据(借助handler)
效果图:
header布局:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!--ListView的头部布局-->
<FrameLayout
android:id="@+id/fl"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingLeft="120dp">
<ImageView
android:id="@+id/refresh"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/pull_to_refresh_arrow" />
<ProgressBar
android:id="@+id/progress"
style="@style/Widget.AppCompat.ProgressBar"
android:layout_gravity="center"
android:max="100"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone" />
</FrameLayout>
<LinearLayout
android:id="@+id/ly"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_toRightOf="@id/fl"
android:gravity="center_horizontal"
android:orientation="vertical"
android:paddingBottom="10dp"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:paddingTop="10dp">
<TextView
android:id="@+id/tip"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:text="下拉刷新"
android:textSize="15sp" />
<TextView
android:id="@+id/update_last_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_horizontal" />
</LinearLayout>
</RelativeLayout>
mainActivity布局:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.example.he.sample.MainActivity">
<com.example.he.sample.myListView
android:id="@+id/lv"
android:layout_width="match_parent"
android:layout_height="wrap_content"></com.example.he.sample.myListView>
</RelativeLayout>
myListView:
/**
* header下拉时的三种种状态,UN_PREPARE表示滑动的幅度不够不需要刷新,PREPARE表示的含义是现在松手就能刷新,REFRESH表示正在刷新
*/
enum State {
UN_PREPARE, PREPARE, REFRESH
}
public class myListView extends ListView {
private View header;
private int headerHeight;
private int startY;
private boolean top;//用于判断是否在顶部滑动
private boolean firstState = true;//是否是第一次进行状态判断
private State state = State.UN_PREPARE;//滑动的状态,用于listView滑动事件的处理,见MainActivity.class
private TextView title;
private ImageView image;
private ProgressBar bar;
public myListView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView(context);
}
public myListView(Context context) {
super(context);
initView(context);
}
public myListView(Context context, AttributeSet attrs) {
super(context, attrs);
initView(context);
}
/**
* 加载header视图
*
* @param context
*/
private void initView(Context context) {
header = LayoutInflater.from(context).inflate(R.layout.head, null);
title = (TextView) header.findViewById(R.id.tip);
image = (ImageView) header.findViewById(R.id.refresh);
bar = (ProgressBar) header.findViewById(R.id.progress);
header.measure(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);//测量head
headerHeight = header.getMeasuredHeight();//获取高度
topPadding(-headerHeight);
this.addHeaderView(header);
}
/**
* 修改header的PaddingTop值达到隐藏header、随着滑动慢慢出现的效果
*
* @param topPadding
*/
protected void topPadding(int topPadding) {
header.setPadding(header.getPaddingLeft(), topPadding,
header.getPaddingRight(), header.getPaddingBottom());
header.invalidate();
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
int t = this.getFirstVisiblePosition();
//t为0表示到达屏幕顶部
if (t == 0) {
top = true;
startY = (int) ev.getY();
}
break;
case MotionEvent.ACTION_UP:
top = false;
firstState = true;
//在状态2时松手则切换到状态3的视图
if (state == State.PREPARE)
refreshView(State.REFRESH, 0);
//在状态1时松手则恢复原样
if (state == State.UN_PREPARE)
topPadding(-headerHeight);
break;
case MotionEvent.ACTION_MOVE:
if (top)
onMove(ev);
break;
}
return super.onTouchEvent(ev);
}
/**
* 手指正在滑动时进行状态判断
* @param event
*/
private void onMove(MotionEvent event) {
int lastY = (int) event.getY();
int space = lastY - startY;
int padding = space - headerHeight;
Log.i("padding", "" + padding);
//状态1
if (padding < 30) {
Log.i("state", "1");
if (firstState)
refreshView(State.UN_PREPARE, padding);
}
//状态2
else if (padding > 30 && padding < 100) {
firstState = false;
refreshView(State.PREPARE, padding);
}
}
/**
* 根据不同的状态,完成相应的操作
*/
private void refreshView(State s, int padding) {
switch (s) {
case UN_PREPARE:
topPadding(padding);
state = State.UN_PREPARE;//更新状态
bar.setVisibility(View.GONE);
image.setVisibility(View.VISIBLE);
title.setText("下拉刷新");
break;
case PREPARE:
image.setVisibility(View.VISIBLE);
bar.setVisibility(View.GONE);
topPadding(padding);
state = State.PREPARE;//更新状态
//更新header中的视图
/**
* 创建旋转动画达到向下的箭头朝上的目的,旋转180度,以自身为中心
*/
RotateAnimation animation = new RotateAnimation(0, 180, RotateAnimation.RELATIVE_TO_SELF, 0.5F, RotateAnimation.RELATIVE_TO_SELF, 0.5F);
animation.setDuration(0);//0毫秒内完成效果
animation.setFillAfter(true);//图片的显示为执行动画后的样子
image.startAnimation(animation);
title.setText("松手进行刷新");
topPadding(padding);
break;
case REFRESH:
image.setVisibility(View.GONE);//隐藏image
bar.setVisibility(View.VISIBLE);//显示bar
//更新header中的视图
image.clearAnimation();//清除image上的动画
title.setText("正在刷新");
state = State.REFRESH;//更新状态
break;
}
}
public int getHeaderHeight() {
return headerHeight;
}
public State getState() {
return state;
}
}
MainActivity:
public class MainActivity extends AppCompatActivity {
private static final int OK = 1;
private myListView listView;
private ArrayAdapter<Integer> adapter;
private int headerHeight;
private List<Integer> list;
private Random random = new Random(47);//随机数
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case OK:
adapter.notifyDataSetChanged();//刷新数据
listView.topPadding(-headerHeight);//隐藏header
break;
}
}
};
@Override
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
listView = (myListView) findViewById(R.id.lv);
headerHeight = listView.getHeaderHeight();
list = new ArrayList<Integer>();
initList();
adapter = new ArrayAdapter<Integer>(this, android.R.layout.simple_list_item_1, list);
listView.setAdapter(adapter);
/**
* listView的滑动监听
*/
listView.setOnScrollListener(new AbsListView.OnScrollListener() {
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
//正在刷新则刷新数据
if (listView.getState() == State.REFRESH) {
onRefresh();
}
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
}
});
}
private void initList() {
for (int i = 0; i < 50; i++) {
list.add(random.nextInt(50));
}
}
/**
* 更新数据
*/
private void onRefresh() {
new Thread(new Runnable() {
@Override
public void run() {
Message message = new Message();
message.what = OK;
for (int i : list) {
list.set(i, random.nextInt(50));
}
handler.sendMessageDelayed(message, 2000);//2s以后发送message
}
}).start();
}
}
源码地址:点击打开链接
在自己实现的下拉刷新的基础上添加了ListView的滑动删除,关于滑动删除的教程请看这篇文章:点击打开链接,但是这位博主的代码是不适合添加了header的ListView的,如果完全按文中代码加到我这个项目中会出现部分item无法滑动的问题,只需将文中的
//我们想知道当前点击了哪一行
int position = pointToPosition(x, y);
if (position != INVALID_POSITION) {
DataHolder data = (DataHolder) getItemAtPosition(position);
itemRoot = data.rootView;
}
修改为
int position = pointToPosition(x, y);
int firstPosition = getFirstVisiblePosition();
if (position != INVALID_POSITION) {
curView = (ScrollDeleteLinearLayout) getChildAt(position - firstPosition);
}
其实官方给我们提供了SwipeRefreshLayout可用于ListView的刷新,它已经帮我们把内部实现封装好了,Demo:点击打开链接
ListView的上拉加载更多和下拉刷新的代码基本上是一样的,只不过不是添加header而是添加footer,其他实现基本相同,有个要注意的地方是,由于ListView添加数据能直接显示出来,所以添加的位置要做变动否则看不出效果
下拉加载更多的Demo:点击打开链接