一、创建header.xml文件作为下拉头。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="80dp"
android:orientation="vertical">
<RelativeLayout
android:id="@+id/xlistview_header_content"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:padding="16dp">
<ProgressBar
android:id="@+id/xlistview_header_progressbar"
style="?android:attr/progressBarStyleSmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginLeft="90dp"
android:visibility="visible" />
<ImageView
android:id="@+id/imageview_header_narrow"
android:layout_width="20dp"
android:layout_height="30dp"
android:layout_centerVertical="true"
android:layout_marginLeft="90dp"
android:src="@mipmap/icon_list_view_down" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:layout_centerVertical="true"
android:gravity="center"
android:orientation="horizontal">
<TextView
android:id="@+id/xlistview_header_hint_textview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="下拉刷新"
android:textColor="@color/theme_color" />
</LinearLayout>
</RelativeLayout>
</LinearLayout>
二、自定义下拉头XListViewHeader,继承LinearLayout
import android.content.Context;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.luo.project.R;
public class XListViewHeader extends LinearLayout {
public final static int STATE_NORMAL = 0;
public final static int STATE_READY = 1;
public final static int STATE_LOADING = 2;
private Context mContext;
private View mContentView;
private View mProgressBar;
private ImageView mNarrow;
private TextView mHintView;
public XListViewHeader(Context context) {
super(context);
initView(context);
}
public XListViewHeader(Context context, AttributeSet attrs) {
super(context, attrs);
initView(context);
}
public void setState(int state) {
mHintView.setVisibility(View.GONE);
mProgressBar.setVisibility(View.GONE);
mHintView.setVisibility(View.GONE);
mNarrow.setVisibility(GONE);
if (state == STATE_READY) {
mHintView.setVisibility(View.VISIBLE);
mNarrow.setImageResource(R.mipmap.icon_list_view_up);
mNarrow.setVisibility(VISIBLE);
mHintView.setText("松开刷新");
} else if (state == STATE_LOADING) {
mHintView.setVisibility(View.VISIBLE);
mHintView.setText("加载中...");
mProgressBar.setVisibility(View.VISIBLE);
} else {
mHintView.setVisibility(View.VISIBLE);
mHintView.setText("下拉刷新");
mNarrow.setImageResource(R.mipmap.icon_list_view_down);
mNarrow.setVisibility(VISIBLE);
}
}
public void setTopMargin(int height) {
if (height < 0) return;
LayoutParams lp = (LayoutParams) mContentView.getLayoutParams();
lp.topMargin = height;
mContentView.setLayoutParams(lp);
}
public int getTopMargin() {
LayoutParams lp = (LayoutParams) mContentView.getLayoutParams();
return lp.topMargin;
}
public void normal() {
mNarrow.setVisibility(VISIBLE);
mHintView.setVisibility(View.VISIBLE);
mProgressBar.setVisibility(View.GONE);
}
public void hide() {
LayoutParams lp = (LayoutParams) mContentView.getLayoutParams();
lp.height = 0;
mContentView.setLayoutParams(lp);
}
public void show() {
LayoutParams lp = (LayoutParams) mContentView.getLayoutParams();
lp.height = LayoutParams.WRAP_CONTENT;
mContentView.setLayoutParams(lp);
}
private void initView(Context context) {
mContext = context;
LinearLayout moreView = (LinearLayout) LayoutInflater.from(mContext).inflate(R.layout.header, null);
addView(moreView);
moreView.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
mContentView = moreView.findViewById(R.id.xlistview_header_content);
mProgressBar = moreView.findViewById(R.id.xlistview_header_progressbar);
mHintView = (TextView) moreView.findViewById(R.id.xlistview_header_hint_textview);
mNarrow = (ImageView) moreView.findViewById(R.id.imageview_header_narrow);
}
}
三、创建底部上拉布局文件footer.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="80dp"
android:orientation="vertical">
<RelativeLayout
android:id="@+id/xlistview_footer_content"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:padding="16dp">
<ProgressBar
android:id="@+id/xlistview_footer_progressbar"
style="?android:attr/progressBarStyleSmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginLeft="90dp"
android:visibility="visible" />
<ImageView
android:id="@+id/imageview_narrow"
android:layout_width="20dp"
android:layout_height="30dp"
android:layout_centerVertical="true"
android:layout_marginLeft="90dp"
android:src="@mipmap/icon_list_view_up" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:layout_centerVertical="true"
android:gravity="center"
android:orientation="horizontal">
<TextView
android:id="@+id/xlistview_footer_hint_textview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/xlistview_footer_hint_normal"
android:textColor="@color/theme_color" />
</LinearLayout>
</RelativeLayout>
</LinearLayout>
四、自定义上拉头XListViewFooter,继承LinearLayout
import android.content.Context;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.luo.project.R;
public class XListViewFooter extends LinearLayout {
public final static int STATE_NORMAL = 0;
public final static int STATE_READY = 1;
public final static int STATE_LOADING = 2;
private Context mContext;
private View mContentView;
private View mProgressBar;
private ImageView mNarrow;
private TextView mHintView;
public XListViewFooter(Context context) {
super(context);
initView(context);
}
public XListViewFooter(Context context, AttributeSet attrs) {
super(context, attrs);
initView(context);
}
public void setState(int state) {
mHintView.setVisibility(View.GONE);
mProgressBar.setVisibility(View.GONE);
mHintView.setVisibility(View.GONE);
mNarrow.setVisibility(GONE);
if (state == STATE_READY) {
mHintView.setVisibility(View.VISIBLE);
mNarrow.setImageResource(R.mipmap.icon_list_view_down);
mNarrow.setVisibility(VISIBLE);
mHintView.setText(R.string.xlistview_footer_hint_ready);
} else if (state == STATE_LOADING) {
mHintView.setVisibility(View.VISIBLE);
mHintView.setText("加载中...");
mProgressBar.setVisibility(View.VISIBLE);
} else {
mHintView.setVisibility(View.VISIBLE);
mHintView.setText(R.string.xlistview_footer_hint_normal);
mNarrow.setImageResource(R.mipmap.icon_list_view_up);
mNarrow.setVisibility(VISIBLE);
}
}
public void setBottomMargin(int height) {
if (height < 0) return;
LayoutParams lp = (LayoutParams) mContentView.getLayoutParams();
lp.bottomMargin = height;
mContentView.setLayoutParams(lp);
}
public int getBottomMargin() {
LayoutParams lp = (LayoutParams) mContentView.getLayoutParams();
return lp.bottomMargin;
}
public void normal() {
mNarrow.setVisibility(VISIBLE);
mHintView.setVisibility(View.VISIBLE);
mProgressBar.setVisibility(View.GONE);
}
public void hide() {
LayoutParams lp = (LayoutParams) mContentView.getLayoutParams();
lp.height = 0;
mContentView.setLayoutParams(lp);
}
public void show() {
LayoutParams lp = (LayoutParams) mContentView.getLayoutParams();
lp.height = LayoutParams.WRAP_CONTENT;
mContentView.setLayoutParams(lp);
}
private void initView(Context context) {
mContext = context;
LinearLayout moreView = (LinearLayout) LayoutInflater.from(mContext).inflate(R.layout.xlistview_footer, null);
addView(moreView);
moreView.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
mContentView = moreView.findViewById(R.id.xlistview_footer_content);
mProgressBar = moreView.findViewById(R.id.xlistview_footer_progressbar);
mHintView = (TextView) moreView.findViewById(R.id.xlistview_footer_hint_textview);
mNarrow = (ImageView) moreView.findViewById(R.id.imageview_narrow);
}
}
五、自定义XListView,继承ListView
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.DecelerateInterpolator;
import android.widget.AbsListView;
import android.widget.AbsListView.OnScrollListener;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.Scroller;
public class XListView extends ListView implements OnScrollListener {
private float mLastY = -1; // save event y
private Scroller mScroller; // used for scroll back
private OnScrollListener mScrollListener; // user's scroll listener
private IXListViewListener mListViewListener;
private XListViewHeader mHeaderView;
private XListViewFooter mFooterView;
private boolean mEnablePullLoad;
private boolean mPullLoading;
private boolean mIsFooterReady = false;
private int mTotalItemCount;
private int mScrollBack;
private final static int SCROLLBACK_HEADER = 0;
private final static int SCROLLBACK_FOOTER = 1;
private final static int SCROLL_DURATION = 400; // scroll back duration
private final static int PULL_LOAD_MORE_DELTA = 50; // when pull up >= 50px
private final static float OFFSET_RADIO = 1.8f; // support iOS like pull
public XListView(Context context) {
super(context);
init(context);
}
public XListView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public XListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context);
}
private void init(Context context) {
super.setOnScrollListener(this);
mScroller = new Scroller(context, new DecelerateInterpolator());
mFooterView = new XListViewFooter(context);
mHeaderView = new XListViewHeader(context);
}
@Override
public void setAdapter(ListAdapter adapter) {
super.setAdapter(adapter);
if (!mIsFooterReady) {
mIsFooterReady = true;
addFooterView(mFooterView);
}
addHeaderView(mHeaderView);
mHeaderView.hide();
}
public void setPullLoadEnable(boolean enable) {
mEnablePullLoad = enable;
if (!mEnablePullLoad) {
mFooterView.hide();
mFooterView.setOnClickListener(null);
setFooterDividersEnabled(false);
} else {
mPullLoading = false;
mFooterView.show();
mFooterView.setState(XListViewFooter.STATE_NORMAL);
setFooterDividersEnabled(true);
mFooterView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
startLoadMore();
}
});
}
}
public void stopLoadMore() {
if (mPullLoading) {
mPullLoading = false;
mFooterView.setState(XListViewFooter.STATE_NORMAL);
}
}
private boolean mIsPullToRefresh = false;
public void stopRefresh() {
if (mIsPullToRefresh) {
mIsPullToRefresh = false;
mHeaderView.setState(XListViewFooter.STATE_NORMAL);
mHeaderView.setTopMargin(0);
mHeaderView.hide();
}
}
private void invokeOnScrolling() {
if (mScrollListener instanceof OnXScrollListener) {
OnXScrollListener l = (OnXScrollListener) mScrollListener;
l.onXScrolling(this);
}
}
private void updateFooterHeight(float delta) {
int height = mFooterView.getBottomMargin() + (int) delta;
if (mEnablePullLoad && !mPullLoading) {
if (height > PULL_LOAD_MORE_DELTA) {
mFooterView.setState(XListViewFooter.STATE_READY);
} else {
mFooterView.setState(XListViewFooter.STATE_NORMAL);
}
}
mFooterView.setBottomMargin(height);
}
private void updateHeaderHeight(float delta) {
int height = mHeaderView.getTopMargin() + (int) delta;
mHeaderView.show();
if (height > PULL_LOAD_MORE_DELTA) {
mHeaderView.setState(XListViewFooter.STATE_READY);
mIsPullToRefresh = true;
} else {
mHeaderView.setState(XListViewFooter.STATE_NORMAL);
mIsPullToRefresh = false;
}
mHeaderView.setTopMargin(height);
}
private void resetFooterHeight() {
int bottomMargin = mFooterView.getBottomMargin();
if (bottomMargin > 0) {
mScrollBack = SCROLLBACK_FOOTER;
mScroller.startScroll(0, bottomMargin, 0, -bottomMargin, SCROLL_DURATION);
invalidate();
}
}
private void resetHeaderHeight() {
int topMargin = mHeaderView.getTopMargin();
if (topMargin > 0) {
mScrollBack = SCROLLBACK_FOOTER;
mScroller.startScroll(0, -topMargin, 0, topMargin, SCROLL_DURATION);
invalidate();
}
}
private void startLoadMore() {
mPullLoading = true;
mFooterView.setState(XListViewFooter.STATE_LOADING);
if (mListViewListener != null) {
mListViewListener.onLoadMore();
}
}
private void startRefresh() {
mIsPullToRefresh = true;
mHeaderView.setState(XListViewFooter.STATE_LOADING);
if (mListViewListener != null) {
mListViewListener.onRefresh();
}
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
if (mLastY == -1) {
mLastY = ev.getRawY();
}
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
mLastY = ev.getRawY();
break;
case MotionEvent.ACTION_MOVE:
final float deltaY = ev.getRawY() - mLastY;
mLastY = ev.getRawY();
if (getLastVisiblePosition() == mTotalItemCount - 1 && (mFooterView.getBottomMargin() > 0 || deltaY < 0)) {
updateFooterHeight(-deltaY / OFFSET_RADIO);
}
if (getFirstVisiblePosition() == 0 && (mHeaderView.getTopMargin() > 0 || deltaY > 0)) {
updateHeaderHeight(deltaY / OFFSET_RADIO);
}
break;
default:
mLastY = -1;
if (getLastVisiblePosition() == mTotalItemCount - 1) {
if (mEnablePullLoad && mFooterView.getBottomMargin() > PULL_LOAD_MORE_DELTA && !mPullLoading) {
startLoadMore();
}
resetFooterHeight();
}
if (getFirstVisiblePosition() == 0) {
if (mHeaderView.getTopMargin() > PULL_LOAD_MORE_DELTA && mIsPullToRefresh) {
mHeaderView.setTopMargin(0);
startRefresh();
}else {
mHeaderView.setTopMargin(0);
mHeaderView.hide();
}
resetHeaderHeight();
}else {
mHeaderView.setTopMargin(0);
mHeaderView.hide();
}
break;
}
return super.onTouchEvent(ev);
}
@Override
public void computeScroll() {
if (mScroller.computeScrollOffset()) {
if (mScrollBack == SCROLLBACK_HEADER) {
//mHeaderView.setVisiableHeight(mScroller.getCurrY());
} else {
mFooterView.setBottomMargin(mScroller.getCurrY());
}
postInvalidate();
invokeOnScrolling();
}
super.computeScroll();
}
@Override
public void setOnScrollListener(OnScrollListener l) {
mScrollListener = l;
}
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
if (mScrollListener != null) {
mScrollListener.onScrollStateChanged(view, scrollState);
}
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
mTotalItemCount = totalItemCount;
if (mScrollListener != null) {
mScrollListener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount);
}
}
public void setXListViewListener(IXListViewListener l) {
mListViewListener = l;
}
public interface OnXScrollListener extends OnScrollListener {
void onXScrolling(View view);
}
public interface IXListViewListener {
void onLoadMore();
void onRefresh();
}
}
六、在Activity里面使用
public class XListViewActivity extends Activity implements XListView.IXListViewListener {
private XListView mListView;
private ListAdapter mListAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
init();
}
private void init() {
findView();
setAdapter();
}
private void findView() {
mListView = (XListView) mRootView.findViewById(R.id.xlistview);
}
private void setAdapter() {
mListView.setXListViewListener(this);
mListAdapter = new ListAdapter(getActivity());
mListView.setAdapter(mListAdapter);
mListView.setPullLoadEnable(false);
}
@Override
public void onLoadMore() {
Log.e("加载更多");
loadDateList();
}
@Override
public void onRefresh() {
mListAdapter.clear();
mListAdapter.addAll(list);
}
private void onFiish(){
mListView.setPullLoadEnable(true);
mListView.stopLoadMore();
mListAdapter.addAll(list);
mListView.stopLoadMore();
}
}