自定义XlistView(上拉加载,下拉刷新)控件
来源:互联网 发布:网络兼职诈骗专题分析 编辑:程序博客网 时间:2024/06/08 12:30
之前在博客里边讲了Android Studio自带的 下拉刷新控件,今天就把之前写过的自定义XlistView整理了一下,然后把代码贴出来。
Demo传送门 : http://download.csdn.net/detail/as_jon/9652906
首先肯定是先定义一个xml文件,代码如下:
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center"> <com.example.administrator.xlistview.PullToRefreshView android:id="@+id/main_pull_refresh_view_two" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" > <ListView android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/listview_test" > </ListView> </com.example.administrator.xlistview.PullToRefreshView></LinearLayout>
接着就是在你的主界面里边进行整合,代码如下:
package com.example.administrator.stock_sell_storage_app;import android.os.Bundle;import android.support.v4.app.Fragment;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.ListView;import com.example.administrator.dapter.DetailAdapter;import com.example.administrator.xlistview.PullToRefreshView;import com.lidroid.xutils.ViewUtils;import com.lidroid.xutils.view.annotation.ViewInject;public class DetailFragment extends Fragment implements PullToRefreshView.OnHeaderRefreshListener, PullToRefreshView.OnFooterRefreshListener { private View view; @ViewInject(R.id.listview_test)ListView listview_test; @ViewInject(R.id.main_pull_refresh_view_two)PullToRefreshView main_pull_refresh_view_two; DetailAdapter detailAdapter; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { view = inflater.inflate(R.layout.detai_fragment, container, false); ViewUtils.inject(this,view); //注入view和事件 Instantiation(); return view; } public void Instantiation(){ main_pull_refresh_view_two.setOnHeaderRefreshListener(this); main_pull_refresh_view_two.setOnFooterRefreshListener(this); detailAdapter = new DetailAdapter(getActivity()); listview_test.setAdapter(detailAdapter); } //上拉加载 @Override public void onFooterRefresh(PullToRefreshView view) { main_pull_refresh_view_two.postDelayed(new Runnable() { //线程 @Override public void run() { main_pull_refresh_view_two.onFooterRefreshComplete(); //需要刷新的内容可以写在这里,比如网络请求 } },1000); } //下拉刷新 @Override public void onHeaderRefresh(PullToRefreshView view) { main_pull_refresh_view_two.postDelayed(new Runnable() { //线程 @Override public void run() { main_pull_refresh_view_two.onHeaderRefreshComplete(); //需要刷新的内容可以写在这里,比如网络请求 } },1000); }}
大家应该发现了在布局文件中的那个自定义控件,其实就是下边的这个东东……
下面就把这几个类贴出来,PullToRefreshView类
package com.example.administrator.xlistview;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.AdapterView;import android.widget.ImageView;import android.widget.LinearLayout;import android.widget.ProgressBar;import android.widget.ScrollView;import android.widget.TextView;import com.example.administrator.stock_sell_storage_app.R;/** * Created by Administrator on 2016-04-26. */public class PullToRefreshView extends LinearLayout { private static final String TAG = "PullToRefreshView"; // refresh states private static final int PULL_TO_REFRESH = 2; private static final int RELEASE_TO_REFRESH = 3; private static final int REFRESHING = 4; // pull state private static final int PULL_UP_STATE = 0; private static final int PULL_DOWN_STATE = 1; /** * last y */ private int mLastMotionY; /** * lock */ private boolean mLock; /** * header view */ private View mHeaderView; /** * footer view */ private View mFooterView; /** * list or grid */ private AdapterView<?> mAdapterView; /** * scrollview */ private ScrollView mScrollView; /** * header view height */ private int mHeaderViewHeight; /** * footer view height */ private int mFooterViewHeight; /** * header view image */ private ImageView mHeaderImageView; /** * footer view image */ private ImageView mFooterImageView; /** * header tip text */ private TextView mHeaderTextView; /** * footer tip text */ private TextView mFooterTextView; /** * header refresh time */ private TextView mHeaderUpdateTextView; /** * footer refresh time */ // private TextView mFooterUpdateTextView; /** * header progress bar */ private ProgressBar mHeaderProgressBar; /** * footer progress bar */ private ProgressBar mFooterProgressBar; /** * layout inflater */ private LayoutInflater mInflater; /** * header view current state */ private int mHeaderState; /** * footer view current state */ private int mFooterState; /** * pull state,pull up or pull down;PULL_UP_STATE or PULL_DOWN_STATE */ private int mPullState; /** * 变为向下的箭头,改变箭头方向 */ private RotateAnimation mFlipAnimation; /** * 变为逆向的箭头,旋转 */ private RotateAnimation mReverseFlipAnimation; /** * footer refresh listener */ private OnFooterRefreshListener mOnFooterRefreshListener; /** * footer refresh listener */ private OnHeaderRefreshListener mOnHeaderRefreshListener; /** * last update time */// private String mLastUpdateTime; public PullToRefreshView(Context context, AttributeSet attrs) { super(context, attrs); init(); } public PullToRefreshView(Context context) { super(context); init(); } private void init() { // Load all of the animations we need in code rather than through XML 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.from(getContext()); // header view 在此添加,保证是第一个添加到linearlayout的最上端 addHeaderView(); } private void addHeaderView() { // header view mHeaderView = mInflater.inflate(R.layout.refresh_header, this, false); mHeaderImageView = (ImageView) mHeaderView .findViewById(R.id.pull_to_refresh_image); mHeaderTextView = (TextView) mHeaderView .findViewById(R.id.pull_to_refresh_text); mHeaderUpdateTextView = (TextView) mHeaderView .findViewById(R.id.pull_to_refresh_updated_at); mHeaderProgressBar = (ProgressBar) mHeaderView .findViewById(R.id.pull_to_refresh_progress); // header layout measureView(mHeaderView); mHeaderViewHeight = mHeaderView.getMeasuredHeight(); LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT, mHeaderViewHeight); // 设置topMargin的值为负的header View高度,即将其隐藏在最上方 params.topMargin = -(mHeaderViewHeight); // mHeaderView.setLayoutParams(params1); addView(mHeaderView, params); } private void addFooterView() { // footer view mFooterView = mInflater.inflate(R.layout.refresh_footer, this, false); mFooterImageView = (ImageView) mFooterView .findViewById(R.id.pull_to_load_image); mFooterTextView = (TextView) mFooterView .findViewById(R.id.pull_to_load_text); mFooterProgressBar = (ProgressBar) mFooterView .findViewById(R.id.pull_to_load_progress); // footer layout measureView(mFooterView); mFooterViewHeight = mFooterView.getMeasuredHeight(); LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT, mFooterViewHeight); // int top = getHeight(); // params.topMargin // =getHeight();//在这里getHeight()==0,但在onInterceptTouchEvent()方法里getHeight()已经有值了,不再是0; // getHeight()什么时候会赋值,稍候再研究一下 // 由于是线性布局可以直接添加,只要AdapterView的高度是MATCH_PARENT,那么footer view就会被添加到最后,并隐藏 addView(mFooterView, params); } @Override protected void onFinishInflate() { super.onFinishInflate(); // footer view 在此添加保证添加到linearlayout中的最后 addFooterView(); initContentAdapterView(); } /** * init AdapterView like ListView,GridView and so on;or init ScrollView * * @description hylin 2012-7-30下午8:48:12 */ private void initContentAdapterView() { int count = getChildCount(); if (count < 3) { throw new IllegalArgumentException( "this layout must contain 3 child views,and AdapterView or ScrollView must in the second position!"); } View view = null; for (int i = 0; i < count - 1; ++i) { view = getChildAt(i); if (view instanceof AdapterView<?>) { mAdapterView = (AdapterView<?>) view; } if (view instanceof ScrollView) { // finish later mScrollView = (ScrollView) view; } } if (mAdapterView == null && mScrollView == null) { throw new IllegalArgumentException( "must contain a AdapterView or ScrollView in this layout!"); } } private void measureView(View child) { ViewGroup.LayoutParams p = child.getLayoutParams(); if (p == null) { p = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.FILL_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 boolean onInterceptTouchEvent(MotionEvent e) { int y = (int) e.getRawY(); switch (e.getAction()) { case MotionEvent.ACTION_DOWN: // 首先拦截down事件,记录y坐标 mLastMotionY = y; break; case MotionEvent.ACTION_MOVE: // deltaY > 0 是向下运动,< 0是向上运动 int deltaY = y - mLastMotionY; if (isRefreshViewScroll(deltaY)) { return true; } break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: break; } return false; } /* * 如果在onInterceptTouchEvent()方法中没有拦截(即onInterceptTouchEvent()方法中 return * false)则由PullToRefreshView 的子View来处理;否则由下面的方法来处理(即由PullToRefreshView自己来处理) */ @Override public boolean onTouchEvent(MotionEvent event) { if (mLock) { return true; } int y = (int) event.getRawY(); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: // onInterceptTouchEvent已经记录 // mLastMotionY = y; break; case MotionEvent.ACTION_MOVE: int deltaY = y - mLastMotionY; if (mPullState == PULL_DOWN_STATE) { // PullToRefreshView执行下拉 Log.i(TAG, " pull down!parent view move!"); headerPrepareToRefresh(deltaY); // setHeaderPadding(-mHeaderViewHeight); } else if (mPullState == PULL_UP_STATE) { // PullToRefreshView执行上拉 Log.i(TAG, "pull up!parent view move!"); footerPrepareToRefresh(deltaY);//---注释了 } mLastMotionY = y; break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: int topMargin = getHeaderTopMargin(); if (mPullState == PULL_DOWN_STATE) { if (topMargin >= 0) { // 开始刷新 headerRefreshing(); } else { // 还没有执行刷新,重新隐藏 setHeaderTopMargin(-mHeaderViewHeight); } } else if (mPullState == PULL_UP_STATE) { if (Math.abs(topMargin) >= mHeaderViewHeight + mFooterViewHeight) { // 开始执行footer 刷新 footerRefreshing(); } else { // 还没有执行刷新,重新隐藏 setHeaderTopMargin(-mHeaderViewHeight); } } break; } return super.onTouchEvent(event); } /** * 是否应该到了父View,即PullToRefreshView滑动 * * @param deltaY * , deltaY > 0 是向下运动,< 0是向上运动 * @return */ private boolean isRefreshViewScroll(int deltaY) { if (mHeaderState == REFRESHING || mFooterState == REFRESHING) { return false; } //对于ListView和GridView if (mAdapterView != null) { // 子view(ListView or GridView)滑动到最顶端 if (deltaY > 0) { View child = mAdapterView.getChildAt(0); if (child == null) { // 如果mAdapterView中没有数据,不拦截 return false; } if (mAdapterView.getFirstVisiblePosition() == 0 && child.getTop() == 0) { mPullState = PULL_DOWN_STATE; return true; } int top = child.getTop(); int padding = mAdapterView.getPaddingTop(); if (mAdapterView.getFirstVisiblePosition() == 0 && Math.abs(top - padding) <= 8) { //这里之前用3可以判断,但现在不行,还没找到原因 mPullState = PULL_DOWN_STATE; return true; } } else if (deltaY < 0) { View lastChild = mAdapterView.getChildAt(mAdapterView .getChildCount() - 1); if (lastChild == null) { // 如果mAdapterView中没有数据,不拦截 return false; } // 最后一个子view的Bottom小于父View的高度说明mAdapterView的数据没有填满父view, // 等于父View的高度说明mAdapterView已经滑动到最后 if (lastChild.getBottom() <= getHeight() && mAdapterView.getLastVisiblePosition() == mAdapterView .getCount() - 1) { mPullState = PULL_UP_STATE; return true; } } } // 对于ScrollView if (mScrollView != null) { // 子scroll view滑动到最顶端 View child = mScrollView.getChildAt(0); if (deltaY > 0 && mScrollView.getScrollY() == 0) { mPullState = PULL_DOWN_STATE; return true; } else if (deltaY < 0 && child.getMeasuredHeight() <= getHeight() + mScrollView.getScrollY()) { mPullState = PULL_UP_STATE; return true; } } return false; } /** * header 准备刷新,手指移动过程,还没有释放 * * @param deltaY * ,手指滑动的距离 */ private void headerPrepareToRefresh(int deltaY) { int newTopMargin = changingHeaderViewTopMargin(deltaY); // 当header view的topMargin>=0时,说明已经完全显示出来了,修改header view 的提示状态 if (newTopMargin >= 0 && mHeaderState != RELEASE_TO_REFRESH) { mHeaderTextView.setText(R.string.pull_to_refresh_release_label); mHeaderUpdateTextView.setVisibility(View.VISIBLE); mHeaderImageView.clearAnimation(); mHeaderImageView.startAnimation(mFlipAnimation); mHeaderState = RELEASE_TO_REFRESH; } else if (newTopMargin < 0 && newTopMargin > -mHeaderViewHeight) {// 拖动时没有释放 mHeaderImageView.clearAnimation(); mHeaderImageView.startAnimation(mFlipAnimation); // mHeaderImageView. mHeaderTextView.setText(R.string.pull_to_refresh_pull_label); mHeaderState = PULL_TO_REFRESH; } } /** * footer 准备刷新,手指移动过程,还没有释放 移动footer view高度同样和移动header view * 高度是一样,都是通过修改header view的topmargin的值来达到 * * @param deltaY * ,手指滑动的距离 */ private void footerPrepareToRefresh(int deltaY) { int newTopMargin = changingHeaderViewTopMargin(deltaY); // 如果header view topMargin 的绝对值大于或等于header + footer 的高度 // 说明footer view 完全显示出来了,修改footer view 的提示状态 if (Math.abs(newTopMargin) >= (mHeaderViewHeight + mFooterViewHeight) && mFooterState != RELEASE_TO_REFRESH) { mFooterTextView .setText(R.string.pull_to_refresh_footer_release_label); mFooterImageView.clearAnimation(); mFooterImageView.startAnimation(mFlipAnimation); mFooterState = RELEASE_TO_REFRESH; } else if (Math.abs(newTopMargin) < (mHeaderViewHeight + mFooterViewHeight)) { mFooterImageView.clearAnimation(); mFooterImageView.startAnimation(mFlipAnimation); mFooterTextView.setText(R.string.pull_to_refresh_footer_pull_label); mFooterState = PULL_TO_REFRESH; } } /** * 修改Header view top margin的值 * * @description * @param deltaY * @return hylin 2012-7-31下午1:14:31 */ private int changingHeaderViewTopMargin(int deltaY) { LayoutParams params = (LayoutParams) mHeaderView.getLayoutParams(); float newTopMargin = params.topMargin + deltaY * 0.3f; //这里对上拉做一下限制,因为当前上拉后然后不释放手指直接下拉,会把下拉刷新给触发了,感谢网友yufengzungzhe的指出 //表示如果是在上拉后一段距离,然后直接下拉 if(deltaY>0&&mPullState == PULL_UP_STATE&&Math.abs(params.topMargin) <= mHeaderViewHeight){ return params.topMargin; } //同样地,对下拉做一下限制,避免出现跟上拉操作时一样的bug if(deltaY<0&&mPullState == PULL_DOWN_STATE&&Math.abs(params.topMargin)>=mHeaderViewHeight){ return params.topMargin; } params.topMargin = (int) newTopMargin; mHeaderView.setLayoutParams(params); invalidate(); return params.topMargin; } /** * header refreshing * * @description hylin 2012-7-31上午9:10:12 */ private void headerRefreshing() { mHeaderState = REFRESHING; setHeaderTopMargin(0); mHeaderImageView.setVisibility(View.GONE); mHeaderImageView.clearAnimation(); mHeaderImageView.setImageDrawable(null); mHeaderProgressBar.setVisibility(View.VISIBLE); mHeaderTextView.setText(R.string.pull_to_refresh_refreshing_label); if (mOnHeaderRefreshListener != null) { mOnHeaderRefreshListener.onHeaderRefresh(this); } } /** * footer refreshing * * @description hylin 2012-7-31上午9:09:59 */ private void footerRefreshing() { mFooterState = REFRESHING; int top = mHeaderViewHeight + mFooterViewHeight; setHeaderTopMargin(-top); mFooterImageView.setVisibility(View.GONE); mFooterImageView.clearAnimation(); mFooterImageView.setImageDrawable(null); mFooterProgressBar.setVisibility(View.VISIBLE); mFooterTextView .setText(R.string.pull_to_refresh_footer_refreshing_label); if (mOnFooterRefreshListener != null) { mOnFooterRefreshListener.onFooterRefresh(this); } } /** * 设置header view 的topMargin的值 * * @description * @param topMargin * ,为0时,说明header view 刚好完全显示出来; 为-mHeaderViewHeight时,说明完全隐藏了 * hylin 2012-7-31上午11:24:06 */ private void setHeaderTopMargin(int topMargin) { LayoutParams params = (LayoutParams) mHeaderView.getLayoutParams(); params.topMargin = topMargin; mHeaderView.setLayoutParams(params); invalidate(); } /** * header view 完成更新后恢复初始状态 * * @description hylin 2012-7-31上午11:54:23 */ public void onHeaderRefreshComplete() { setHeaderTopMargin(-mHeaderViewHeight); mHeaderImageView.setVisibility(View.VISIBLE); mHeaderImageView.setImageResource(R.mipmap.ic_pulltorefresh_arrow); mHeaderTextView.setText(R.string.pull_to_refresh_pull_label); mHeaderProgressBar.setVisibility(View.GONE); // mHeaderUpdateTextView.setText(""); mHeaderState = PULL_TO_REFRESH; } /** * Resets the list to a normal state after a refresh. * * @param lastUpdated * Last updated at. */ public void onHeaderRefreshComplete(CharSequence lastUpdated) { setLastUpdated(lastUpdated); onHeaderRefreshComplete(); } /** * footer view 完成更新后恢复初始状态 */ public void onFooterRefreshComplete() { setHeaderTopMargin(-mHeaderViewHeight); mFooterImageView.setVisibility(View.VISIBLE); mFooterImageView.setImageResource(R.mipmap.ic_pulltorefresh_arrow_up); mFooterTextView.setText(R.string.pull_to_refresh_footer_pull_label); mFooterProgressBar.setVisibility(View.GONE);// mHeaderUpdateTextView.setText(""); mFooterState = PULL_TO_REFRESH; } /** * Set a text to represent when the list was last updated. * * @param lastUpdated * Last updated at. */ public void setLastUpdated(CharSequence lastUpdated) { if (lastUpdated != null) { mHeaderUpdateTextView.setVisibility(View.VISIBLE); mHeaderUpdateTextView.setText(lastUpdated); } else { mHeaderUpdateTextView.setVisibility(View.GONE); } } /** * 获取当前header view 的topMargin * * @description * @return hylin 2012-7-31上午11:22:50 */ private int getHeaderTopMargin() { LayoutParams params = (LayoutParams) mHeaderView.getLayoutParams(); return params.topMargin; }// /**// * lock// *// * @description hylin 2012-7-27下午6:52:25// */// private void lock() {// mLock = true;// }//// /**// * unlock// *// * @description hylin 2012-7-27下午6:53:18// */// private void unlock() {// mLock = false;// } /** * set headerRefreshListener * * @description * @param headerRefreshListener * hylin 2012-7-31上午11:43:58 */ public void setOnHeaderRefreshListener( OnHeaderRefreshListener headerRefreshListener) { mOnHeaderRefreshListener = headerRefreshListener; } public void setOnFooterRefreshListener( OnFooterRefreshListener footerRefreshListener) { mOnFooterRefreshListener = footerRefreshListener; } /** * Interface definition for a callback to be invoked when list/grid footer * view should be refreshed. */ public interface OnFooterRefreshListener { public void onFooterRefresh(PullToRefreshView view); } /** * Interface definition for a callback to be invoked when list/grid header * view should be refreshed. */ public interface OnHeaderRefreshListener { public void onHeaderRefresh(PullToRefreshView view); }}
XListView类:
package com.example.administrator.xlistview;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.administrator.stock_sell_storage_app.R;/** * @file XListView.java * @package me.maxwin.view * @create Mar 18, 2012 6:28:41 PM * @author Maxwin * @description An ListView support (a) Pull down to refresh, (b) Pull up to load more. * Implement IXListViewListener, and see stopRefresh() / stopLoadMore(). */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 // the interface to trigger refresh and load more. private IXListViewListener mListViewListener; // -- header view private XListViewHeader mHeaderView; // header view content, use it to calculate the Header's height. And hide it // when disable pull refresh. private RelativeLayout mHeaderViewContent; private TextView mHeaderTimeView; private int mHeaderViewHeight; // header view's height private boolean mEnablePullRefresh = true; private boolean mPullRefreshing = false; // is refreashing. // -- footer view private XListViewFooter mFooterView; 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); } private void initWithContext(Context context) { mScroller = new Scroller(context, new DecelerateInterpolator()); // XListView need the scroll event, and it will dispatch the event to // user's listener (as a proxy). super.setOnScrollListener(this); // init header view mHeaderView = new XListViewHeader(context); mHeaderViewContent = (RelativeLayout) mHeaderView .findViewById(R.id.xlistview_header_content); mHeaderTimeView = (TextView) mHeaderView .findViewById(R.id.xlistview_header_time); addHeaderView(mHeaderView); // init footer view mFooterView = new XListViewFooter(context); // init header height mHeaderView.getViewTreeObserver().addOnGlobalLayoutListener( new OnGlobalLayoutListener() { @SuppressWarnings("deprecation") @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) { mIsFooterReady = true; addFooterView(mFooterView); } super.setAdapter(adapter); } /** * enable or disable pull down refresh feature. * * @param enable */ public void setPullRefreshEnable(boolean enable) { mEnablePullRefresh = enable; if (!mEnablePullRefresh) { // disable, hide the content mHeaderViewContent.setVisibility(View.INVISIBLE); } else { mHeaderViewContent.setVisibility(View.VISIBLE); } } /** * 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(); } }); } } /** * stop refresh, reset header view. */ public void stopRefresh() { if (mPullRefreshing == true) { mPullRefreshing = false; resetHeaderHeight(); } } /** * stop load more, reset footer view. */ public void stopLoadMore() { if (mPullLoading == true) { mPullLoading = false; mFooterView.setState(XListViewFooter.STATE_NORMAL); } } /** * 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); } } 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(); } private void updateFooterHeight(float delta) { int height = mFooterView.getBottomMargin() + (int) delta; if (mEnablePullLoad && !mPullLoading) { if (height > PULL_LOAD_MORE_DELTA) { // height enough to invoke load // more. mFooterView.setState(XListViewFooter.STATE_READY); } else { mFooterView.setState(XListViewFooter.STATE_NORMAL); } } mFooterView.setBottomMargin(height); // setSelection(mTotalItemCount - 1); // scroll to bottom } private void resetFooterHeight() { int bottomMargin = mFooterView.getBottomMargin(); if (bottomMargin > 0) { mScrollBack = SCROLLBACK_FOOTER; mScroller.startScroll(0, bottomMargin, 0, -bottomMargin, SCROLL_DURATION); invalidate(); } } private void startLoadMore() { mPullLoading = true; mFooterView.setState(XListViewFooter.STATE_LOADING); if (mListViewListener != null) { mListViewListener.onLoadMore(); } } @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(); System.out.println("数据监测:" + getFirstVisiblePosition() + "---->" + getLastVisiblePosition()); 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 && (mFooterView.getBottomMargin() > 0 || deltaY < 0)) { // last item, already pulled up or want to pull up. updateFooterHeight(-deltaY / OFFSET_RADIO); } break; default: mLastY = -1; // reset if (getFirstVisiblePosition() == 0) { // invoke refresh if (mEnablePullRefresh && mHeaderView.getVisiableHeight() > mHeaderViewHeight) { mPullRefreshing = true; mHeaderView.setState(XListViewHeader.STATE_REFRESHING); if (mListViewListener != null) { mListViewListener.onRefresh(); } } resetHeaderHeight(); } 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) { 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) { // send to user's listener mTotalItemCount = totalItemCount; if (mScrollListener != null) { mScrollListener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount); } } public void setXListViewListener(IXListViewListener l) { mListViewListener = l; } /** * you can listen ListView.OnScrollListener or this one. it will invoke * onXScrolling when header/footer scroll back. */ public interface OnXScrollListener extends OnScrollListener { public void onXScrolling(View view); } /** * implements this interface to get refresh/load more event. */ public interface IXListViewListener { public void onRefresh(); public void onLoadMore(); }}
XListViewFooter类:
package com.example.administrator.xlistview;import android.content.Context;import android.util.AttributeSet;import android.view.LayoutInflater;import android.view.View;import android.widget.LinearLayout;import android.widget.TextView;import com.example.administrator.stock_sell_storage_app.R;/** * Created by Administrator on 2016-04-26. *//** * @file XFooterView.java * @create Mar 31, 2012 9:33:43 PM * @author Maxwin * @description XListView's footer */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 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(R.string.xlistview_footer_hint_ready); } else if (state == STATE_LOADING) { mProgressBar.setVisibility(View.VISIBLE); } else { mHintView.setVisibility(View.VISIBLE); mHintView.setText(R.string.xlistview_footer_hint_normal); } } 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); }}
XListViewHeader类:
package com.example.administrator.xlistview;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.administrator.stock_sell_storage_app.R;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); } @SuppressWarnings("deprecation") private void initView(Context context) { // 初始情况,设置下拉刷新view高度为0 LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams( LayoutParams.FILL_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); } 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(); }}
OK!大家可以根据步骤把这几个类放在一个文件目录下。接下来还有一些相关的xml文件,我也一并贴在下边了。
xml(一) refresh_footer
<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/pull_to_refresh_header" android:layout_width="fill_parent" android:layout_height="wrap_content" android:background="@android:color/white" android:gravity="center" android:paddingBottom="10dip" android:paddingTop="10dip" > <ProgressBar android:id="@+id/pull_to_load_progress" style="?android:attr/progressBarStyleSmall" android:layout_width="30dp" android:layout_height="30dp" android:layout_marginLeft="90dp" android:layout_centerVertical="true" android:visibility="gone" /> <ImageView android:id="@+id/pull_to_load_image" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:layout_marginLeft="95dp" android:gravity="center" android:src="@mipmap/ic_pulltorefresh_arrow_up" android:visibility="visible" /> <TextView android:id="@+id/pull_to_load_text" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_gravity="center" android:gravity="center" android:textColor="#000000" android:text="@string/pull_to_refresh_footer_pull_label" android:textAppearance="?android:attr/textAppearanceMedium" android:textStyle="bold" /></RelativeLayout>
xml(二) refresh_header
<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/pull_to_refresh_header" android:background="@android:color/white" android:layout_width="fill_parent" android:layout_height="wrap_content" android:gravity="center" android:paddingBottom="15dip" android:paddingTop="10dip" > <ProgressBar android:id="@+id/pull_to_refresh_progress" style="?android:attr/progressBarStyleSmall" android:layout_width="30dp" android:layout_height="30dp" android:layout_marginLeft="90dp" android:layout_centerVertical="true" android:visibility="invisible" /> <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="95dp" android:layout_marginTop="5dp" android:gravity="center" android:src="@mipmap/ic_pulltorefresh_arrow" android:visibility="visible" /> <TextView android:id="@+id/pull_to_refresh_text" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_gravity="center" android:layout_marginTop="5dp" android:gravity="center" android:textColor="#000000" android:text="@string/pull_to_refresh_pull_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="30dip" android:layout_below="@+id/pull_to_refresh_text" android:text="更新于:刚刚" android:layout_gravity="center" android:gravity="center" android:textAppearance="?android:attr/textAppearanceSmall" android:visibility="gone" /></RelativeLayout>
xml(三) xlistview_footer
<?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="10dp" > <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="@string/xlistview_footer_hint_normal" /> </RelativeLayout></LinearLayout>
xml(四) xlistview_header
<?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" android:gravity="bottom" > <RelativeLayout android:id="@+id/xlistview_header_content" android:layout_width="fill_parent" android:layout_height="60dp" > <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:gravity="center" android:orientation="vertical" android:id="@+id/xlistview_header_text"> <TextView android:id="@+id/xlistview_header_hint_textview" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/xlistview_header_hint_normal" /> <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="3dp" > <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/xlistview_header_last_time" android:textSize="12sp" /> <TextView android:id="@+id/xlistview_header_time" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="12sp" /> </LinearLayout> </LinearLayout> <ImageView android:id="@+id/xlistview_header_arrow" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignLeft="@id/xlistview_header_text" android:layout_centerVertical="true" android:layout_marginLeft="-35dp" android:src="@mipmap/xlistview_arrow" /> <ProgressBar android:id="@+id/xlistview_header_progressbar" android:layout_width="30dp" android:layout_height="30dp" android:layout_alignLeft="@id/xlistview_header_text" android:layout_centerVertical="true" android:layout_marginLeft="-40dp" android:visibility="invisible" /> </RelativeLayout></LinearLayout>
好的,到了这里的话,基本上xml文件都来了,最后,再把一些资源文件也贴出来,包括里边用到的一些图片
<!--XlistView--> <string name="pull_to_refresh_refreshing_label">刷新\u2026</string> <string name="pull_to_refresh_footer_release_label">松开后加载</string> <string name="pull_to_refresh_footer_refreshing_label">加载中\u2026</string> <string name="pull_to_refresh_release_label">松开后刷新</string> <string name="pull_to_refresh_footer_pull_label">上拉加载更多</string> <string name="pull_to_refresh_pull_label">下拉刷新</string> <string name="xlistview_header_hint_normal">下拉刷新</string> <string name="xlistview_header_hint_ready">松开刷新数据</string> <string name="xlistview_header_hint_loading">正在加载...</string> <string name="xlistview_header_last_time">上次更新时间:</string> <string name="xlistview_footer_hint_normal">查看更多</string> <string name="xlistview_footer_hint_ready">松开载入更多</string>
图片以及一些资源文件都贴上去了,到此处为止一个自定义的XlistView控件就实现了。把效果贴出来给他家看看,虽然是有点丑(- -)。
还是觉得有点丑…… 不过都是自定义的,大家可以按照自己的喜好来设计。
2 0
- 自定义XlistView(上拉加载,下拉刷新)控件
- 自定义XListView控件实现上拉刷新下拉加载以及关联应用的一点感受
- XlistView上拉刷新下拉加载
- xListView上拉刷新下拉加载
- xListView上拉加载下拉刷新
- XListView上拉加载,下拉刷新
- xlistview上拉加载下拉刷新
- XListView上拉加载下拉刷新
- XListView上拉刷新,下拉加载
- 上拉刷新--下拉加载XListView
- XListView上拉加载下拉刷新
- xlistview上拉刷新、下拉加载
- XListView上拉加载下拉刷新
- XListView上拉加载下拉刷新
- XListView上拉加载下拉刷新
- Xlistview上拉加载下拉刷新
- xlistview上拉刷新,下拉加载
- 上拉刷新下拉加载xlistview-------string
- qemu的virtqueue实现
- 关于函数的几点思考
- Block(块或者闭包)实现页面传值
- Hibernate merge、update与saveOrUpdate方法的区别
- Android源码学习笔记-同步机制
- 自定义XlistView(上拉加载,下拉刷新)控件
- HTTP协议之multipart/form-data请求分析
- Linux -- 安装配置vim
- git常用命令2
- SpringMVC初探
- 【java线程系列】java线程系列之java线程池详解
- 线程同步(1):原子操作,内存屏障,锁综述
- 智能手机下一个方向:将VR体验放进口袋
- Netty in Action (二十四) 第十三章节 UDP的广播事件