http://blog.csdn.net/wwj_748/article/details/12512885html
Android UI--自定義ListView(實現下拉刷新+加載更多)
關於實現ListView下拉刷新和加載更多的實現,我想網上一搜就一堆。不過我就沒發現比較實用的,要不就是實現起來太複雜,要不就是不健全的。由於小巫近期要開發新浪微博客戶端,須要實現ListView的下拉刷新,因此就想把這個UI整合到項目當中去,這裏只是一個demo,能夠根據項目的須要進行修改。
就不要太在意界面了哈
:
自定義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;
-
-
-
-
-
-
-
-
-
-
- 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;
-
-
-
-
- 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) {
-
-
-
- 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);
-
-
- 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);
- }
-
-
-
-
-
-
-
- @Override
- public void setOnScrollListener(AbsListView.OnScrollListener l) {
- mOnScrollListener = l;
- }
-
-
-
-
-
-
- public void setOnRefreshListener(OnRefreshListener onRefreshListener) {
- mOnRefreshListener = onRefreshListener;
- }
-
-
-
-
-
-
- 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();
-
- 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) {
-
- mRefreshState = REFRESHING;
- prepareForRefresh();
- onRefresh();
- } else if (mRefreshHeaderView.getBottom() < mRefreshViewHeight
- || mRefreshHeaderView.getTop() < 0) {
-
- 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();
-
-
-
- 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 {
-
- Method method = MotionEvent.class.getMethod(
- "getHistoricalY", Integer.TYPE, Integer.TYPE);
- historicalY = ((Float) method.invoke(ev, p, h)).intValue();
- } catch (NoSuchMethodException e) {
-
- 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);
- }
-
-
-
- int topPadding = (int) (((historicalY - mLastMotionY)
- - mRefreshViewHeight) / 1.7);
-
-
- mRefreshHeaderView.setPadding(
- mRefreshHeaderView.getPaddingLeft(),
- topPadding,
- mRefreshHeaderView.getPaddingRight(),
- mRefreshHeaderView.getPaddingBottom());
- }
- }
- }
- }
-
-
-
-
-
- private void resetHeaderPadding() {
- mRefreshHeaderView.setPadding(
- mRefreshHeaderView.getPaddingLeft(),
- mRefreshOriginalTopPadding,
- mRefreshHeaderView.getPaddingRight(),
- mRefreshHeaderView.getPaddingBottom());
- }
-
-
-
-
-
- private void resetHeader() {
- if (mRefreshState != TAP_TO_REFRESH) {
- mRefreshState = TAP_TO_REFRESH;
-
- resetHeaderPadding();
-
-
- mRefreshViewText.setText(R.string.pull_to_refresh_tap_label);
-
- mRefreshViewImage.setImageResource(R.drawable.ic_pulltorefresh_arrow);
-
- mRefreshViewImage.clearAnimation();
-
- mRefreshViewImage.setVisibility(View.GONE);
- mRefreshViewProgress.setVisibility(View.GONE);
- }
- }
-
-
-
-
- private void resetFooter() {
- if(mLoadState != TAP_TO_LOADMORE) {
- mLoadState = TAP_TO_LOADMORE;
-
-
- mLoadMoreProgress.setVisibility(View.GONE);
-
- mLoadMoreText.setText(R.string.loadmore_label);
- }
-
- }
-
-
-
-
-
-
- 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) {
-
-
- if (mCurrentScrollState == SCROLL_STATE_TOUCH_SCROLL
- && mRefreshState != REFRESHING) {
- if (firstVisibleItem == 0) {
- mRefreshViewImage.setVisibility(View.VISIBLE);
-
- if ((mRefreshHeaderView.getBottom() > mRefreshViewHeight + 20
- || mRefreshHeaderView.getTop() >= 0)
- && mRefreshState != RELEASE_TO_REFRESH) {
- mRefreshViewText.setText(R.string.pull_to_refresh_release_label);
- 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);
- 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);
-
- mRefreshViewImage.setImageDrawable(null);
- mRefreshViewProgress.setVisibility(View.VISIBLE);
-
-
- 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();
- }
- }
-
-
-
-
-
- public void onRefreshComplete(CharSequence lastUpdated) {
- setLastUpdated(lastUpdated);
- onRefreshComplete();
- }
-
-
-
-
- public void onRefreshComplete() {
- Log.d(TAG, "onRefreshComplete");
-
- resetHeader();
-
-
-
- if (mRefreshHeaderView.getBottom() > 0) {
- invalidateViews();
- setSelection(1);
- }
- }
-
- public void onLoadMoreComplete() {
- Log.d(TAG, "onLoadMoreComplete");
- resetFooter();
- }
-
-
-
-
-
-
-
- private class OnClickRefreshListener implements OnClickListener {
-
- @Override
- public void onClick(View v) {
- if (mRefreshState != REFRESHING) {
- prepareForRefresh();
- onRefresh();
- }
- }
-
- }
-
-
-
-
-
-
- private class OnClickLoadMoreListener implements OnClickListener {
-
- @Override
- public void onClick(View v) {
- if(mLoadState != LOADING) {
- prepareForLoadMore();
- OnLoadMore();
- }
- }
- }
-
-
-
-
-
-
- public interface OnRefreshListener {
-
-
-
-
-
-
-
- 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;
-
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.pull_to_refresh);
- weiboListView = (PullToRefreshListView) findViewById(R.id.weibolist);
-
-
- weiboListView.setOnRefreshListener(new OnRefreshListener() {
- @Override
- public void onRefresh() {
-
- 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) {
-
- try {
- Thread.sleep(2000);
- } catch (InterruptedException e) {
- ;
- }
- return mStrings;
- }
-
- @Override
- protected void onPostExecute(String[] result) {
- if (index == 0) {
-
- mListItems.addFirst("Added after refresh...");
-
- SimpleDateFormat format = new SimpleDateFormat(
- "yyyy年MM月dd日 HH:mm");
- String date = format.format(new Date());
-
- 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
- <?xml version="1.0" encoding="utf-8"?>
- <!--
- Copyright (C) 2011 Johan Nilsson <http://markupartist.com>
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- -->
-
- <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:gravity="center"
- android:paddingBottom="15dip"
- android:paddingTop="10dip" >
-
-
-
- <ProgressBar
- android:id="@+id/pull_to_refresh_progress"
- style="?android:attr/progressBarStyleSmall"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_centerVertical="true"
- android:layout_marginLeft="30dip"
- android:layout_marginRight="20dip"
- android:layout_marginTop="10dip"
- android:indeterminate="true"
- android:visibility="gone" />
-
-
- <ImageView
- android:id="@+id/pull_to_refresh_image"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:layout_marginLeft="30dip"
- android:layout_marginRight="20dip"
- android:gravity="center"
- android:src="@drawable/ic_pulltorefresh_arrow"
- android:visibility="gone" />
-
-
- <TextView
- android:id="@+id/pull_to_refresh_text"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:gravity="center"
- android:paddingTop="5dip"
- android:text="@string/pull_to_refresh_tap_label"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:textStyle="bold" />
-
- <TextView
- android:id="@+id/pull_to_refresh_updated_at"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:layout_below="@+id/pull_to_refresh_text"
- android:layout_gravity="center"
- android:gravity="center"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:visibility="gone" />
-
- </RelativeLayout>
加載更多的底部佈局
/2013.08.22_PullToRefresh_ListView_Demo/res/layout/loadmore_footer.xml
- <?xml version="1.0" encoding="utf-8"?>
- <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/loadmore_layout"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:gravity="center"
- android:paddingBottom="15dip"
- android:paddingTop="10dip" >
-
- <!-- <Button
- android:id="@+id/loadmore_btn"
- android:layout_width="match_parent"
- android:layout_height="60.0dip"
- android:layout_centerHorizontal="true"
- android:layout_marginTop="0.0dip"
- android:background="@drawable/weibo_list_item_selector"
- android:text="@string/loadmore_label"
- android:textColor="@color/loadmore_btn_selector"
- android:textSize="18.0sp" />
- -->
- <ProgressBar
- android:id="@+id/loadmore_progress"
- style="?android:attr/progressBarStyleSmall"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_centerVertical="true"
- android:layout_marginLeft="30dip"
- android:layout_marginRight="20dip"
- android:layout_marginTop="10dip"
- android:indeterminate="true"
- android:visibility="gone" />
-
- <TextView
- android:id="@+id/loadmore_text"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:gravity="center"
- android:paddingTop="5dip"
- android:text="@string/loadmore_label"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:textStyle="bold" />
-
- </RelativeLayout>
/2013.08.22_PullToRefresh_ListView_Demo/res/layout/pull_to_refresh.xml
- <?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="match_parent"
- android:orientation="vertical" >
-
- <!--
- The PullToRefreshListView replaces a standard ListView widget.
- 自定義列表在這
- -->
-
- <com.markupartist.android.widget.PullToRefreshListView
- android:id="@+id/weibolist"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:cacheColorHint="#00000000"
- android:fastScrollEnabled="true"/>
-
- </LinearLayout>