有四個步驟:
1.添加提示界面,即ListView的header頭布局
2.監(jiān)聽listView滾動事件拾弃,onScrollListenter()事件
3.監(jiān)聽ListView的onTouch()事件
4.加載最新數(shù)據(jù)
------------------效果圖----------------------
先創(chuàng)建一個RefreshLivstView類
----------------代碼如下------------
···
package com.example.mylistviewrefresh;
import java.text.SimpleDateFormat;
import java.util.Date;
import com.example.mylistviewfresh.R;
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.RotateAnimation;
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 RefreshListView extends ListView implements OnScrollListener {
View header;// 頂部布局文件
int headerHeight;// 頂部布局文件的高度
int firstVisibleItem;//當(dāng)前第一個可見的item的位置;
int scrollState;// listview當(dāng)前滾動狀態(tài)摆霉;
boolean isRemark;// 標(biāo)記豪椿,當(dāng)前是在listview最頂端摁下的;
int startY;//摁下時的Y值
//定義狀態(tài)變量
int state;// 當(dāng)前的狀態(tài)
final int NONE = 0;//正常狀態(tài)
final int PULL = 1;//提示下拉狀態(tài)
final int RELEASE = 2;// 提示釋放狀態(tài)携栋;
final int REFRESHING = 3;// 刷新狀態(tài)
IRefreshListener iRefreshListener;//刷新數(shù)據(jù)的接口
public RefreshListView(Context context) {
super(context);
initView(context);
}
public RefreshListView(Context context, AttributeSet attrs) {
super(context, attrs);
initView(context);
}
public RefreshListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initView(context);
}
/**
* 初始化界面搭盾,添加頂部布局文件到 listview
*
*/
private void initView(Context context) {
//通過LayoutInflater獲取頂部布局
LayoutInflater inflater = LayoutInflater.from(context);
header = inflater.inflate(R.layout.header, null);
measureView(header);//measure測量的方法
headerHeight = header.getMeasuredHeight();
Log.i("tag", "headerHeight = " + headerHeight);
topPadding(-headerHeight);
this.addHeaderView(header);//添加頂部布局文件
this.setOnScrollListener(this);
}
/**
* 通知父布局,占用的寬婉支,高鸯隅;
*
* @param view
*/
private void measureView(View view) {
ViewGroup.LayoutParams p = view.getLayoutParams();
if (p == null) {
p = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
}
//寬度
int width = ViewGroup.getChildMeasureSpec(0, 0, p.width);
int height;
int tempHeight = p.height;
if (tempHeight > 0) {
height = MeasureSpec.makeMeasureSpec(tempHeight,
MeasureSpec.EXACTLY);
} else {//高度為空
height = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
}
view.measure(width, height);
}
/**
* 設(shè)置header布局 上邊距;
*
*/
private void topPadding(int topPadding) {
header.setPadding(header.getPaddingLeft(), topPadding,
header.getPaddingRight(), header.getPaddingBottom());
header.invalidate();
}
@Override//fristVisibleItem 當(dāng)前界面第一個可見的item(他的位置在哪)
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
//fristVisibleItem是不是在最頂端
this.firstVisibleItem = firstVisibleItem;
}
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
this.scrollState = scrollState;
}
/**
對屏幕觸摸的監(jiān)控向挖,
* 先判斷當(dāng)前是否是在頂端蝌以。如果是在最頂端,記錄下你開始滑動的Y值
* 然后在滑動過程中(監(jiān)聽到的是ACTION_MOVE)何之,不斷地判斷當(dāng)前滑動的范圍是否到達應(yīng)該刷新的程度跟畅。
* (根據(jù)當(dāng)前的Y-之前的startY的值 與我們的控件的高度之間關(guān)系來判斷)
* 然后在監(jiān)聽到手指松開時,根據(jù)當(dāng)前的狀態(tài)(我們在onmove()中計算的)溶推,做相應(yīng)的操作徊件。
*/
@Override//監(jiān)聽
public boolean onTouchEvent(MotionEvent ev) {
// TODO Auto-generated method stub
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN://按下時
if (firstVisibleItem == 0) {//判斷是不是在最頂端,0是在最頂端
isRemark = true;
startY = (int) ev.getY();
}
break;
case MotionEvent.ACTION_MOVE://移動時
onMove(ev);
break;
case MotionEvent.ACTION_UP://抬起時
if (state == RELEASE) {//提示釋放狀態(tài)
//即提示松開刷新的狀態(tài)蒜危,一旦松開虱痕,進入到正在刷新;這時候就可以加載數(shù)據(jù)了辐赞!
state = REFRESHING;//刷新狀態(tài)
// 加載最新數(shù)據(jù)
refreshViewByState();
iRefreshListener.onRefresh();
} else if (state == PULL) {//如果是下拉狀態(tài)部翘,不會刷新數(shù)據(jù)
state = NONE;
isRemark = false;
refreshViewByState();
}
break;
}
return super.onTouchEvent(ev);
}
/**
判斷移動過程操作:
* 如果不是頂端,不需要做任何的操作
* 否則就獲取當(dāng)前的Y值占拍,與開始的Y值做比較略就。
* 判斷下拉的高度捎迫,與我們定義的一些臨界值做判斷(其實這個臨界值你可以自己定義)
* @param ev
*/
private void onMove(MotionEvent ev) {
if (!isRemark) {//是不是移動狀態(tài)
return;
}
int tempY = (int) ev.getY();//當(dāng)前移動的位置
int space = tempY - startY;//移動的距離是多少 = 移動的距離-開始標(biāo)記的距離
int topPadding = space - headerHeight;
//根據(jù)state當(dāng)前的狀態(tài) 改變state當(dāng)前的狀態(tài)值
switch (state) {
case NONE:
if (space > 0) {
state = PULL; ////正常狀態(tài)變?yōu)橄吕瓲顟B(tài)
refreshViewByState();
}
break;
case PULL:
topPadding(topPadding);
//如果大于一定高度,并且滾動狀態(tài)是正在滾動時表牢,就到了松開可以刷新的狀態(tài)
if (space > headerHeight + 30
&& scrollState == SCROLL_STATE_TOUCH_SCROLL) {
state = RELEASE;
refreshViewByState();
}
break;
case RELEASE:
topPadding(topPadding);
//在提示松開刷新時窄绒,如果你往上拖,距離小于一定高度時崔兴,提示下拉可以刷新
if (space < headerHeight + 30) {
state = PULL;
refreshViewByState();
}
break;
}
}
/**
* 根據(jù)當(dāng)前狀態(tài)彰导,改變界面顯示;
*/
private void refreshViewByState() {
TextView tip = (TextView) header.findViewById(R.id.tip);
ImageView arrow = (ImageView) header.findViewById(R.id.arrow);
ProgressBar progress = (ProgressBar) header.findViewById(R.id.progress);
//給箭頭添加一個動畫
RotateAnimation anim = new RotateAnimation(0, 180,
RotateAnimation.RELATIVE_TO_SELF, 0.5f,
RotateAnimation.RELATIVE_TO_SELF, 0.5f);
anim.setDuration(500);//500毫秒
anim.setFillAfter(true);
RotateAnimation anim1 = new RotateAnimation(180, 0,
RotateAnimation.RELATIVE_TO_SELF, 0.5f,
RotateAnimation.RELATIVE_TO_SELF, 0.5f);
anim1.setDuration(500);
anim1.setFillAfter(true);
switch (state) {
case NONE: //正常狀態(tài)不顯示
arrow.clearAnimation();
topPadding(-headerHeight);
break;
case PULL: //下拉狀態(tài)顯示箭頭敲茄,隱藏進度條位谋,以下的狀態(tài)也類似
arrow.setVisibility(View.VISIBLE);
progress.setVisibility(View.GONE);
tip.setText("下拉可以刷新");
arrow.clearAnimation();
arrow.setAnimation(anim1);
break;
case RELEASE:
arrow.setVisibility(View.VISIBLE);
progress.setVisibility(View.GONE);
tip.setText("松開可以刷新");
arrow.clearAnimation();
arrow.setAnimation(anim);
break;
case REFRESHING:
topPadding(50);//設(shè)置高度
arrow.setVisibility(View.GONE);//滾動條隱藏
progress.setVisibility(View.VISIBLE);//箭頭顯示
tip.setText("正在加載...");
arrow.clearAnimation();//移除動畫
break;
}
}
/**
*讀完數(shù)據(jù)后
*/
public void refreshComplete() {
state = NONE;
isRemark = false;
refreshViewByState();//刷新界面
TextView lastupdatetime = (TextView) header
.findViewById(R.id.lastupdate_time);
SimpleDateFormat format = new SimpleDateFormat("yyyy年MM月dd日 hh:mm:ss");
Date date = new Date(System.currentTimeMillis());
String time = format.format(date);
lastupdatetime.setText(time);
}
public void setInterface(IRefreshListener iRefreshListener){
this.iRefreshListener = iRefreshListener;
}
/**
* 刷新數(shù)據(jù)接口
* @author Administrator
*/
public interface IRefreshListener{
public void onRefresh();
}
}
···
-----在MainActivity中-------------
···
package com.example.mylistviewrefresh;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.example.mylistviewfresh.R;
import com.example.mylistviewrefresh.RefreshListView.IRefreshListener;
import android.os.Bundle;
import android.os.Handler;
import android.app.Activity;
import android.view.Menu;
import android.widget.SimpleAdapter;
/**
1.下拉刷新
-
2.自定義Aandroid控件 ,重寫ListView
- ScorllListener滾動監(jiān)聽
4.Adapter適配器
- ScorllListener滾動監(jiān)聽
-
*/
public class MainActivity extends Activity implements IRefreshListener {
private RefreshListView listView;
private SimpleAdapter simple_adapter;
private List<Map<String, Object>> list;@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
listView = (RefreshListView) findViewById(R.id.listview);
iniData(); //初始化數(shù)據(jù)堰燎,我們給它加20條Item
//設(shè)置SimpleAdapter監(jiān)聽器
/**
* SimpleAdapter的五個參數(shù)的含義:
* 第一個:context上下文
* 第二個:用于顯示的數(shù)據(jù)掏父,map的list
* 第三個:Item的布局,即我們自定義的那個文件
* 第四個:與第二個參數(shù)緊密聯(lián)系秆剪,與第五個緊密聯(lián)系赊淑,是在map中的鍵值
* 第五個:我們看到是id(int類型)的數(shù)組,
* 這個數(shù)組里的東西是哪里來的仅讽?是我們自己在布局文件中定義的
*/
simple_adapter = new SimpleAdapter(MainActivity.this, list,
R.layout.listview_item, new String[] { "image", "text" },
new int[] { R.id.image, R.id.text });
//設(shè)置適配器
listView.setAdapter(simple_adapter);listView.setInterface(this);
}
//初始化SimpleAdapter數(shù)據(jù)集
private List<Map<String, Object>> iniData() {
list = new ArrayList<Map<String, Object>>();
for (int i = 0; i < 20; i++) {
Map<String, Object> map = new HashMap<String, Object>();
//解釋下這里的數(shù)據(jù)陶缺,key對應(yīng)SimpleAdapter的第三個參數(shù),
//值對應(yīng)第五個參數(shù)洁灵,分別是圖片和文字
map.put("text", i);
map.put("image", R.drawable.ic_launcher);
list.add(map);
}return list;
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
//在動作欄中添加條目饱岸。
getMenuInflater().inflate(R.menu.main, menu);
return true;
}/**
- 接口回調(diào),在RefreshListView中可以調(diào)用此方法進行數(shù)據(jù)添加徽千。
*/
@Override
public void onRefresh() {
// //1.獲取最新數(shù)據(jù) 2.通知界面顯示數(shù)據(jù)3.通知listivew刷新數(shù)據(jù)
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
@Override
public void run() {
Map<String, Object> map = new HashMap<String, Object>();
map.put("text", "滾動添加");
map.put("image", R.drawable.ic_launcher);
list.add(0, map);
listView.setAdapter(simple_adapter);
simple_adapter.notifyDataSetChanged();
listView.refreshComplete();
}
}, 2000);
}
- 接口回調(diào),在RefreshListView中可以調(diào)用此方法進行數(shù)據(jù)添加徽千。
}
···
我們的布局 header.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:orientation="vertical" >
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="10dip"
android:paddingTop="10dip" >
<LinearLayout
android:id="@+id/layout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:gravity="center"
android:orientation="vertical" >
<TextView
android:id="@+id/tip"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="下拉可以刷新苫费!" />
<TextView
android:id="@+id/lastupdate_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
<ImageView
android:id="@+id/arrow"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toLeftOf="@id/layout"
android:layout_marginRight="20dip"
android:src="@drawable/pull_to_refresh_arrow" />
<ProgressBar
android:id="@+id/progress"
style="?android:attr/progressBarStyleSmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toLeftOf="@id/layout"
android:layout_marginRight="20dip"
android:visibility="gone" />
</RelativeLayout>
</LinearLayout>
···
listView.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="match_parent"
android:gravity="center_vertical"
android:orientation="horizontal" >
<ImageView
android:id="@+id/image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp" />
<TextView
android:id="@+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:textSize="25sp" />
</LinearLayout>
···
-------------activity.xml---------------
···
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity" >
<com.example.mylistviewrefresh.RefreshListView
android:id="@+id/listview"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:cacheColorHint="#00000000"
android:dividerHeight="5dip" />
</RelativeLayout>
···