android 自定義ListView實現下拉刷新

這兩天突然想弄個下拉刷新的,於是就在網上搜索,找呀找,發現那些都好長,反正我是不怎麼看得懂,於是,我就到處尋,果不其然,被我弄懂了。下面我從我得角度向大家詳細如何實現的。

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;
		}
    }

}




整個項目工程Demo 需要的朋友自己下載去研究



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章