Listview is often combined with refresh and pull-up loading for more use. This article summarizes three commonly used solutions to explain them separately.
Solution 1: Add head layout and foot layout
The Android system provides listview with two APIs: addfootview and addheadview. This allows you to directly customize a View, and refresh and pull-up load in the form of adding views.
Implementation steps
1. Create a class to inherit ListView: class PullToRefreshListView extends ListView;
2. Add HeadView to the construction method: addHeaderView(headView);
3. Get the height of the HeadView. There are two methods to measure the height of the control. GetMeasuredHeight and getHeight. GetMeasuredHeight() can only be obtained after the onMeasure method is executed; getHeight() can only be obtained after the onLayout method is executed;
4. Show and hide headView, which is implemented through setpadding. When the first visible item is item 0, you need to set the paddingTop of the HeadView to display the HeadView.
Display: (0,0,0,0);
Hide: (0,-headViewHeight,0,0);
5. The judgment of pull-down refreshing three states. When moving, when paddingTop < 0, it means that the HeadView is not fully displayed and enters the pull-down refresh state; when paddingTop >= 0, it means that the HeadView has been fully displayed and entered the release and new state; when the finger is raised, and the current state is the release and refresh state, it enters the refresh state; when it is already the "refreshing" state, the "pull-down refresh" and "refreshing" operations are not allowed. Add judgment in the Move event. If the refresh state is already, the pull-down operation will not be processed.
6. Rotate the pull-down arrow. Pull down refresh is down, and when releasing refresh is up. Rotating animation is implemented through attribute animation. When hiding the arrow, clear the animation: iv_arrow.clearAnimation(); If the animation effect is not hidden, it can still be seen after setting.
7. When the HeadView is displayed, the processing when the finger is released. If the "refreshing" state is released, the headVie will be fully displayed; if the "pull down refreshing" state is released, the headView will be completely hidden.
8. Add FooterView: addFooterView(footerView). When the ListView is in an idle state and the last visible item is the last data in the ListView, the footview will not automatically slide up to display the FooterView, so you need to manually set: setSelection(getCount() - 1); that is, select the last one.
9. Add callback listener. When the ListView is in refreshed, the onRefreshing() method will be called; when the ListView is loading more, the onLoadMore() will be called. Notify the control to complete loading after the loading is completed.
Specific implementation:
import ; import ; import ; import ; import ; import ; import ; import ; import ; import ; import ; public class PullToRefreshListView extends ListView { private View headerView; private float downY; private int headerViewHeight; /** Status: Pull down to refresh */ private static final int STATE_PULL_TO_REFRESH = 0; /** Status: Release Refresh */ private static final int STATE_RELEASE_REFRESH = 1; /** Status: Refreshing */ private static final int STATE_REFRESHING = 2; /** Current status */ private int currentState = STATE_PULL_TO_REFRESH; // The default is the drop-down refresh state private ImageView iv_arrow; private ProgressBar progress_bar; private TextView tv_state; private RotateAnimation upAnim; private RotateAnimation downAnim; private OnRefreshingListener mOnRefreshingListener; private View footerView; private int footerViewHeight; /** More loading */ private boolean loadingMore; public PullToRefreshListView(Context context, AttributeSet attrs) { super(context, attrs); initHeaderView(); initFooterView(); } private void initHeaderView() { headerView = (getContext(), .header_view, null); iv_arrow = (ImageView) (.iv_arrow); progress_bar = (ProgressBar) (.progress_bar); showRefreshingProgressBar(false); tv_state = (TextView) (.tv_state); (0, 0); // Actively trigger measurement, onMeasure will be called internally by mesure headerViewHeight = (); hideHeaderView(); (headerView); upAnim = createRotateAnim(0f, -180f); downAnim = createRotateAnim(-180f, -360f); } private void initFooterView() { footerView = (getContext(), .footer_view, null); (0, 0);// Actively trigger measurement, onMeasure will be called internally by mesure footerViewHeight = (); hideFooterView(); (footerView); (new OnScrollListener() { // This method will be called when the scrolling state of ListView changes @Override public void onScrollStateChanged(AbsListView view, int scrollState) { if (scrollState == OnScrollListener.SCROLL_STATE_IDLE // ListView is idle && getLastVisiblePosition() == getCount() - 1 // The last item visible on the interface is the last item in the ListView && loadingMore == false // If you are not currently doing more things loading ) { loadingMore = true; showFooterView(); setSelection(getCount() - 1); if (mOnRefreshingListener != null) { (); } } } // This method will be called when the ListView is scrolling @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { } }); } private void hideFooterView() { int paddingTop = -footerViewHeight; setFooterViewPaddingTop(paddingTop); } private void showFooterView() { int paddingTop = 0; setFooterViewPaddingTop(paddingTop); } private void setFooterViewPaddingTop(int paddingTop) { (0, paddingTop, 0, 0); } /** * Set the circle to display progress * @param showProgressBar If true, then display ProgressBar, otherwise display arrows */ private void showRefreshingProgressBar(boolean showProgressBar) { progress_bar.setVisibility(showProgressBar ? : ); iv_arrow.setVisibility(!showProgressBar ? : ); if (showProgressBar) { iv_arrow.clearAnimation(); // Animated Views must be cleared to truly hide } } /** * Create rotation animation * @param fromDegrees From which angle do you start turning * @param toDegrees Which angle to go to * @return */ private RotateAnimation createRotateAnim(float fromDegrees, float toDegrees) { int pivotXType = RotateAnimation.RELATIVE_TO_SELF; // Reference for rotation point int pivotYType = RotateAnimation.RELATIVE_TO_SELF; // Reference for rotation point float pivotXValue = 0.5f; // The position of the rotation point x direction float pivotYValue = 0.5f; // The position of the rotation point y direction RotateAnimation ra = new RotateAnimation(fromDegrees, toDegrees, pivotXType, pivotXValue, pivotYType, pivotYValue); (300); (true); // Let the animation stay at the end return ra; } /** Hide HeaderView */ private void hideHeaderView() { int paddingTop = -headerViewHeight; setHeaderViewPaddingTop(paddingTop); } /** Show HeaderView */ private void showHeaderView() { int paddingTop = 0; setHeaderViewPaddingTop(paddingTop); } /** * Set the paddingTop of HeaderView * @param paddingTop */ private void setHeaderViewPaddingTop(int paddingTop) { (0, paddingTop, 0, 0); } @Override public boolean onTouchEvent(MotionEvent ev) { switch (()) { case MotionEvent.ACTION_DOWN: downY = (); break; case MotionEvent.ACTION_MOVE: if (currentState == STATE_REFRESHING) { // If the state is currently in "refreshing", you don't need to deal with pull-down refresh return (ev); } int fingerMoveDistanceY = (int) (() - downY); // The distance the fingers move // If it is sliding down and the first item visible on the interface is an item with the index of ListView of 0, we will only handle the pull-down refresh operation if (fingerMoveDistanceY > 0 && getFirstVisiblePosition() == 0) { int paddingTop = -headerViewHeight + fingerMoveDistanceY; setHeaderViewPaddingTop(paddingTop); if (paddingTop < 0 && currentState != STATE_PULL_TO_REFRESH) { // If paddingTop is less than 0, it means that the HeaderView is not fully displayed, and then it will enter the state of pull-down refresh currentState = STATE_PULL_TO_REFRESH; tv_state.setText("Drive down to refresh"); iv_arrow.startAnimation(downAnim); showRefreshingProgressBar(false); // Let the arrow turn } else if (paddingTop >= 0 && currentState != STATE_RELEASE_REFRESH) { // If paddingTop>=0, it means that the HeaderView has been fully displayed, and then it enters the state of releasing and refreshing currentState = STATE_RELEASE_REFRESH; tv_state.setText("Release Refresh"); iv_arrow.startAnimation(upAnim); showRefreshingProgressBar(false); } return true; } break; case MotionEvent.ACTION_UP: if (currentState == STATE_RELEASE_REFRESH) { // If the current state is to release refresh and raise your hand, it enters the refreshing state currentState = STATE_REFRESHING; tv_state.setText("Refreshing"); showRefreshingProgressBar(true); showHeaderView(); if (mOnRefreshingListener != null) { (); } } else if (currentState == STATE_PULL_TO_REFRESH) { // If the refresh state is pulled down when raising your hand, hide the HeaderView hideHeaderView(); } break; } return (ev); } public void setOnRefreshingListener(OnRefreshingListener mOnRefreshingListener) { = mOnRefreshingListener; } /** ListView refresh listener */ public interface OnRefreshingListener { /** This method will be called when ListView can refresh data */ void onRefreshing(); /** This method will be called when ListView can load more */ void onLoadMore(); } /** The operation of refreshing data in the network has been completed */ public void onRefreshComplete() { hideHeaderView(); currentState = STATE_PULL_TO_REFRESH; showRefreshingProgressBar(false); } /** The operation to load more new data has been completed */ public void onLoadmoreComplete() { hideFooterView(); loadingMore = false; } }
Solution 2: Multiple styles of listview display
When setting up the adapter of the listview, two methods can be implemented: getViewTypeCount() and getItemViewType(). The former specifies the type of entry, and the latter returns the specific type. This allows you to design relevant styles according to different types, including pull-up loading more and pull-down refreshing. The two are similar, so only the writing method for loading more is given here. The specific implementation is as follows:
1. Rewrite getViewTypeCount() and getItemViewType(), which includes ordinary item entries and load more entries, so getViewTypeCount() returns a value of 2;
@Override public int getViewTypeCount() { return () + 1; } @Override public int getItemViewType(int position) { if (position == getCount() - 1) { return 0; } else { return addViewType(position); //Construct a method to facilitate subclass modification and add more styles } } public int addViewType(int position) { return 1; }
2. Add layout for different types in getview():
@Override public View getView(int position, View convertView, ViewGroup parent) { BaseHoldle holdle; if (convertView == null) { if (getItemViewType(position) == 0) { // Type 0 means more views should be loaded holdle = getLoadmoreHoldle(); } else { //Otherwise it is a normal view holdle = getSpecialBaseHoldle(position); } } else { holdle = (BaseHoldle) (); } if (getItemViewType(position) == 0) { //Load more views and request the network to obtain data if (havemore()) { (LoadmoreHoldle.LOADMORE_LODING); triggleLoadMoreData(); } else { (LoadmoreHoldle.LOADMORE_NONE); } } else { // Normal view view, request the network to obtain data T data = (T) (position); (data); } mHoldleView = ; (0.6f); (0.6f); (mHoldleView).scaleX(1).scaleY(1).setDuration(400).setInterpolator(new OvershootInterpolator(4)).start(); return mHoldleView; }
3. Specific implementation of loading more views
private BaseHoldle getLoadmoreHoldle() { if (mLoadmoreHoldle == null) { mLoadmoreHoldle = new LoadmoreHoldle(); } return mLoadmoreHoldle; } public class LoadmoreHoldle extends BaseHoldle { @Bind(.item_loadmore_container_loading) LinearLayout itemloadmorecontainerloading; @Bind(.item_loadmore_container_retry) LinearLayout itemloadmorecontainerretry; @Bind(.item_loadmore_tv_retry) TextView item_loadmore_tv_retry; public static final int LOADMORE_LODING = 0; public static final int LOADMORE_ERROR = 1; public static final int LOADMORE_NONE = 2; private int mCurretState; @Override public void refreshHoldleView(Object data) { (); (); mCurretState = (int) data; switch (mCurretState) { case LOADMORE_LODING: (); break; case LOADMORE_ERROR: (); break; case LOADMORE_NONE: break; } } @Override public View ininViewHoldle() { View view = ((), , null); (this, view); return view; } } //holder base class, extract public methodpublic abstract class BaseHoldle<T> { public View mHoldleView; public T mdata; public BaseHoldle() { mHoldleView = ininViewHoldle(); (this); } public void setDataAndRefreshHoldleView(T mdata) { = mdata; refreshHoldleView(mdata); } public abstract void refreshHoldleView(T data); public abstract View ininViewHoldle(); }
Solution 3: SwipeRefreshLayout is implemented and refreshed
SwipeRefreshLayout is incompatible with bottoms, and only the pull-down refresh function does not have more functions to load up. At that time, as a new feature after Android 5.0, it was easy to use and could directly call the system's API. The method of use is also relatively simple. The specific implementation is as follows:
First declare the control and set the color:
refreshLayout = (SwipeRefreshLayout) findViewById(); (this); (.holo_blue_bright, .holo_green_light,.holo_orange_light, .holo_red_light); (.refresh_bg); (.refresh_bg);
Write a class implementation and override the onRefresh() method:
@Override public void onRefresh() { (new Runnable() { @Override public void run() { //Request the network and obtain data (false); } },3000); }
The above is a variety of implementation solutions for Listview drop-down refresh and pull-up loading in Android introduced by the editor. I hope it will be helpful to everyone. If you have any questions, please leave me a message and the editor will reply to everyone in time. Thank you very much for your support for my website!