按照这篇博文里的思路分析和理解的
先要理解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; } }
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();// 刷新事件的回调函数 } }
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博文编辑不能撤销么,写的东西都没了
原文地址:http://blog.csdn.net/noaboutfengyue/article/details/46635229