1. 程式人生 > >下拉刷新XListView的簡單分析

下拉刷新XListView的簡單分析

widget touch radi lee show progress 箭頭 warning 理解

依照這篇博文裏的思路分析和理解的

先要理解Scroller,看過的博文:

http://ipjmc.iteye.com/blog/1615828

http://blog.csdn.net/wangjinyu501/article/details/32339379

還要理解View的touch時間傳遞:

http://www.codekk.com/open-source-project-analysis/detail/Android/Trinea/%E5%85%AC%E5%85%B1%E6%8A%80%E6%9C%AF%E7%82%B9%E4%B9%8BView%20%E4%BA%8B%E4%BB%B6%E4%BC%A0%E9%80%92


在實現中遇到的問題:

1、下拉時,下拉區域不會尾隨下拉而變化,僅僅顯示當中一部分。

圖:

技術分享

解決:採用設置下拉區域的paddind,實現尾隨滾動效果。終於圖:

技術分享

2、當下拉超過極限高度後向上滑動時。listview會尾隨滑動。

解決方法是通過在onTouchEvent推斷這一情況推斷這一情況,具體在代碼裏。


代碼:

下拉區域布局文件

<?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:id="@+id/xlistview_header_content"
        android:layout_width="fill_parent"
        android:layout_height="60dp"
        android:layout_marginBottom="2dp"
        android:gravity="center_horizontal" >

        <TextView
            android:id="@+id/xlistview_header_hint_textview"
            android:layout_width="100dp"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:gravity="center"
            android:text="正在載入"
            android:textColor="@android:color/black"
            android:textSize="14sp" />

        <ImageView
            android:id="@+id/xlistview_header_image"
            android:layout_width="30dp"
            android:layout_height="wrap_content"
            android:layout_centerVertical="true"
            android:layout_toLeftOf="@id/xlistview_header_hint_textview"
            android:src="@drawable/indicator_arrow" />

        <ProgressBar
            android:id="@+id/xlistview_header_progressbar"
            android:layout_width="30dp"
            android:layout_height="30dp"
            android:layout_centerVertical="true"
            android:layout_toLeftOf="@id/xlistview_header_hint_textview"
            android:visibility="invisible" />
    </RelativeLayout>

</LinearLayout>

下拉區域

package com.example.test;

import android.annotation.SuppressLint;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.RotateAnimation;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.TextView;

public class XListViewHeader extends LinearLayout {

	private static final String HINT_NORMAL = "下拉刷新";
	private static final String HINT_READY = "松開刷新數據";
	private static final String HINT_LOADING = "正在載入...";

	// 正常狀態,下拉未超過head高度
	public final static int STATE_NORMAL = 0;
	// 準備刷新狀態,也就是箭頭方向發生改變之後的狀態,可是沒有刷新
	public final static int STATE_READY = 1;
	// 刷新狀態。箭頭變成了progressBar,正在刷新
	public final static int STATE_REFRESHING = 2;
	// 布局容器,也就是根布局
	private LinearLayout mContentLayout;
	// 箭頭圖片
	private ImageView mImageView;
	// 刷新狀態顯示
	private ProgressBar mProgressBar;
	// 說明文本
	private TextView mHintTextView;
	// 記錄當前的狀態
	private int mState = -1;
	// 用於改變箭頭的方向的動畫
	private Animation mRotateUpAnim;
	private Animation mRotateDownAnim;
	// 動畫持續時間
	private final int ROTATE_ANIM_DURATION = 180;

	private int headHeight;
	private Context context;

	public XListViewHeader(Context context) {
		super(context);
		this.context = context;
		init();
	}

