這兩天突然想弄個下拉刷新的,於是就在網上搜索,找呀找,發現那些都好長,反正我是不怎麼看得懂,於是,我就到處尋,果不其然,被我弄懂了。下面我從我得角度向大家詳細如何實現的。
ListView 有 兩個方法, addFooterView(View view); 和 addHeaderView(View view); 直接傳一個佈局文件進去調用 。實現下拉刷新,其實就是檢測滑動事件來顯示或者隱藏頭尾佈局 . 下面我上全部代碼與大家一一解釋:
RefreListview.java
package com.qzzhu.refrelist;
import java.text.SimpleDateFormat;
import java.util.Date;
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.RotateAnimation;
import android.widget.AbsListView;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.AbsListView.OnScrollListener;
import android.widget.ProgressBar;
import android.widget.TextView;
public class RefreListview extends ListView implements OnScrollListener {
private View headview; //頭佈局
private View footview;//尾佈局
private Context context; //上下文
private int headHeight; //頭佈局高度
private int footheight; //尾佈局高度
private int downY; //觸摸事件 按下的Y值
private int mYfirstVisibleItem=0;//list顯示的頭item是第幾個item
private final int PULL_DOWN_REFRE=0;//下拉刷新
private final int RELEASE_REFRE = 1;//釋放刷新
private final int REFREING = 3;//刷新中
private int currentState = PULL_DOWN_REFRE;//當前的狀態
private ImageView headicon; //頭佈局裏面的空間 箭頭
private ProgressBar headpb; // 加載控件
private TextView headcontent; //頭佈局裏面的控件 文本
private TextView headrefretime;//頭佈局裏面的控件 最後更新事件
private Animation downAnimation; //下拉刷新動畫
private Animation upAnimation; //釋放刷新動畫
private OnRefreChangeListener changeListener; //接口
private boolean isMove = false; //是否移動
public RefreListview(Context context, AttributeSet attrs) {
super(context, attrs);
this.context = context;
//從這裏看起 實例化頭文件,把目光先放到該方法上面去
initHeadView();
initFootView();
initAnimation();
setOnScrollListener(this);
}
private void initFootView() {
footview = View.inflate(getContext(), R.layout.item_list_footview, null);
footview.measure(0, 0);
footheight = footview.getMeasuredHeight();
footview.setPadding(0, 0, 0, -footheight);
addFooterView(footview);
}
private void initAnimation() {
downAnimation = new RotateAnimation(0,-180,Animation.RELATIVE_TO_SELF,0.5f,Animation.RELATIVE_TO_SELF,0.5f);
downAnimation.setDuration(500);
downAnimation.setFillAfter(true);//結束保持動畫
upAnimation = new RotateAnimation(-180,-360,Animation.RELATIVE_TO_SELF,0.5f,Animation.RELATIVE_TO_SELF,0.5f);
upAnimation.setDuration(500);
upAnimation.setFillAfter(true);//結束保持動畫
}
private void initHeadView() {
//引入一個xml文件
headview = View.inflate(context, R.layout.item_list_headview, null);
//得到裏面的控件
headicon=(ImageView) headview.findViewById(R.id.head_icon);
headpb = (ProgressBar) headview.findViewById(R.id.head_pb);
headcontent = (TextView) headview.findViewById(R.id.head_tvcontent);
headrefretime = (TextView) headview.findViewById(R.id.head_refretime);
//調用這一句,我們就在listView裏面添加了我們自定義的頭佈局了。但是我們一開始不想它顯示的,我們應該把它向上移動頭佈局高度的距離。但是如何獲 取頭佈局的高度呢?
addHeaderView(headview);/* * 如果直接調用headview.getHeight();是拿不到高度的,這個前提條件是 界面顯示了,所以只能等到0*/
//要手動得到測量方法,只有通過這個方法,下面的高度才能拿到getMeasuredHeight()
//該方法是得到測量方法,
headview.measure(0, 0);
//調用getMeasuredHeight() 就能拿到當前頭佈局的高度了
headHeight =headview.getMeasuredHeight();
//接下來就是隱藏頭佈局了,
setPadding(<int left, int top,int right,int bottom)
// 我們是要向上移,所以就改變第二個參數了,因爲超出了屏幕,所以爲負值,0代表着剛好完全顯示,所以就向上隱藏它的高度了。
headview.setPadding(0, -headHeight, 0, 0);//這樣初始的時候就會隱藏頭佈局
/下面就開始監聽觸摸事件了
}
//複寫Touch事件
@Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
<span style="white-space:pre"> </span>//得到按下的位置,並做成全局變量
downY = (int) ev.getY();
break;
case MotionEvent.ACTION_MOVE:
if (currentState==REFREING) {//如果當前正在刷新的話,攔截當前事件,不讓它執行下面的方法
break;
}
int moveY = (int) ev.getY();//得到當前移動的Y值
int diff = moveY-downY;//判斷是向上滑還是向下滑,我們下拉,肯定要下滑的
if (diff>0&&mYfirstVisibleItem==0) {//判斷向下滑,並且listview顯示的第一個條目的總的第一個條目
int padding = -headHeight+diff;//得到偏移量
if (padding>0&¤tState==PULL_DOWN_REFRE) {//完全拉出來
currentState = RELEASE_REFRE;
System.out.println("釋放刷新");
<span style="white-space:pre"> </span>//改變當前狀態和佈局文字等等方法
selectCurrentState();
}
else if (padding<0&& currentState ==RELEASE_REFRE) {
currentState = PULL_DOWN_REFRE;
System.out.println("下拉刷新");
selectCurrentState();
}
headview.setPadding(0, padding, 0, 0);
return true;
}
break;
case MotionEvent.ACTION_UP:
if (currentState ==PULL_DOWN_REFRE) {
headview.setPadding(0, -headHeight, 0, 0);
}
else if (currentState ==RELEASE_REFRE){
currentState = REFREING;
selectCurrentState();
headview.setPadding(0, 0, 0, 0);
if (changeListener!=null) {
changeListener.pull_down_refresh();
}
}
break;
}
return super.onTouchEvent(ev);
}
/**
* 各個狀態 改變UI的方法
*/
private void selectCurrentState() {
switch (currentState) {
case PULL_DOWN_REFRE:
headcontent.setText("下拉刷新");
headicon.startAnimation(upAnimation);
break;
case RELEASE_REFRE:
headcontent.setText("釋放刷新");
headicon.startAnimation(downAnimation);
break;
case REFREING:
headcontent.setText("刷新中");
headicon.clearAnimation();
headicon.setVisibility(View.INVISIBLE);
headpb.setVisibility(View.VISIBLE);
break;
}
}
/*滾動狀態改變調用
public static int SCROLL_STATE_TOUCH_SCROLL = 1;
public static int SCROLL_STATE_FLING = 2;
* (non-Javadoc)
* @see android.widget.AbsListView.OnScrollListener#onScrollStateChanged(android.widget.AbsListView, int)
*/
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
//顯示出來
if ((scrollState==SCROLL_STATE_IDLE||scrollState==SCROLL_STATE_FLING)&&getLastVisiblePosition()==getCount()-1) {
if (!isMove) {
isMove=!isMove;
footview.setPadding(0, 0, 0, 0);
if (changeListener!=null) {
changeListener.loadMove();
}
setSelection(getCount()-1);//設置當前位置
}
}
}
/**
* 自定義的監聽事件,接受接口
* @param listener
*/
public void setOnListenerChanger(OnRefreChangeListener listener){
changeListener = listener;
}
/**
* 滾動時調用
*/
@Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
mYfirstVisibleItem = firstVisibleItem;
}
/**
* 隱藏頭佈局 刷新結束調用
*/
public void hideHeadView() {
headcontent.setText("下拉刷新");
currentState =PULL_DOWN_REFRE;
headicon.setVisibility(View.VISIBLE);
headpb.setVisibility(View.INVISIBLE);
headview.setPadding(0, -headHeight, 0, 0);//這樣初始的時候就會隱藏頭佈局
headrefretime.setText("最後更新時間:"+getLastTime());
}
/**
* 獲取當前時間
* @return
*/
private String getLastTime() {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
return dateFormat.format(new Date());
}
/**
* 隱藏尾佈局 刷新結束調用
*/
public void hideFootView() {
isMove =false;
headview.setPadding(0, 0, 0,-footheight);//這樣初始的時候就會隱藏頭佈局
}
}
OnRefreChangeListener.java
package com.qzzhu.refrelist;
public interface OnRefreChangeListener {
//釋放刷新回調的方法
void pull_down_refresh();
//上啦加載更多
void loadMove();
}
頭佈局文件:
<span style="color:#3333ff;"><?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:orientation="horizontal" >
<FrameLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
>
<ImageView
android:id="@+id/head_icon"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_gravity="center"
android:background="@drawable/common_listview_headview_red_arrow"
/>
<ProgressBar
android:id="@+id/head_pb"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:visibility="invisible"
android:indeterminateDrawable="@drawable/custom_bg"
/>
</FrameLayout>
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:gravity="center"
>
<TextView
android:id="@+id/head_tvcontent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="下拉刷新"
android:textSize="23sp"
android:textColor="#ff0000"
/>
<TextView
android:id="@+id/head_refretime"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="最後更新時間 2016-5-28"
android:textSize="15sp"
android:textColor="#77000000"
/>
</LinearLayout>
</LinearLayout></span>
<span style="color:#3333ff;"><?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:gravity="center"
android:orientation="horizontal" >
<ProgressBar
android:id="@+id/foot_pb"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:indeterminateDrawable="@drawable/custom_bg"
/>
<TextView
android:id="@+id/head_tvcontent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="下拉刷新"
android:textSize="23sp"
android:textColor="#ff0000"
/>
</LinearLayout>
<pre name="code" class="html">ProgressBar 樣式:custom_bg.xml 在drawable文件夾下
<span style="font-family: Arial, Helvetica, sans-serif;"></span><pre name="code" class="html"><span style="font-family: Arial, Helvetica, sans-serif;"><rotate xmlns:android="http://schemas.android.com/apk/res/android"</span>
android:fromDegrees="0"
android:toDegrees="360"
android:pivotX="50%"
android:pivotY="50%"
>
<shape
android:shape="ring"
android:innerRadiusRatio="3"
android:thicknessRatio="14"
android:useLevel="false"
>
<gradient
android:startColor="#ffffff"
android:centerColor="#ff6a6a"
android:endColor="#ff0000"
android:type="sweep"
/>
</shape>
</rotate>
MainActivity.java
import java.util.ArrayList;
import java.util.List;
import android.support.v7.app.ActionBarActivity;
import android.graphics.Color;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.SystemClock;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;
public class MainActivity extends ActionBarActivity {
private RefreListview refreListview;
private List<String> itemList;
private MyAdapter adapter ;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
refreListview = (RefreListview) findViewById(R.id.refrelist);
itemList = new ArrayList<String>();
for (int i = 0; i < 30; i++) {
itemList.add("啦啦啦啦?"+i+"次");
}
adapter = new MyAdapter();
refreListview.setAdapter(adapter);
refreListview.setOnListenerChanger(new OnRefreChangeListener() {
@Override
public void pull_down_refresh() {
new AsyncTask<Void, Void, Void>(){
//開始執行的時候調用,此方法 在主線程調用
@Override
protected void onPreExecute() {
//輸出測試方法的執行先後和運行所在的線程,運行在主線程中,可以進行修改UI操作
System.out.println("onPreExecute"+Thread.currentThread().getName());
super.onPreExecute();
}
//在需要與服務器交互的時候,當onPreExecute被調用之後執行,此方法運行在子線程中,可以進行耗時的操作
@Override
protected Void doInBackground(Void... params) {
// TODO Auto-generated method stub
SystemClock.sleep(2000);
System.out.println("doInBackground"+Thread.currentThread().getName());
itemList.add(0, "新增的條目");
return null;
}
//最後執行,運行在主線程中,可以進行修改UI操作
protected void onPostExecute(Void result) {
System.out.println("onPostExecute"+Thread.currentThread().getName());
adapter.notifyDataSetChanged();
refreListview.hideHeadView();
super.onProgressUpdate(result);
};
}.execute(new Void[]{});
}
@Override
public void loadMove() {
new AsyncTask<Void, Void, Void>(){
protected void onPreExecute() {
};
@Override
protected Void doInBackground(Void... params) {
// TODO Auto-generated method stub
SystemClock.sleep(2000);
itemList.add("新增的條目");
return null;
}
protected void onPostExecute(Void result) {
refreListview.hideFootView();
adapter.notifyDataSetChanged();
super.onProgressUpdate(result);
};
}.execute(new Void[]{});
}
});
}
private class MyAdapter extends BaseAdapter{
@Override
public int getCount() {
return itemList.size();
}
@Override
public Object getItem(int position) {
return null;
}
@Override
public long getItemId(int position) {
// TODO Auto-generated method stub
return 0;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
TextView view =null;
if (convertView==null) {
view = new TextView(getApplicationContext());
}
else {
view = (TextView) convertView;
}
view.setText(itemList.get(position));
view.setTextColor(Color.BLACK);
return view;
}
}
}