本例是實現下拉時刷新而且底部設置加載更多選擇並在下拉和回滑時加入相應的動畫效果, 功能實現主要爲自定義一個Layout在此佈局中頭部刷新主要是用到了在頭部添加布局view控件並使用GestureDetector控制下拉時的變化以便控制滑動的效果,在自定義底部也加一個加載更多的佈局,中間內嵌一個ListView控件以顯示內容。具體步驟以下:java
第一步:設計自定義佈局PullDownListView繼承FrameLayout 實現GestureDetector.OnGestureListener和Animation.AnimationListener接口:android
import android.content.Context; import android.graphics.Rect; import android.util.AttributeSet; import android.util.Log; import android.view.GestureDetector; import android.view.Gravity; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.ViewConfiguration; import android.view.animation.Animation; import android.view.animation.AnimationUtils; import android.widget.AbsListView; import android.widget.AbsListView.OnScrollListener; import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.ListView; import android.widget.ProgressBar; import android.widget.Scroller; import android.widget.TextView; import com.example.pulldownview.R; public class PullDownListView extends FrameLayout implements GestureDetector.OnGestureListener, Animation.AnimationListener { public static int MAX_LENGHT = 0; public static final int STATE_REFRESH = 1; public static final int SCROLL_TO_CLOSE = 2; public static final int SCROLL_TO_REFRESH = 3; public static final double SCALE = 0.9d; private static final int CLOSEDELAY = 300; private static final int REFRESHDELAY = 300; private Animation mAnimationDown; private Animation mAnimationUp; private ImageView mArrow; private View emptyHeaderView; private ProgressBar mProgressBar; private TextView more; private ProgressBar mProgressBar2; private int mState; private TextView mTitle; public ListView mListView; LinearLayout foot; LinearLayout footer_layout; LinearLayout header; private GestureDetector mDetector; private FlingRunnable mFlinger; private int mPading; private int mDestPading; private int mLastTop; private LinearLayout mFirstChild; private FrameLayout mUpdateContent; private OnRefreshListioner mRefreshListioner; private boolean isAutoLoadMore = false; private boolean hasMore = true; private boolean isEnd = true; private boolean listviewDoScroll = false; private boolean isFirstLoading = false; private boolean mLongPressing;// 若是設置爲true說明恰好到了執行長按的時間 private boolean mPendingRemoved = false;// private String pulldowntorefresh; private String releasetorefresh; private String loading; Rect r = new Rect(); private MotionEvent downEvent; private CheckForLongPress mPendingCheckForLongPress = new CheckForLongPress(); private CheckForLongPress2 mPendingCheckForLongPress2 = new CheckForLongPress2(); private float lastY; private boolean useempty = true; //這個標籤做爲測試用 String TAG = "PullDownListView"; /** * 長按檢查方法執行1線程 * @author Administrator * */ private class CheckForLongPress implements Runnable { public void run() { if (mListView.getOnItemLongClickListener() == null) { } else { postDelayed(mPendingCheckForLongPress2, 100); } } } /** * 長按檢查方法執行2線程 ----> 延後 100 * @author Administrator * */ private class CheckForLongPress2 implements Runnable { public void run() { mLongPressing = true; MotionEvent e = MotionEvent.obtain(downEvent.getDownTime(),downEvent.getEventTime() + ViewConfiguration.getLongPressTimeout(), MotionEvent.ACTION_CANCEL, downEvent.getX(), downEvent.getY(), downEvent.getMetaState()); PullDownListView.super.dispatchTouchEvent(e); } } class FlingRunnable implements Runnable { private void startCommon() { removeCallbacks(this); } public void run() { boolean noFinish = mScroller.computeScrollOffset(); int curY = mScroller.getCurrY(); int deltaY = curY - mLastFlingY; if (noFinish) { move(deltaY, true); mLastFlingY = curY; post(this); } else { removeCallbacks(this); if (mState == SCROLL_TO_CLOSE) { mState = -1; } } } public void startUsingDistance(int distance, int duration) { if (distance == 0) distance--; startCommon(); mLastFlingY = 0; mScroller.startScroll(0, 0, 0, distance, duration); post(this); } private int mLastFlingY; private Scroller mScroller; public FlingRunnable() { mScroller = new Scroller(getContext()); } } /** * 下拉刷新以及加載更多回調監聽接口 * @author Administrator * */ public interface OnRefreshListioner { public abstract void onRefresh(); public abstract void onLoadMore(); } /** * 直接new時調用的構造方法 * @param context */ public PullDownListView(Context context) { super(context); mDetector = new GestureDetector(context, this); mFlinger = new FlingRunnable(); init(); addRefreshBar(); } /** * 在xml中使用時調用的構造方法 * @param context */ public PullDownListView(Context context, AttributeSet att) { super(context, att); useempty = att.getAttributeBooleanValue(null, "useempty", true); mDetector = new GestureDetector(this); mFlinger = new FlingRunnable(); init(); addRefreshBar(); } View view; /** * 添加刷新頭部的控件 */ private void addRefreshBar() { //向上滑動的動畫 mAnimationUp = AnimationUtils.loadAnimation(getContext(),R.anim.rotate_up); mAnimationUp.setAnimationListener(this); //向下滑動的動畫 mAnimationDown = AnimationUtils.loadAnimation(getContext(),R.anim.rotate_down); mAnimationDown.setAnimationListener(this); //刷新頭部的view view = LayoutInflater.from(getContext()).inflate(R.layout.refresh_bar,null); //添加view在本控件中 addView(view); /* * 如下都是刷新頭部的一些控件的設置 */ mFirstChild = (LinearLayout) view; mUpdateContent = (FrameLayout) getChildAt(0).findViewById(R.id.iv_content); mArrow = new ImageView(getContext()); FrameLayout.LayoutParams layoutparams = new FrameLayout.LayoutParams( LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT); mArrow.setScaleType(ImageView.ScaleType.FIT_CENTER); mArrow.setLayoutParams(layoutparams); mArrow.setImageResource(R.drawable.arrow_down); mUpdateContent.addView(mArrow); FrameLayout.LayoutParams layoutparams1 = new FrameLayout.LayoutParams( LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT); layoutparams1.gravity = Gravity.CENTER; mProgressBar = new ProgressBar(getContext(), null, android.R.attr.progressBarStyleSmallInverse); mProgressBar.setIndeterminate(false); int i = getResources().getDimensionPixelSize(R.dimen.updatebar_padding); mProgressBar.setPadding(i, i, i, i); mProgressBar.setLayoutParams(layoutparams1); mUpdateContent.addView(mProgressBar); mTitle = (TextView) findViewById(R.id.tv_title); } public void setGone() { mTitle.setVisibility(View.GONE); mUpdateContent.setVisibility(View.GONE); } protected void onFinishInflate() { super.onFinishInflate(); mListView = (ListView) getChildAt(1); footer_layout = (LinearLayout) LayoutInflater.from(getContext()) .inflate(R.layout.empty_main, null); foot = (LinearLayout) LayoutInflater.from(getContext()).inflate( R.layout.ref2, null); more = (TextView) foot.findViewById(R.id.ref); mProgressBar2 = (ProgressBar) foot.findViewById(R.id.refbar); mProgressBar2.setVisibility(View.GONE); if (useempty) { header = (LinearLayout) LayoutInflater.from(getContext()).inflate( R.layout.empty_main, null); mListView.addHeaderView(header); } mListView.addFooterView(footer_layout); foot.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { if (!isAutoLoadMore) onLoadMore(); } }); mListView.setOnScrollListener(new OnScrollListener() { public void onScrollStateChanged(AbsListView view, int scrollState) { if (isEnd && scrollState == SCROLL_STATE_IDLE && hasMore&& isAutoLoadMore) { onLoadMore(); } } public void onScroll(AbsListView view, int f, int v, int t) { if (isAutoLoadMore) { if (f + v >= t - 1) isEnd = true; else isEnd = false; } } }); } /** * 設置沒有數據時默認圖片 * * @param empty */ public void setEmptyHeaderView(View empty) { emptyHeaderView = empty; } /** * 添加空的定部view */ public void addEmptyHeaderView() { header.removeAllViews(); if (emptyHeaderView != null) header.addView(emptyHeaderView); } /** * 移除頂部空view */ public void removeEmptyHeaderView() { if (emptyHeaderView != null) header.removeView(emptyHeaderView); } /** * 初始化設置及變量 */ private void init() { MAX_LENGHT = getResources().getDimensionPixelSize( R.dimen.updatebar_height);// 62.0dip setDrawingCacheEnabled(false); setBackgroundDrawable(null); setClipChildren(false); mDetector.setIsLongpressEnabled(false); mPading = -MAX_LENGHT; mLastTop = -MAX_LENGHT; pulldowntorefresh = getContext().getText(R.string.drop_dowm).toString(); releasetorefresh = getContext().getText(R.string.release_update) .toString(); loading = getContext().getText(R.string.loading).toString(); } /** deltaY > 0 向上 */ private boolean move(float deltaY, boolean auto) { //move 方法執行 " if (deltaY > 0 && mFirstChild.getTop() == -MAX_LENGHT) { mPading = -MAX_LENGHT; return false; } if (auto) { //move 方法執行 if (mFirstChild.getTop() - deltaY < mDestPading) { deltaY = mFirstChild.getTop() - mDestPading; } mFirstChild.offsetTopAndBottom((int) -deltaY); mListView.offsetTopAndBottom((int) -deltaY); mPading = mFirstChild.getTop(); if (mDestPading == 0 && mFirstChild.getTop() == 0&& mState == SCROLL_TO_REFRESH) { //onRefresh 刷新方法執行 onRefresh(); } else if (mDestPading == -MAX_LENGHT) { } invalidate(); updateView(); return true; } else { if (mState != STATE_REFRESH || (mState == STATE_REFRESH && deltaY > 0)) { mFirstChild.offsetTopAndBottom((int) -deltaY); mListView.offsetTopAndBottom((int) -deltaY); mPading = mFirstChild.getTop(); } else if (mState == STATE_REFRESH && deltaY < 0&& mFirstChild.getTop() <= 0) { if (mFirstChild.getTop() - deltaY > 0) { deltaY = mFirstChild.getTop(); } mFirstChild.offsetTopAndBottom((int) -deltaY); mListView.offsetTopAndBottom((int) -deltaY); mPading = mFirstChild.getTop(); } } if (deltaY > 0 && mFirstChild.getTop() <= -MAX_LENGHT) { mPading = -MAX_LENGHT; deltaY = -MAX_LENGHT - mFirstChild.getTop(); mFirstChild.offsetTopAndBottom((int) deltaY); mListView.offsetTopAndBottom((int) deltaY); mPading = mFirstChild.getTop(); updateView(); invalidate(); return false; } updateView(); invalidate(); return true; } private void updateView() { String s = ""; if (mState != STATE_REFRESH) { if (mFirstChild.getTop() < 0) { mArrow.setVisibility(View.VISIBLE); mProgressBar.setVisibility(View.INVISIBLE); mTitle.setText(pulldowntorefresh); if (mLastTop >= 0 && mState != SCROLL_TO_CLOSE) { mArrow.startAnimation(mAnimationUp);//向上移動動畫 } } else if (mFirstChild.getTop() > 0) { mTitle.setText(releasetorefresh + s); mProgressBar.setVisibility(View.INVISIBLE); mArrow.setVisibility(View.VISIBLE); if (mLastTop <= 0) { mArrow.startAnimation(mAnimationDown);//向下移動動畫 } } } mLastTop = mFirstChild.getTop(); } //release 方法執行 private boolean release() { if (listviewDoScroll) { listviewDoScroll = false; return true; } if (mFirstChild.getTop() > 0) { scrollToUpdate(false); } else { scrollToClose(); } invalidate(); return false; } private void scrollToClose() { mDestPading = -MAX_LENGHT; mFlinger.startUsingDistance(MAX_LENGHT, CLOSEDELAY); } //scrollToUpdate 方法執行 public void scrollToUpdate(boolean load) { mState = SCROLL_TO_REFRESH; mDestPading = 0; if (load) { mFlinger.startUsingDistance(50, REFRESHDELAY); load = false; } else mFlinger.startUsingDistance(mFirstChild.getTop(), REFRESHDELAY); } private void onRefresh() { mState = STATE_REFRESH; mTitle.setText(loading); mProgressBar.setVisibility(View.VISIBLE); mArrow.setVisibility(View.INVISIBLE); if (mRefreshListioner != null) { mRefreshListioner.onRefresh(); } } public void onRefreshComplete() { onRefreshComplete(null); } public void onRefreshComplete(String date) { mState = SCROLL_TO_CLOSE; mArrow.setImageResource(R.drawable.arrow_down); mProgressBar2.setVisibility(View.INVISIBLE); updateCommon(); scrollToClose(); } public void setMore(boolean hasMore) { if (hasMore) { mListView.setFooterDividersEnabled(true); footer_layout.removeAllViews(); footer_layout.addView(foot); } else { mListView.setFooterDividersEnabled(false); footer_layout.removeAllViews(); } } private void updateCommon() { if (mListView.getCount() == (mListView.getHeaderViewsCount() + mListView .getFooterViewsCount())) { Log.e("out", "數據爲空"); if (useempty) addEmptyHeaderView(); } else { removeEmptyHeaderView(); mListView.setFooterDividersEnabled(false); footer_layout.removeAllViews(); } } public void setFoot() { footer_layout.setVisibility(View.GONE); } public void onFirstLoad() { if (footer_layout.getChildCount() == 0) { footer_layout.addView(foot); } isFirstLoading = true; foot.setEnabled(false); //onFirstLoad 方法執行 mState = STATE_REFRESH; mProgressBar2.setVisibility(View.VISIBLE); more.setText(R.string.loading); } public void onLoadMore() { //onLoadMore 方法執行 foot.setEnabled(false); mState = STATE_REFRESH; mProgressBar2.setVisibility(View.VISIBLE); more.setText(R.string.loading); if (mRefreshListioner != null) { mRefreshListioner.onLoadMore(); } } public void onLoadMoreComplete(String date) { mState = -1; mProgressBar2.setVisibility(View.INVISIBLE); more.setText(R.string.seen_more); updateCommon(); if (isFirstLoading) isFirstLoading = false; foot.setEnabled(true); } public void onLoadMoreComplete() { onLoadMoreComplete(null); } public boolean dispatchTouchEvent(MotionEvent e) { if (isFirstLoading) { return false; } int action; float y = e.getY(); action = e.getAction(); if (mLongPressing && action != MotionEvent.ACTION_DOWN) { return false; } if (e.getAction() == MotionEvent.ACTION_DOWN) { mLongPressing = true; } boolean handled = true; handled = mDetector.onTouchEvent(e); switch (action) { case MotionEvent.ACTION_UP: boolean f1 = mListView.getTop() <= e.getY() && e.getY() <= mListView.getBottom(); if (!handled && mFirstChild.getTop() == -MAX_LENGHT && f1|| mState == STATE_REFRESH) { super.dispatchTouchEvent(e); } else { //執行釋放方法 handled = release(); } break; case MotionEvent.ACTION_CANCEL: handled = release(); super.dispatchTouchEvent(e); break; case MotionEvent.ACTION_DOWN: downEvent = e; mLongPressing = false; //長按的時間間隔 postDelayed(mPendingCheckForLongPress, ViewConfiguration.getLongPressTimeout() + 100); mPendingRemoved = false; super.dispatchTouchEvent(e); break; case MotionEvent.ACTION_MOVE: float deltaY = lastY - y; lastY = y; if (!mPendingRemoved) { removeCallbacks(mPendingCheckForLongPress); mPendingRemoved = true; } if (!handled && mFirstChild.getTop() == -MAX_LENGHT) { try { return super.dispatchTouchEvent(e); } catch (Exception e2) { e2.printStackTrace(); return true; } } else if (handled && mListView.getTop() > 0 && deltaY < 0) {// deltaY小於0,向�? e.setAction(MotionEvent.ACTION_CANCEL); super.dispatchTouchEvent(e); } break; default: break; } return true; } public void onAnimationEnd(Animation animation) { int top = mFirstChild.getTop(); if (top < 0) mArrow.setImageResource(R.drawable.arrow_down); else if (top > 0) mArrow.setImageResource(R.drawable.arrow_up); else { if (top < mLastTop) { mArrow.setImageResource(R.drawable.arrow_down); } else { mArrow.setImageResource(R.drawable.arrow_up); } } } public void onAnimationRepeat(Animation animation) { } public void onAnimationStart(Animation animation) { } public boolean onDown(MotionEvent e) { return false; } public boolean onFling(MotionEvent motionevent, MotionEvent e, float f, float f1) { return false; } protected void onLayout(boolean flag, int i, int j, int k, int l) { int top = mPading; int w = getMeasuredWidth(); mFirstChild.layout(0, top, w, top + MAX_LENGHT); int h = getMeasuredHeight() + mPading + MAX_LENGHT; mListView.layout(0, top + MAX_LENGHT, w, h); } public void onLongPress(MotionEvent e) { } /** deltaY > 0 向上 */ public boolean onScroll(MotionEvent curdown, MotionEvent cur, float deltaX, float deltaY) { deltaY = (float) ((double) deltaY * SCALE); boolean handled = false; boolean flag = false; if (mListView.getCount() == 0) { flag = true; } else { View c = mListView.getChildAt(0); if (mListView.getFirstVisiblePosition() == 0 && c != null && c.getTop() == 0) { flag = true; } } if (deltaY < 0F && flag || getChildAt(0).getTop() > -MAX_LENGHT) { // deltaY // < // 0 // 向下 handled = move(deltaY, false); } else handled = false; return handled; } public void onShowPress(MotionEvent motionevent) { } public boolean onSingleTapUp(MotionEvent motionevent) { return false; } public void setRefreshListioner(OnRefreshListioner RefreshListioner) { mRefreshListioner = RefreshListioner; } public boolean isAutoLoadMore() { return isAutoLoadMore; } public void setAutoLoadMore(boolean isAutoLoadMore) { this.isAutoLoadMore = isAutoLoadMore; if (!isAutoLoadMore) { foot.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { onLoadMore(); } }); mListView.setOnScrollListener(null); } else { mListView.setOnScrollListener(new OnScrollListener() { public void onScrollStateChanged(AbsListView view, int scrollState) { if (isEnd && scrollState == SCROLL_STATE_IDLE && hasMore) { onLoadMore(); } } public void onScroll(AbsListView view, int f, int v, int t) { if (f + v >= t - 1) isEnd = true; else isEnd = false; } }); foot.setOnClickListener(null); } } public void setHasMore(boolean hasMore) { this.hasMore = hasMore; } public void removeFoot() { footer_layout.removeAllViews(); } public void addFoot() { footer_layout.removeAllViews(); footer_layout.addView(foot); } }
第二步:設計xml這裏注意的是在自定義控件PullDownListView中內嵌一個ListView控件app
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" > <com.example.widget.PullDownListView android:id="@+id/sreach_list" android:layout_width="fill_parent" android:layout_height="fill_parent" > <ListView android:id="@+id/list" android:layout_width="fill_parent" android:layout_height="fill_parent" android:layout_weight="0.0" android:divider="@null" android:drawSelectorOnTop="false" android:fadingEdgeLength="0.0sp" /> </com.example.widget.PullDownListView> </RelativeLayout>
第三步:編寫MainActivity 實現PullDownListView.OnRefreshListioner回調接口實現功能的演示:ide
import java.util.ArrayList; import java.util.List; import android.app.Activity; import android.os.Bundle; import android.os.Handler; import android.widget.ListView; import com.example.widget.PullDownListView; public class MainActivity extends Activity implements PullDownListView.OnRefreshListioner{ private PullDownListView mPullDownView; private ListView mListView; private List<String> list = new ArrayList<String>(); private MyAdapter adapter; private Handler mHandler = new Handler(); private int maxAount = 20;//設置了最大數據值 @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mPullDownView = (PullDownListView) findViewById(R.id.sreach_list); mPullDownView.setRefreshListioner(this); mListView = mPullDownView.mListView; addLists(10); adapter = new MyAdapter(this,list); mPullDownView.setMore(true);//這裏設置true表示還有更多加載,設置爲false底部將不顯示更多 mListView.setAdapter(adapter); } private void addLists(int n){ n += list.size(); for(int i=list.size();i<n;i++){ list.add("選項"+i); } } /** * 刷新,先清空list中數據而後從新加載更新內容 */ public void onRefresh() { mHandler.postDelayed(new Runnable() { public void run() { list.clear(); addLists(10); mPullDownView.onRefreshComplete();//這裏表示刷新處理完成後把上面的加載刷新界面隱藏 mPullDownView.setMore(true);//這裏設置true表示還有更多加載,設置爲false底部將不顯示更多 adapter.notifyDataSetChanged(); } }, 1500); } /** * 加載更多,在原來基礎上在添加新內容 */ public void onLoadMore() { mHandler.postDelayed(new Runnable() { public void run() { addLists(5);//每次加載五項新內容 mPullDownView.onLoadMoreComplete();//這裏表示加載更多處理完成後把下面的加載更多界面(隱藏或者設置字樣更多) if(list.size()<maxAount)//判斷當前list中已添加的數據是否小於最大值maxAount,是那麼久顯示更多不然不顯示 mPullDownView.setMore(true);//這裏設置true表示還有更多加載,設置爲false底部將不顯示更多 else mPullDownView.setMore(false); adapter.notifyDataSetChanged(); } }, 1500); } }
第四:運行效果如圖: 佈局