	private void init() {
		LinearLayout.LayoutParams lp = new LayoutParams(
				LayoutParams.MATCH_PARENT, 0);// 初始化高度為0
		mContentLayout = (LinearLayout) LayoutInflater.from(context).inflate(
				R.layout.xlistview_header, null);
		mContentLayout.setLayoutParams(lp);
		addView(mContentLayout);

		mImageView = (ImageView) mContentLayout
				.findViewById(R.id.xlistview_header_image);// 箭頭圖片
		mHintTextView = (TextView) mContentLayout
				.findViewById(R.id.xlistview_header_hint_textview);// 提示文本
		mProgressBar = (ProgressBar) mContentLayout
				.findViewById(R.id.xlistview_header_progressbar);// 進度條
		mRotateUpAnim = new RotateAnimation(0, 180, Animation.RELATIVE_TO_SELF,
				0.5f, Animation.RELATIVE_TO_SELF, 0.5f);// 箭頭向上旋轉的動畫
		mRotateUpAnim.setDuration(ROTATE_ANIM_DURATION);// 動畫持續時間
		mRotateUpAnim.setFillAfter(true);// 動畫終止時停留在最後,也就是保留動畫以後的狀態
		mRotateDownAnim = new RotateAnimation(-180, 0,
				Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF,
				0.5f);
		mRotateDownAnim.setFillAfter(true);
		setState(STATE_NORMAL);// 初始化設置為正常模式
	}

	public void setState(int state) {
		if (state == mState) {
			return;
		}
		if (state == STATE_REFRESHING) {// 設置為正在刷新狀態時,清楚全部動畫,箭頭隱藏, 進度條顯示
			mImageView.clearAnimation();
			mImageView.setVisibility(View.GONE);
			mProgressBar.setVisibility(View.VISIBLE);
		} else {
			mImageView.setVisibility(View.VISIBLE);
			mProgressBar.setVisibility(View.GONE);
		}
		switch (state) {
		case STATE_NORMAL:
			if (mState == STATE_READY) {// 由準備狀態變為正常狀態。開啟向下動畫
				mImageView.startAnimation(mRotateDownAnim);
			} else {
				mImageView.clearAnimation();
			}
			mHintTextView.setText(HINT_NORMAL);
			break;
		case STATE_READY:
			if (mState == STATE_NORMAL) {
				mImageView.startAnimation(mRotateUpAnim);
			}
			mHintTextView.setText(HINT_READY);
			break;
		case STATE_REFRESHING:
			mHintTextView.setText(HINT_LOADING);
			break;
		}
		mState = state;
	}

	@SuppressLint("NewApi")
	public void setVisitHeight(int height) {
		if (height < 0) {
			height = 0;
		}
		LinearLayout.LayoutParams lp = (LayoutParams) mContentLayout
				.getLayoutParams();
		lp.height = height;
		mContentLayout.setLayoutParams(lp);
		mContentLayout.setPadding(mContentLayout.getPaddingLeft(), height
				- headHeight, mContentLayout.getPaddingRight(),
				mContentLayout.getPaddingBottom());// 設置padding是為了下拉時,head尾隨著下拉。更好看
	}

	public int getVisitHeight() {
		return mContentLayout.getHeight();
	}

	public void show() {
		mContentLayout.setVisibility(View.VISIBLE);
	}

	public void hide() {
		mContentLayout.setVisibility(View.INVISIBLE);
	}

	public int getHeadHeight() {
		return headHeight;
	}

	public void setHeadHeight(int headHeight) {
		this.headHeight = headHeight;
	}

}

listview

package com.example.test;

import android.content.Context;
import android.view.MotionEvent;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.view.animation.DecelerateInterpolator;
import android.widget.ListView;
import android.widget.RelativeLayout;
import android.widget.Scroller;

public class XListView extends ListView {
	private Context context;
	// 滑動時長
	private final static int SCROLL_DURATION = 400;
	// 滑動比例
	private final static float OFFSET_RADIO = 2f;
	// 記錄按下點的y坐標
	private float lastY;
	// 用來回滾
	private Scroller scroller;
	private IXListViewListener mListViewListener;
	private XListViewHeader headerView;
	private RelativeLayout headerViewContent;
	// header的高度
	private int headerHeight;
	// 是否可以刷新
	private boolean enableRefresh = true;
	// 是否正在刷新
	private boolean isRefreashing = false;
	// 記錄當前手勢是向上還是向下
	private int TOUCH_UP = 0, TOUCH_DOWN = 1;
	private int mTouch;

	public XListView(Context context) {
		super(context);
		this.context = context;
		init();
	}

	private void init() {
		scroller = new Scroller(context, new DecelerateInterpolator());
		headerView = new XListViewHeader(context);
		headerViewContent = (RelativeLayout) headerView
				.findViewById(R.id.xlistview_header_content);
		// 獲得head的高度
		headerView.getViewTreeObserver().addOnGlobalLayoutListener(
				new OnGlobalLayoutListener() {
					@SuppressWarnings("deprecation")
					@Override
					public void onGlobalLayout() {
						headerHeight = headerViewContent.getHeight();
						headerView.setHeadHeight(headerHeight);
						getViewTreeObserver()
								.removeGlobalOnLayoutListener(this);
					}
				});
		addHeaderView(headerView);
	}

