下拉刷新XListView的簡單分析
阿新 • • 發佈:2017-08-15
widget touch radi lee show progress 箭頭 warning 理解
下拉區域
listview
mainactivity
依照這篇博文裏的思路分析和理解的
先要理解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的簡單分析