按照这篇博文里的思路分析和理解的
先要理解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博文编辑不能撤销么,写的东西都没了