Android自定义控件开发实战之实现ListView下拉刷新实例代码
作者:奋斗者—cyf
这篇文章主要介绍了Android自定义控件开发实战之实现ListView下拉刷新实例代码的相关资料,需要的朋友可以参考下
这篇博客为大家介绍一个android常见的功能——ListView下拉刷新:
首先下拉未松手时候手机显示这样的界面:
下面的代码是自定的扎样的控件:
<span style="font-family: comic sans ms,sans-serif; font-size: 16px;">package com.dhsr.smartID.view; import android.content.Context; import android.util.AttributeSet; import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; import android.view.animation.Animation; import android.view.animation.RotateAnimation; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.ProgressBar; import android.widget.TextView; import com.example.sirelinkscanapp.R; /** * 自定义控件,完成下拉时候ListView显示“图标及提示正在刷新……”的布局 * * @author cyf * */ public class XListViewHeader extends LinearLayout { private LinearLayout mContainer; // 图片 private ImageView mArrowImageView; // 圆形进度条 private ProgressBar mProgressBar; private TextView mHintTextView; // 状态 private int mState = STATE_NORMAL; // 动画 private Animation mRotateUpAnim; private Animation mRotateDownAnim; private final int ROTATE_ANIM_DURATION = 180; // 正常 public final static int STATE_NORMAL = 0; // 准备刷新 public final static int STATE_READY = 1; // 刷新中 public final static int STATE_REFRESHING = 2; public XListViewHeader(Context context) { super(context); initView(context); } /** * @param context * @param attrs */ public XListViewHeader(Context context, AttributeSet attrs) { super(context, attrs); initView(context); } /** * “ 松开即可刷新……正在刷新……”的布局 * * @param context */ private void initView(Context context) { // 初始情况,设置下拉刷新view高度为0 LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams( LayoutParams.MATCH_PARENT, 0); mContainer = (LinearLayout) LayoutInflater.from(context).inflate( R.layout.xlistview_header, null); // 加载视图 addView(mContainer, lp); // 居中方式 setGravity(Gravity.BOTTOM); // 初始化控件 mArrowImageView = (ImageView) findViewById(R.id.xlistview_header_arrow); mHintTextView = (TextView) findViewById(R.id.xlistview_header_hint_textview); mProgressBar = (ProgressBar) findViewById(R.id.xlistview_header_progressbar); // 设置动画 mRotateUpAnim = new RotateAnimation(0.0f, -180.0f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); mRotateUpAnim.setDuration(ROTATE_ANIM_DURATION); mRotateUpAnim.setFillAfter(true); mRotateDownAnim = new RotateAnimation(-180.0f, 0.0f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); mRotateDownAnim.setDuration(ROTATE_ANIM_DURATION); mRotateDownAnim.setFillAfter(true); } /** * 根据下拉状态执行相应的功能,开始以及停止相应的动画 * * @param state */ public void setState(int state) { if (state == mState) return; if (state == STATE_REFRESHING) { // 显示进度 mArrowImageView.clearAnimation(); mArrowImageView.setVisibility(View.INVISIBLE); mProgressBar.setVisibility(View.VISIBLE); } else { // 显示箭头图片 mArrowImageView.setVisibility(View.VISIBLE); mProgressBar.setVisibility(View.INVISIBLE); } switch (state) { case STATE_NORMAL: if (mState == STATE_READY) { // 开始动画 mArrowImageView.startAnimation(mRotateDownAnim); } //刷新中 if (mState == STATE_REFRESHING) { // 清除动画 mArrowImageView.clearAnimation(); } mHintTextView.setText(R.string.xlistview_header_hint_normal); break; case STATE_READY: if (mState != STATE_READY) { mArrowImageView.clearAnimation(); mArrowImageView.startAnimation(mRotateUpAnim); mHintTextView.setText(R.string.xlistview_header_hint_ready); } break; case STATE_REFRESHING: mHintTextView.setText(R.string.xlistview_header_hint_loading); break; default: } mState = state; } public void setVisiableHeight(int height) { if (height < 0) height = 0; LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) mContainer .getLayoutParams(); lp.height = height; mContainer.setLayoutParams(lp); } public int getVisiableHeight() { return mContainer.getHeight(); } } </span>
接下来需要自定义自己的ListView继承与android本身的ListView,方便自己添加新的方法。
<span style="font-family: comic sans ms,sans-serif; font-size: 16px;">package com.dhsr.smartID.view; import android.content.Context; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; import android.view.ViewTreeObserver.OnGlobalLayoutListener; 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.RelativeLayout; import android.widget.Scroller; import android.widget.TextView; import com.example.sirelinkscanapp.R; /** * 自定义ListView * * @author cyf * */ public class XListView extends ListView implements OnScrollListener { private final String TAG = "XListView"; private float mLastY = -1; private Scroller mScroller; // 滑动 private OnScrollListener mScrollListener; // 为外界创建监听 private IXListViewListener mListViewListener; // 刚才定义的自定义控件 private XListViewHeader mHeaderView; private RelativeLayout mHeaderViewContent; private TextView mHeaderTimeView; private int mHeaderViewHeight; // header view's height private boolean mEnablePullRefresh = true; private boolean mPullRefreshing = false; // is refreashing. private boolean mEnablePullLoad; private boolean mPullLoading; private boolean mIsFooterReady = false; // total list items, used to detect is at the bottom of listview. private int mTotalItemCount; // for mScroller, scroll back from header or footer. 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 // at bottom, trigger // load more. private final static float OFFSET_RADIO = 1.8f; // support iOS like pull // feature. /** * @param context */ public XListView(Context context) { super(context); initWithContext(context); } public XListView(Context context, AttributeSet attrs) { super(context, attrs); initWithContext(context); } public XListView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); initWithContext(context); } /** * 初始化控件 * * @param context */ private void initWithContext(Context context) { mScroller = new Scroller(context, new DecelerateInterpolator()); super.setOnScrollListener(this); mHeaderView = new XListViewHeader(context); mHeaderViewContent = (RelativeLayout) mHeaderView .findViewById(R.id.xlistview_header_content); mHeaderTimeView = (TextView) mHeaderView .findViewById(R.id.xlistview_header_time); addHeaderView(mHeaderView, null, false); mHeaderView.getViewTreeObserver().addOnGlobalLayoutListener( new OnGlobalLayoutListener() { @Override public void onGlobalLayout() { mHeaderViewHeight = mHeaderViewContent.getHeight(); getViewTreeObserver() .removeGlobalOnLayoutListener(this); } }); } @Override public void setAdapter(ListAdapter adapter) { // make sure XListViewFooter is the last footer view, and only add once. if (mIsFooterReady == false) { if (mEnablePullLoad) { mIsFooterReady = true; // addFooterView(mFooterView); } } super.setAdapter(adapter); } public void setPullRefreshEnable(boolean enable) { mEnablePullRefresh = enable; if (!mEnablePullRefresh) { // disable, hide the content mHeaderViewContent.setVisibility(View.INVISIBLE); } else { mHeaderViewContent.setVisibility(View.VISIBLE); } } /** * 停止刷新 */ public void stopRefresh() { if (mPullRefreshing == true) { mPullRefreshing = false; resetHeaderHeight(); } } // 可使进入Activity时便执行下拉刷新 public void startRefresh() { if (mPullRefreshing == false) { mPullRefreshing = true; mHeaderView.setState(XListViewHeader.STATE_REFRESHING); mHeaderView.setVisiableHeight(90); if (mListViewListener != null) { mListViewListener.onRefresh(); } } } /** * stop load more, reset footer view. */ public void stopLoadMore() { if (mPullLoading == true) { mPullLoading = false; } } /** * set last refresh time * * @param time */ public void setRefreshTime(String time) { mHeaderTimeView.setText(time); } private void invokeOnScrolling() { if (mScrollListener instanceof OnXScrollListener) { OnXScrollListener l = (OnXScrollListener) mScrollListener; l.onXScrolling(this); } } private void updateHeaderHeight(float delta) { mHeaderView.setVisiableHeight((int) delta + mHeaderView.getVisiableHeight()); if (mEnablePullRefresh && !mPullRefreshing) { // 未处于刷新状态,更新箭头 if (mHeaderView.getVisiableHeight() > mHeaderViewHeight) { mHeaderView.setState(XListViewHeader.STATE_READY); } else { mHeaderView.setState(XListViewHeader.STATE_NORMAL); } if (mPullLoading) { // disable, hide the content mHeaderViewContent.setVisibility(View.INVISIBLE); } else { mHeaderViewContent.setVisibility(View.VISIBLE); } } setSelection(0); // scroll to top each time } /** * reset header view's height. */ private void resetHeaderHeight() { int height = mHeaderView.getVisiableHeight(); if (height == 0) // not visible. return; // refreshing and header isn't shown fully. do nothing. if (mPullRefreshing && height <= mHeaderViewHeight) { return; } int finalHeight = 0; // default: scroll back to dismiss header. // is refreshing, just scroll back to show all the header. if (mPullRefreshing && height > mHeaderViewHeight) { finalHeight = mHeaderViewHeight; } mScrollBack = SCROLLBACK_HEADER; mScroller.startScroll(0, height, 0, finalHeight - height, SCROLL_DURATION); // trigger computeScroll 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; // DLog.i(TAG, "deltaY is " + deltaY); mLastY = ev.getRawY(); if (getFirstVisiblePosition() == 0 && (mHeaderView.getVisiableHeight() > 0 || deltaY > 0)) { // the first item is showing, header has shown or pull down. updateHeaderHeight(deltaY / OFFSET_RADIO); invokeOnScrolling(); } else if (getLastVisiblePosition() == mTotalItemCount - 1) { // last item, already pulled up or want to pull up. } break; default: mLastY = -1; // reset if (getFirstVisiblePosition() == 0) { // invoke refresh if (!mPullRefreshing && mEnablePullRefresh && !mPullLoading && mHeaderView.getVisiableHeight() > mHeaderViewHeight) { mPullRefreshing = true; mHeaderView.setState(XListViewHeader.STATE_REFRESHING); // DLog.i(TAG, "invoke refresh"); if (mListViewListener != null) { // 此时执行刷刷新的方法 mListViewListener.onRefresh(); } } resetHeaderHeight(); } else if (getLastVisiblePosition() == mTotalItemCount - 1) { // invoke load more. if (!mPullLoading && mEnablePullLoad && !mPullRefreshing) { // DLog.i(TAG, "invoke load more"); } } break; } return super.onTouchEvent(ev); } @Override public void computeScroll() { if (mScroller.computeScrollOffset()) { if (mScrollBack == SCROLLBACK_HEADER) { mHeaderView.setVisiableHeight(mScroller.getCurrY()); } else { } postInvalidate(); invokeOnScrolling(); } super.computeScroll(); } @Override public void setOnScrollListener(OnScrollListener l) { mScrollListener = l; } /** * 在滚动时回调,回调2-3次,手指没抛开则回调2次,scrollState = 2的这次不回调 第1次:scrollState = * SCROLL_STATE_TOUCH_SCROLL(1) 正在滚动 第2次:scrollState = * SCROLL_STATE_FLING(2)手指做了抛的动作(手指离开屏幕前,用力滑了一下) 第3次:scrollState = * SCROLL_STATE_IDLE(0) 停止滚动 */ @Override public void onScrollStateChanged(AbsListView view, int scrollState) { if (mScrollListener != null) { mScrollListener.onScrollStateChanged(view, scrollState); } // 滑到底部时,自动加载更多。 也可以禁用此逻辑 if (getLastVisiblePosition() == mTotalItemCount - 1 && scrollState == SCROLL_STATE_IDLE) { if (!mPullLoading && mEnablePullLoad && !mPullRefreshing) { // DLog.i(TAG, "invoke load more"); } } } /** * 正在滚动的时候回调,停止滚动时才停止回调,单击时回调一次 */ @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { // send to user's listener mTotalItemCount = totalItemCount; if (mScrollListener != null) { mScrollListener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount); } } public void setXListViewListener(IXListViewListener l) { mListViewListener = l; } /** * 监听ListView的滑动事件 * * @author cyf * */ public interface OnXScrollListener extends OnScrollListener { public void onXScrolling(View view); } /** * 自定义接口 */ public interface IXListViewListener { // 下拉刷新时候执行的方法 public void onRefresh(); // 上拉加载时候执行的方法 public void onLoadMore(); } } </span>
MainActivity的xml文件中的ListView就不要用android的ListView了,用上面自定义的ListView(加包名)
<span style="font-family: comic sans ms,sans-serif; font-size: 16px;">package com.test.andy; import java.util.ArrayList; import com.test.andy.view.XListView; import com.test.andy.view.XListView.IXListViewListener; import com.test.andy.R; import android.app.Activity; import android.os.Bundle; import android.os.Handler; import android.widget.ArrayAdapter; /** * XListViewActivity * 实现IXListViewListener接口是为了实现其中的下拉刷新等方法 * @author cyf * */ public class XListViewActivity extends Activity implements IXListViewListener { //自定义的ListView private XListView mListView; private ArrayAdapter<String> mAdapter; private ArrayList<String> items = new ArrayList<String>(); private Handler mHandler; private int start = 0; private int refreshCnt = 0; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); //初始化的时候加载20条数据 geneItems(); mListView = (XListView) findViewById(R.id.xListView); mListView.setPullLoadEnable(true); mAdapter = new ArrayAdapter<String>(this, R.layout.list_item, items); mListView.setAdapter(mAdapter); // mListView.setPullRefreshEnable(false); mListView.setXListViewListener(this); mHandler = new Handler(); mListView.startRefresh(); } private void geneItems() { for (int i = 0; i != 20; ++i) { items.add("refresh cnt " + (++start)); } } private void onLoad() { mListView.stopRefresh(); mListView.stopLoadMore(); mListView.setRefreshTime("刚刚"); } @Override public void onRefresh() { mHandler.postDelayed(new Runnable() { @Override public void run() { start = ++refreshCnt; items.clear(); geneItems(); // mAdapter.notifyDataSetChanged(); mAdapter = new ArrayAdapter<String>(XListViewActivity.this, R.layout.list_item, items); mListView.setAdapter(mAdapter); onLoad(); } }, 2000); } @Override public void onLoadMore() { mHandler.postDelayed(new Runnable() { @Override public void run() { geneItems(); mAdapter.notifyDataSetChanged(); onLoad(); } }, 2000); } } </span>
重要的代码都总结在这了,希望给大家带来帮助,谢谢。