首页 > 系统 > Android > 正文

Android自定义控件开发实战之实现ListView下拉刷新实例代码

2019-12-12 06:36:03
字体:
来源:转载
供稿:网友

这篇博客为大家介绍一个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高度为0LinearLayout.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 heightprivate 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 durationprivate 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() {@Overridepublic void onGlobalLayout() {mHeaderViewHeight = mHeaderViewContent.getHeight();getViewTreeObserver().removeGlobalOnLayoutListener(this);}});}@Overridepublic 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 contentmHeaderViewContent.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 contentmHeaderViewContent.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 computeScrollinvalidate();}/*** 触屏监听*/@Overridepublic 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; // resetif (getFirstVisiblePosition() == 0) {// invoke refreshif (!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);}@Overridepublic void computeScroll() {if (mScroller.computeScrollOffset()) {if (mScrollBack == SCROLLBACK_HEADER) {mHeaderView.setVisiableHeight(mScroller.getCurrY());} else {}postInvalidate();invokeOnScrolling();}super.computeScroll();}@Overridepublic 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) 停止滚动*/@Overridepublic 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");}}}/*** 正在滚动的时候回调,停止滚动时才停止回调,单击时回调一次*/@Overridepublic void onScroll(AbsListView view, int firstVisibleItem,int visibleItemCount, int totalItemCount) {// send to user's listenermTotalItemCount = 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 {//自定义的ListViewprivate 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. */@Overridepublic 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("刚刚");}@Overridepublic void onRefresh() {mHandler.postDelayed(new Runnable() {@Overridepublic 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);}@Overridepublic void onLoadMore() {mHandler.postDelayed(new Runnable() {@Overridepublic void run() {geneItems();mAdapter.notifyDataSetChanged();onLoad();}}, 2000);}}</span> 

重要的代码都总结在这了,希望给大家带来帮助,谢谢。

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表