关于实现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. * 当列表应当被刷新是调用这个方法 ** 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 LinkedListmListItems; 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 (); mListItems.addAll(Arrays.asList(mStrings)); ArrayAdapter adapter = new ArrayAdapter (this, android.R.layout.simple_list_item_1, mListItems); weiboListView.setAdapter(adapter); } private class GetDataTask extends AsyncTask { 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) { mListItems.addLast("Added after loadmore..."); weiboListView.onLoadMoreComplete(); } super.onPostExecute(result); } } public static String[] mStrings = { "一条微博", "两条微博", "三条微博", "四条微博", "五条微博", "六条微博", "七条微博", "八条微博", "九条微博", "十条微博", "十一条微博", "十二条微博" }; }
下拉刷新的那个头部布局
/2013.08.22_PullToRefresh_ListView_Demo/res/layout/pull_to_refresh_header.xml
加载更多的底部布局
/2013.08.22_PullToRefresh_ListView_Demo/res/layout/loadmore_footer.xml
/2013.08.22_PullToRefresh_ListView_Demo/res/layout/pull_to_refresh.xml
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。