自定义控件之上拉刷新
自定义控件所需要的布局文件:xlistview_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="wrap_content" > <RelativeLayout android:id="@+id/xlistview_footer_content" android:layout_width="fill_parent" android:layout_height="wrap_content" android:padding="5dp" > <ProgressBar android:id="@+id/xlistview_footer_progressbar" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:visibility="invisible" /> <TextView android:id="@+id/xlistview_footer_hint_textview" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:text="查看更多" /> </RelativeLayout> </LinearLayout>
自定义控件:XListView.java
/* * @(#)XListView.java * * Copyright 2015, ..... */ package com.example.test_my_x_list_view.widget; import android.content.Context; import android.util.AttributeSet; import android.view.LayoutInflater; 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.LinearLayout; import android.widget.ListAdapter; import android.widget.ListView; import android.widget.Scroller; import android.widget.TextView; import com.example.test_my_x_list_view.R; /** * XListView:上拉刷新 * * <p> * <b>History:</b> * <table border="1"> * <tr> * <th>Date</th> * <th>Operator</th> * <th>Memo</th> * </tr> * <tr> * <td>2015-5-13</td> * <td>Zjc</td> * <td>Create</td> * </tr> * </table> * * @author Zjc * * @version 1.0.0 * @since 1.0.0 */ public class XListView extends ListView implements OnScrollListener { private float mLastY = -1; private Scroller mScroller; private OnScrollListener mScrollListener; private IXListViewListener mListViewListener; public 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; private final static int PULL_LOAD_MORE_DELTA = 50; private final static float OFFSET_RADIO = 1.8f; /** * 默认构造方法 * * @param context * @param attrs */ public XListView(Context context, AttributeSet attrs) { super(context, attrs); initWithContext(context); } /** * 默认构造方法 * * @param context * @param attrs * @param defStyle */ public XListView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); initWithContext(context); } /** * 默认构造方法 * * @param context */ public XListView(Context context) { super(context); initWithContext(context); } /** * 根据上下文进行初始化 * * @param context */ private void initWithContext(Context context) { mScroller = new Scroller(context, new DecelerateInterpolator()); super.setOnScrollListener(this); mFooterView = new XListViewFooter(context); } @Override public void setAdapter(ListAdapter adapter) { if (mIsFooterReady == false) { mIsFooterReady = true; addFooterView(mFooterView); // addHeaderView(mFooterView); } super.setAdapter(adapter); } /** * enable or disable pull up load more feature. * * @param enable */ public void setPullLoadEnable(boolean enable) { mEnablePullLoad = enable; if (!mEnablePullLoad) { mFooterView.hide(); mFooterView.setOnClickListener(null); } else { mPullLoading = false; mFooterView.show(); mFooterView.setState(XListViewFooter.STATE_NORMAL); // both "pull up" and "click" will invoke load more. mFooterView.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { startLoadMore(); } }); } } private void startLoadMore() { mPullLoading = true; // 设置底部状态 mFooterView.setState(XListViewFooter.STATE_LOADING); if (mListViewListener != null) { mListViewListener.onLoadMore(); } } /** * stop load more, reset footer view. */ public void stopLoadMore() { if (mPullLoading == true) { mPullLoading = false; mFooterView.setState(XListViewFooter.STATE_NORMAL); } } 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 invokeOnScrolling() { if (mScrollListener instanceof OnXScrollListener) { OnXScrollListener l = (OnXScrollListener) mScrollListener; l.onXScrolling(this); } } private void resetFooterHeight() { int bottomMargin = mFooterView.getBottomMargin(); if (bottomMargin > 0) { mScrollBack = SCROLLBACK_FOOTER; mScroller.startScroll(0, bottomMargin, 0, -bottomMargin, SCROLL_DURATION); invalidate(); } } @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); } break; default: mLastY = -1; // reset if (getLastVisiblePosition() == mTotalItemCount - 1) { // invoke load more. if (mEnablePullLoad && mFooterView.getBottomMargin() > PULL_LOAD_MORE_DELTA) { startLoadMore(); } resetFooterHeight(); } break; } return super.onTouchEvent(ev); } @Override public void computeScroll() { if (mScroller.computeScrollOffset()) { if (mScrollBack != SCROLLBACK_HEADER) { mFooterView.setBottomMargin(mScroller.getCurrY()); } postInvalidate(); invokeOnScrolling(); } super.computeScroll(); } @Override public void setOnScrollListener(OnScrollListener l) { mScrollListener = l; } public interface OnXScrollListener extends OnScrollListener { public void onXScrolling(View view); } public interface IXListViewListener { public void onLoadMore(); } public void setXListViewListener(IXListViewListener l) { mListViewListener = l; } /* * (non-Javadoc) * * @see * android.widget.AbsListView.OnScrollListener#onScrollStateChanged(android * .widget.AbsListView, int) */ @Override public void onScrollStateChanged(AbsListView view, int scrollState) { if (mScrollListener != null) { mScrollListener.onScrollStateChanged(view, scrollState); } } /* * (non-Javadoc) * * @see android.widget.AbsListView.OnScrollListener#onScroll(android.widget. * AbsListView, int, int, int) */ @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { mTotalItemCount = totalItemCount; if (mScrollListener != null) { mScrollListener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount); } } protected 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 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.INVISIBLE); mProgressBar.setVisibility(View.INVISIBLE); mHintView.setVisibility(View.INVISIBLE); if (state == STATE_READY) { mHintView.setVisibility(View.VISIBLE); mHintView.setText("松开载入更多"); } else if (state == STATE_LOADING) { mProgressBar.setVisibility(View.VISIBLE); } else { mHintView.setVisibility(View.VISIBLE); mHintView.setText("查看更多"); } } public void setBottomMargin(int height) { if (height < 0) return; LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) mContentView .getLayoutParams(); lp.bottomMargin = height; mContentView.setLayoutParams(lp); } public int getBottomMargin() { LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) mContentView .getLayoutParams(); return lp.bottomMargin; } /** * normal status */ public void normal() { mHintView.setVisibility(View.VISIBLE); mProgressBar.setVisibility(View.GONE); } /** * loading status */ public void loading() { mHintView.setVisibility(View.GONE); mProgressBar.setVisibility(View.VISIBLE); } /** * hide footer when disable pull load more */ public void hide() { LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) mContentView .getLayoutParams(); lp.height = 0; mContentView.setLayoutParams(lp); } /** * show footer */ public void show() { LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) mContentView .getLayoutParams(); lp.height = LayoutParams.WRAP_CONTENT; mContentView.setLayoutParams(lp); } @SuppressWarnings("deprecation") private void initView(Context context) { mContext = context; LinearLayout moreView = (LinearLayout) LayoutInflater .from(mContext).inflate(R.layout.xlistview_footer, null); addView(moreView); moreView.setLayoutParams(new LinearLayout.LayoutParams( LayoutParams.FILL_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); } } }
MainActivity.java对控件的使用
package com.example.test_my_x_list_view; import android.app.Activity; import android.os.Bundle; import com.example.test_my_x_list_view.widget.XListView; import com.example.test_my_x_list_view.widget.XListView.IXListViewListener; import com.example.test_my_x_list_view.widget.adapter.MyAdapter; public class MainActivity extends Activity { /** 页面控件 */ private XListView x_list_view; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); x_list_view = (XListView) findViewById(R.id.x_list_view); // 设置可加载更多 x_list_view.setPullLoadEnable(true); // 设置上拉刷新监听 x_list_view.setXListViewListener(new IXListViewListener() { @Override public void onLoadMore() { // onLoaded(); } }); // 映射值 x_list_view.setAdapter(new MyAdapter(getApplicationContext())); } }
activity_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" > <com.example.test_my_x_list_view.widget.XListView android:id="@+id/x_list_view" android:layout_width="fill_parent" android:layout_height="fill_parent" /> </RelativeLayout>
适配器MyAdapter.java
/* * @(#)MyAdapter.java * * Copyright 2015, ..... */ package com.example.test_my_x_list_view.widget.adapter; import android.content.Context; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import com.example.test_my_x_list_view.R; /** * ListView适配器 * * <p> * <b>History:</b> * <table border="1"> * <tr> * <th>Date</th> * <th>Operator</th> * <th>Memo</th> * </tr> * <tr> * <td>2015-5-6</td> * <td>Zjc</td> * <td>Create</td> * </tr> * </table> * * @author Zjc * * @version 1.0.0 * @since 1.0.0 */ public class MyAdapter extends BaseAdapter { private LayoutInflater inflater; /** * 默认构造方法 */ public MyAdapter(Context context) { this.inflater = LayoutInflater.from(context); } /* * (non-Javadoc) * * @see android.widget.Adapter#getCount() */ @Override public int getCount() { return 28; // TODO 完成Adapter.getCount方法的构建或覆盖 } /* * (non-Javadoc) * * @see android.widget.Adapter#getItem(int) */ @Override public Object getItem(int position) { return null; // TODO 完成Adapter.getItem方法的构建或覆盖 } /* * (non-Javadoc) * * @see android.widget.Adapter#getItemId(int) */ @Override public long getItemId(int position) { return position; // TODO 完成Adapter.getItemId方法的构建或覆盖 } /* * (non-Javadoc) * * @see android.widget.Adapter#getView(int, android.view.View, * android.view.ViewGroup) */ @Override public View getView(int position, View convertView, ViewGroup parent) { convertView = inflater.inflate(R.layout.item, parent, false); return convertView; } }
item.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" android:background="#607D8B" > <LinearLayout android:layout_width="fill_parent" android:layout_height="60dp" android:gravity="center_vertical" > <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="列表数据" android:textColor="#ffffff" android:textSize="14sp" /> </LinearLayout> </LinearLayout>