下拉刷新XListView的简单分析

依照这篇博文里的思路分析和理解的

先要理解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博文编辑不能撤销么。写的东西都没了

posted @ 2017-08-15 18:37  wzzkaifa  阅读(220)  评论(0编辑  收藏  举报