http://blog.csdn.net/wwj_748/article/details/12512885
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>