	@Override
	public boolean onTouchEvent(MotionEvent ev) {
		switch (ev.getAction()) {
		case MotionEvent.ACTION_DOWN:
			lastY = ev.getRawY();
			break;
		case MotionEvent.ACTION_MOVE:
			float t = ev.getRawY() - lastY;
			lastY = ev.getRawY();
			if (t > 0) {
				mTouch = TOUCH_DOWN;
			} else {
				mTouch = TOUCH_UP;
			}
			// 當前是第一個item,且手勢是向下,就顯示下拉條,更新高度
			if (getFirstVisiblePosition() == 0
					&& (headerView.getVisitHeight() > 0 || t > 0)) {
				updateHeaderViewHeight(t / OFFSET_RADIO);
			}
			if (!isRefreashing && mTouch == TOUCH_UP
					&& headerView.getVisitHeight() > 0) {
				return true;// 當下拉高度達到header高度時候,松開就可以刷新。若此刻向上滑,listview會尾隨滑動,return
							// true 代表消費這個事件,listview禁止滾動
			}
			break;
		case MotionEvent.ACTION_UP:
			if (getFirstVisiblePosition() == 0) {
				if (enableRefresh && headerView.getVisitHeight() > headerHeight) {
					isRefreashing = true;
					headerView.setState(headerView.STATE_REFRESHING);
					if (mListViewListener != null) {
						mListViewListener.onRefresh();//刷新事件
					}
				}
			}
			resetHeaderHeight();
			break;
		}
		return super.onTouchEvent(ev);
	}

	public void updateHeaderViewHeight(float f) {
		headerView.setVisitHeight((int) f + headerView.getVisitHeight());
		// 未處於刷新狀態,更新箭頭
		if (enableRefresh && !isRefreashing) {
			if (headerView.getVisitHeight() > headerHeight) {
				headerView.setState(XListViewHeader.STATE_READY);
			}else{
				headerView.setState(XListViewHeader.STATE_NORMAL);				
			}
		}
	}

	// 下拉條動態消失
	public void resetHeaderHeight() {
		int height = headerView.getVisitHeight();
		int endheight = 0;
		if (isRefreashing) {
			endheight = headerHeight;
		}
		// y軸方向由 height 到 endheight 。第三個參數是增量,假設不是刷新則高度變為0,假設是,高度變為head原始高度
		scroller.startScroll(0, height, 0, endheight - height, SCROLL_DURATION);
		invalidate();
	}

	public void computeScroll() {
		if (scroller.computeScrollOffset()) {
			// 利用scroller 。設置高度。重復重繪
			headerView.setVisitHeight(scroller.getCurrY());
			postInvalidate();
		}
		super.computeScroll();
	}

	public void stopRefresh() {
		if (isRefreashing == true) {
			isRefreashing = false;
			resetHeaderHeight();
		}
	}

	public void setIxListener(IXListViewListener listener) {
		this.mListViewListener = listener;
	}

	interface IXListViewListener {
		public void onRefresh();// 刷新事件的回調函數
	}
}

mainactivity

package com.example.test;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.widget.ArrayAdapter;

import com.example.test.XListView.IXListViewListener;

public class MainActivity extends Activity {
	private XListView xListView;
	private Handler handler = new Handler() {
		public void handleMessage(Message msg) {
			xListView.stopRefresh();
		}
	};

	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		xListView = new XListView(this);
		ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
				android.R.layout.simple_expandable_list_item_1);
		xListView.setAdapter(adapter);
		for (int i = 0; i < 10; i++) {
			adapter.add("text" + i);
		}
		setContentView(xListView);
		xListView.setIxListener(new IXListViewListener() {
			public void onRefresh() {
				new Thread() {
					public void run() {
						try {
							Thread.sleep(2000);
						} catch (InterruptedException e) {
							e.printStackTrace();
						}
						handler.sendEmptyMessage(0);
					}
				}.start();
			}
		});
	}
}


下載

csdn博文編輯不能撤銷麽。寫的東西都沒了

下拉刷新XListView的簡單分析