Android UI 自定義ListView 實現下拉重新整理 載入更多
阿新 • • 發佈:2018-11-08
分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow
也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!
Android UI--自定義ListView(實現下拉重新整理+載入更多)關於實現ListView下拉重新整理和載入更多的實現,我想網上一搜就一堆。不過我就沒發現比較實用的,要不就是實現起來太複雜,要不就是不健全的。因為小巫近期要開發新浪微部落格戶端,需要實現ListView的下拉重新整理,所以就想把這個UI整合到專案當中去,這裡只是一個demo,可以根據專案的需要進行修改。
就不要太在乎介面了哈 :
知道你們想要原始碼了,去下吧:http://download.csdn.net/detail/wwj_748/6373183
自定義ListView:
package com.markupartist.android.widget;import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;import com.markupartist.android.example.pulltorefresh.R;import android.content.Context;import android.util.AttributeSet;import android.util.Log;import android.view.LayoutInflater;import android.view.MotionEvent;import android.view.View;import android.view.ViewGroup;import android.view.animation.LinearInterpolator;import android.view.animation.RotateAnimation;import android.widget.AbsListView;import android.widget.AbsListView.OnScrollListener;import android.widget.ImageView;import android.widget.ListAdapter;import android.widget.ListView;import android.widget.ProgressBar;import android.widget.RelativeLayout;import android.widget.TextView;/** * 2013/8/13 * 自定義ListView,實現OnScrollListener介面 * 此ListView是為實現"下拉重新整理"和"上拉載入更多"而定製的,具體效果可參考新浪微博、騰訊微博 * @author wwj * */public class PullToRefreshListView extends ListView implements OnScrollListener { private static final int TAP_TO_REFRESH = 1; //(未重新整理) private static final int PULL_TO_REFRESH = 2; // 下拉重新整理 private static final int RELEASE_TO_REFRESH = 3; // 釋放重新整理 private static final int REFRESHING = 4; // 正在重新整理 private static final int TAP_TO_LOADMORE = 5; // 未載入更多 private static final int LOADING = 6; // 正在載入 private static final String TAG = "PullToRefreshListView"; private OnRefreshListener mOnRefreshListener; // 重新整理監聽器 /** * Listener that will receive notifications every time the list scrolls. */ private OnScrollListener mOnScrollListener; // 列表滾動監聽器 private LayoutInflater mInflater; // 用於載入佈局檔案 private RelativeLayout mRefreshHeaderView; // 重新整理檢視(也就是頭部那部分) private TextView mRefreshViewText; // 重新整理提示文字 private ImageView mRefreshViewImage; // 重新整理向上向下的那個圖片 private ProgressBar mRefreshViewProgress; // 這裡是圓形進度條 private TextView mRefreshViewLastUpdated; // 最近更新的文字 private RelativeLayout mLoadMoreFooterView; // 載入更多 private TextView mLoadMoreText; // 提示文字 private ProgressBar mLoadMoreProgress; // 載入更多進度條 private int mCurrentScrollState; // 當前滾動位置 private int mRefreshState; // 重新整理狀態 private int mLoadState; // 載入狀態 private RotateAnimation mFlipAnimation; // 下拉動畫 private RotateAnimation mReverseFlipAnimation; // 恢復動畫 private int mRefreshViewHeight; // 重新整理檢視高度 private int mRefreshOriginalTopPadding; // 原始上部間隙 private int mLastMotionY; // 記錄點選位置 public PullToRefreshListView(Context context) { super(context); init(context); } public PullToRefreshListView(Context context, AttributeSet attrs) { super(context, attrs); init(context); } public PullToRefreshListView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(context); } private void init(Context context) { // Load all of the animations we need in code rather than through XML /** 定義旋轉動畫**/ // 引數:1.旋轉開始的角度 2.旋轉結束的角度 3. X軸伸縮模式 4.X座標的伸縮值 5.Y軸的伸縮模式 6.Y座標的伸縮值 mFlipAnimation = new RotateAnimation(0, -180, RotateAnimation.RELATIVE_TO_SELF, 0.5f, RotateAnimation.RELATIVE_TO_SELF, 0.5f); mFlipAnimation.setInterpolator(new LinearInterpolator()); mFlipAnimation.setDuration(250); // 設定持續時間 mFlipAnimation.setFillAfter(true); // 動畫執行完是否停留在執行完的狀態 mReverseFlipAnimation = new RotateAnimation(-180, 0, RotateAnimation.RELATIVE_TO_SELF, 0.5f, RotateAnimation.RELATIVE_TO_SELF, 0.5f); mReverseFlipAnimation.setInterpolator(new LinearInterpolator()); mReverseFlipAnimation.setDuration(250); mReverseFlipAnimation.setFillAfter(true); // 獲取LayoutInflater例項物件 mInflater = (LayoutInflater) context.getSystemService( Context.LAYOUT_INFLATER_SERVICE); // 載入下拉重新整理的頭部檢視 mRefreshHeaderView = (RelativeLayout) mInflater.inflate( R.layout.pull_to_refresh_header, this, false); mRefreshViewText = (TextView) mRefreshHeaderView.findViewById(R.id.pull_to_refresh_text); mRefreshViewImage = (ImageView) mRefreshHeaderView.findViewById(R.id.pull_to_refresh_image); mRefreshViewProgress = (ProgressBar) mRefreshHeaderView.findViewById(R.id.pull_to_refresh_progress); mRefreshViewLastUpdated = (TextView) mRefreshHeaderView.findViewById(R.id.pull_to_refresh_updated_at); mLoadMoreFooterView = (RelativeLayout) mInflater.inflate( R.layout.loadmore_footer, this, false); mLoadMoreText = (TextView) mLoadMoreFooterView.findViewById(R.id.loadmore_text); mLoadMoreProgress = (ProgressBar) mLoadMoreFooterView.findViewById(R.id.loadmore_progress); mRefreshViewImage.setMinimumHeight(50); // 設定圖片最小高度 mRefreshHeaderView.setOnClickListener(new OnClickRefreshListener()); mRefreshOriginalTopPadding = mRefreshHeaderView.getPaddingTop(); mLoadMoreFooterView.setOnClickListener(new OnClickLoadMoreListener()); mRefreshState = TAP_TO_REFRESH; // 初始重新整理狀態 mLoadState = TAP_TO_LOADMORE; addHeaderView(mRefreshHeaderView); // 增加頭部檢視 addFooterView(mLoadMoreFooterView); // 增加尾部檢視 super.setOnScrollListener(this); measureView(mRefreshHeaderView); // 測量檢視 mRefreshViewHeight = mRefreshHeaderView.getMeasuredHeight(); // 得到檢視的高度 } @Override protected void onAttachedToWindow() { setSelection(1); // 設定當前選中的項 } @Override public void setAdapter(ListAdapter adapter) { super.setAdapter(adapter); setSelection(1); } /** * Set the listener that will receive notifications every time the list * scrolls. * * @param l The scroll listener. */ @Override public void setOnScrollListener(AbsListView.OnScrollListener l) { mOnScrollListener = l; } /** * Register a callback to be invoked when this list should be refreshed. * 註冊監聽器 * @param onRefreshListener The callback to run. */ public void setOnRefreshListener(OnRefreshListener onRefreshListener) { mOnRefreshListener = onRefreshListener; } /** * Set a text to represent when the list was last updated. * 設定一個文字來表示最近更新的列表,顯示的是最近更新列表的時間 * @param lastUpdated Last updated at. */ public void setLastUpdated(CharSequence lastUpdated) { if (lastUpdated != null) { mRefreshViewLastUpdated.setVisibility(View.VISIBLE); mRefreshViewLastUpdated.setText("更新於: " + lastUpdated); } else { mRefreshViewLastUpdated.setVisibility(View.GONE); } } @Override public boolean onTouchEvent(MotionEvent event) { final int y = (int) event.getY(); // 獲取點選位置的Y座標 switch (event.getAction()) { case MotionEvent.ACTION_UP: // 手指擡起 if (!isVerticalScrollBarEnabled()) { setVerticalScrollBarEnabled(true); } if (getFirstVisiblePosition() == 0 && mRefreshState != REFRESHING) { if ((mRefreshHeaderView.getBottom() > mRefreshViewHeight || mRefreshHeaderView.getTop() >= 0) && mRefreshState == RELEASE_TO_REFRESH) { // Initiate the refresh mRefreshState = REFRESHING; // 重新整理狀態 prepareForRefresh(); onRefresh(); } else if (mRefreshHeaderView.getBottom() < mRefreshViewHeight || mRefreshHeaderView.getTop() < 0) { // Abort refresh and scroll down below the refresh view resetHeader(); setSelection(1); } } break; case MotionEvent.ACTION_DOWN: mLastMotionY = y; break; case MotionEvent.ACTION_MOVE: applyHeaderPadding(event); break; } return super.onTouchEvent(event); } private void applyHeaderPadding(MotionEvent ev) { final int historySize = ev.getHistorySize(); // Workaround for getPointerCount() which is unavailable in 1.5 // (it's always 1 in 1.5) int pointerCount = 1; try { Method method = MotionEvent.class.getMethod("getPointerCount"); pointerCount = (Integer)method.invoke(ev); } catch (NoSuchMethodException e) { pointerCount = 1; } catch (IllegalArgumentException e) { throw e; } catch (IllegalAccessException e) { System.err.println("unexpected " + e); } catch (InvocationTargetException e) { System.err.println("unexpected " + e); } for (int h = 0; h < historySize; h++) { for (int p = 0; p < pointerCount; p++) { if (mRefreshState == RELEASE_TO_REFRESH) { if (isVerticalFadingEdgeEnabled()) { setVerticalScrollBarEnabled(false); } int historicalY = 0; try { // For Android > 2.0 Method method = MotionEvent.class.getMethod( "getHistoricalY", Integer.TYPE, Integer.TYPE); historicalY = ((Float) method.invoke(ev, p, h)).intValue(); } catch (NoSuchMethodException e) { // For Android < 2.0 historicalY = (int) (ev.getHistoricalY(h)); } catch (IllegalArgumentException e) { throw e; } catch (IllegalAccessException e) { System.err.println("unexpected " + e); } catch (InvocationTargetException e) { System.err.println("unexpected " + e); } // Calculate the padding to apply, we divide by 1.7 to // simulate a more resistant effect during pull. int topPadding = (int) (((historicalY - mLastMotionY) - mRefreshViewHeight) / 1.7); // 設定上、下、左、右四個位置的間隙間隙 mRefreshHeaderView.setPadding( mRefreshHeaderView.getPaddingLeft(), topPadding, mRefreshHeaderView.getPaddingRight(), mRefreshHeaderView.getPaddingBottom()); } } } } /** * Sets the header padding back to original size. * 設定頭部填充會原始大小 */ private void resetHeaderPadding() { mRefreshHeaderView.setPadding( mRefreshHeaderView.getPaddingLeft(), mRefreshOriginalTopPadding, mRefreshHeaderView.getPaddingRight(), mRefreshHeaderView.getPaddingBottom()); } /** * Resets the header to the original state. * 重新設定頭部為原始狀態 */ private void resetHeader() { if (mRefreshState != TAP_TO_REFRESH) { mRefreshState = TAP_TO_REFRESH; resetHeaderPadding(); // Set refresh view text to the pull label mRefreshViewText.setText(R.string.pull_to_refresh_tap_label); // Replace refresh drawable with arrow drawable mRefreshViewImage.setImageResource(R.drawable.ic_pulltorefresh_arrow); // Clear the full rotation animation mRefreshViewImage.clearAnimation(); // Hide progress bar and arrow. mRefreshViewImage.setVisibility(View.GONE); mRefreshViewProgress.setVisibility(View.GONE); } } /** * 重設ListView尾部檢視為初始狀態 */ private void resetFooter() { if(mLoadState != TAP_TO_LOADMORE) { mLoadState = TAP_TO_LOADMORE; // 進度條設定為不可見 mLoadMoreProgress.setVisibility(View.GONE); // 按鈕的文字替換為“載入更多” mLoadMoreText.setText(R.string.loadmore_label); } } /** * 測量檢視的大小 * @param child */ private void measureView(View child) { ViewGroup.LayoutParams p = child.getLayoutParams(); if (p == null) { p = new ViewGroup.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); } int childWidthSpec = ViewGroup.getChildMeasureSpec(0, 0 + 0, p.width); int lpHeight = p.height; int childHeightSpec; if (lpHeight > 0) { childHeightSpec = MeasureSpec.makeMeasureSpec(lpHeight, MeasureSpec.EXACTLY); } else { childHeightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); } child.measure(childWidthSpec, childHeightSpec); } @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { // When the refresh view is completely visible, change the text to say // "Release to refresh..." and flip the arrow drawable. if (mCurrentScrollState == SCROLL_STATE_TOUCH_SCROLL && mRefreshState != REFRESHING) { if (firstVisibleItem == 0) { // 如果第一個可見條目為0 mRefreshViewImage.setVisibility(View.VISIBLE); // 讓指示箭頭變得可見 /**如果頭部檢視相對與父容器的位置大於其自身高度+20或者頭部檢視的頂部位置>0,並且要在重新整理狀態不等於"釋放以重新整理"**/ if ((mRefreshHeaderView.getBottom() > mRefreshViewHeight + 20 || mRefreshHeaderView.getTop() >= 0) && mRefreshState != RELEASE_TO_REFRESH) { mRefreshViewText.setText(R.string.pull_to_refresh_release_label);// 設定重新整理文字為"Release to refresh..." mRefreshViewImage.clearAnimation(); // 清除動畫 mRefreshViewImage.startAnimation(mFlipAnimation); // 啟動動畫 mRefreshState = RELEASE_TO_REFRESH; // 更改重新整理狀態為“釋放以重新整理" } else if (mRefreshHeaderView.getBottom() < mRefreshViewHeight + 20 && mRefreshState != PULL_TO_REFRESH) { mRefreshViewText.setText(R.string.pull_to_refresh_pull_label);// 設定重新整理文字為"Pull to refresh..." if (mRefreshState != TAP_TO_REFRESH) { mRefreshViewImage.clearAnimation(); mRefreshViewImage.startAnimation(mReverseFlipAnimation); } mRefreshState = PULL_TO_REFRESH; } } else { mRefreshViewImage.setVisibility(View.GONE); // 讓重新整理箭頭不可見 resetHeader(); // 重新設定頭部為原始狀態 } } else if (mCurrentScrollState == SCROLL_STATE_FLING && firstVisibleItem == 0 && mRefreshState != REFRESHING) { setSelection(1); } if (mOnScrollListener != null) { mOnScrollListener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount); } } @Override public void onScrollStateChanged(AbsListView view, int scrollState) { mCurrentScrollState = scrollState; if (mOnScrollListener != null) { mOnScrollListener.onScrollStateChanged(view, scrollState); } } /**為重新整理做準備**/ public void prepareForRefresh() { resetHeaderPadding(); mRefreshViewImage.setVisibility(View.GONE); // 去掉重新整理的箭頭 // We need this hack, otherwise it will keep the previous drawable. mRefreshViewImage.setImageDrawable(null); mRefreshViewProgress.setVisibility(View.VISIBLE); // 圓形進度條變為可見 // Set refresh view text to the refreshing label mRefreshViewText.setText(R.string.pull_to_refresh_refreshing_label); mRefreshState = REFRESHING; } /**為載入更多做準備**/ public void prepareForLoadMore() { mLoadMoreProgress.setVisibility(View.VISIBLE); mLoadMoreText.setText(R.string.loading_label); mLoadState = LOADING; } public void onRefresh() { Log.d(TAG, "onRefresh"); if (mOnRefreshListener != null) { mOnRefreshListener.onRefresh(); } } public void OnLoadMore() { Log.d(TAG, "onLoadMore"); if(mOnRefreshListener != null) { mOnRefreshListener.onLoadMore(); } } /** * Resets the list to a normal state after a refresh. * @param lastUpdated Last updated at. */ public void onRefreshComplete(CharSequence lastUpdated) { setLastUpdated(lastUpdated); // 顯示更新時間 onRefreshComplete(); } /** * Resets the list to a normal state after a refresh. */ public void onRefreshComplete() { Log.d(TAG, "onRefreshComplete"); resetHeader(); // If refresh view is visible when loading completes, scroll down to // the next item. if (mRefreshHeaderView.getBottom() > 0) { invalidateViews(); setSelection(1); } } public void onLoadMoreComplete() { Log.d(TAG, "onLoadMoreComplete"); resetFooter(); } /** * Invoked when the refresh view is clicked on. This is mainly used when * there's only a few items in the list and it's not possible to drag the * list. * 點選重新整理 */ private class OnClickRefreshListener implements OnClickListener { @Override public void onClick(View v) { if (mRefreshState != REFRESHING) { prepareForRefresh(); onRefresh(); } } } /** * * @author wwj * 載入更多 */ private class OnClickLoadMoreListener implements OnClickListener { @Override public void onClick(View v) { if(mLoadState != LOADING) { prepareForLoadMore(); OnLoadMore(); } } } /** * Interface definition for a callback to be invoked when list should be * refreshed. * 介面定義一個回撥方法當列表應當被重新整理 */ public interface OnRefreshListener { /** * Called when the list should be refreshed. * 當列表應當被重新整理是呼叫這個方法 * <p> * A call to {@link PullToRefreshListView #onRefreshComplete()} is * expected to indicate that the refresh has completed. */ public void onRefresh(); public void onLoadMore(); }}
使用方法:
package com.markupartist.android.example.pulltorefresh;import java.text.SimpleDateFormat;import java.util.Arrays;import java.util.Date;import java.util.LinkedList;import android.app.Activity;import android.content.Context;import android.os.AsyncTask;import android.os.Bundle;import android.widget.ArrayAdapter;import com.markupartist.android.widget.PullToRefreshListView;import com.markupartist.android.widget.PullToRefreshListView.OnRefreshListener;public class PullToRefreshActivity extends Activity { private LinkedList<String> mListItems; public static PullToRefreshListView weiboListView; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.pull_to_refresh); weiboListView = (PullToRefreshListView) findViewById(R.id.weibolist); // Set a listener to be invoked when the list should be refreshed. weiboListView.setOnRefreshListener(new OnRefreshListener() { @Override public void onRefresh() { // Do work to refresh the list here. new GetDataTask(PullToRefreshActivity.this, 0).execute(); } @Override public void onLoadMore() { new GetDataTask(PullToRefreshActivity.this, 1).execute(); } }); mListItems = new LinkedList<String>(); mListItems.addAll(Arrays.asList(mStrings)); ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, mListItems); weiboListView.setAdapter(adapter); } private class GetDataTask extends AsyncTask<Void, Void, String[]> { private Context context; private int index; public GetDataTask(Context context, int index) { this.context = context; this.index = index; } @Override protected String[] doInBackground(Void... params) { // Simulates a background job. try { Thread.sleep(2000); } catch (InterruptedException e) { ; } return mStrings; } @Override protected void onPostExecute(String[] result) { if (index == 0) { // 將字串“Added after refresh”新增到頂部 mListItems.addFirst("Added after refresh..."); SimpleDateFormat format = new SimpleDateFormat( "yyyy年MM月dd日 HH:mm"); String date = format.format(new Date()); // Call onRefreshComplete when the list has been refreshed. weiboListView.onRefreshComplete(date); } else if (index == 1) { mListIte