Build an android universal pull-down refresh framework-XRefreshView (2), android pull-down refresh

Source: Internet
Author: User
Tags gety

Build an android universal pull-down refresh framework-XRefreshView (2), android pull-down refresh

I. Preface

Since the last release of the XRefreshView (1) framework for building an android universal pull-down and refresh framework, it has been very busy for more than half a month, however, I update and maintain XRefreshView every night after work, and solve some problems according to some friends' opinions. This article is written for this time, because XRefreshView has reached a stage of relatively reliable and stable functions. Next, I will introduce the latest features and usage of XrefreshView and the main implementation ideas.

Ii. Update

2.1 modify the pull-down refresh time mode

Previously, we used refreshView. setRefreshViewType (XRefreshViewType. ABSLISTVIEW). In this way, we set the view type in advance to select the corresponding method for determining the time. Now we don't need to do this. We changed it to the following.

/*** @ Return Whether it is possible for the child view of this layout to * scroll up. override this if the child view is a custom view. */public boolean canChildPullDown () {if (child instanceof AbsListView) {final AbsListView absListView = (AbsListView) child; return canScrollVertically (child,-1) | absListView. getChildCount ()> 0 & (absListView. getFirstVisiblePosition ()> 0 | absListView. getChild At (0 ). getTop () <absListView. getPaddingTop ();} else {return canScrollVertically (child,-1) | child. getScrollY ()> 0 ;}} public boolean canChildPullUp () {if (child instanceof AbsListView) {AbsListView absListView = (AbsListView) child; return canScrollVertically (child, 1) | absListView. getLastVisiblePosition ()! = MTotalItemCount-1;} else if (child instanceof WebView) {WebView webview = (WebView) child; return canScrollVertically (child, 1) | webview. getContentHeight () * webview. getScale ()! = Webview. getHeight () + webview. getScrollY ();} else if (child instanceof ScrollView) {ScrollView scrollView = (ScrollView) child; View childView = scrollView. getChildAt (0); if (childView! = Null) {return canScrollVertically (child, 1) | scrollView. getScrollY ()! = ChildView. getHeight ()-scrollView. getHeight () ;}} else {return canScrollVertically (child, 1);} return true ;} /*** it is used to determine whether a view can be moved up or down in the vertical direction * @ param view v * @ param direction. A negative number indicates that the view slides up, if the number is positive, * @ return */public boolean canScrollVertically (View view, int direction) {return ViewCompat. canScrollVertically (view, direction );}
As you can see, ViewCompat. canScrollVertically (view, direction) can be used to determine whether the view can slide up or down to determine whether the view has reached the top or bottom. After 4.0, it is usually useful in a method, however, this was not the case before 2.3.3. In order to be compatible with 2.3.3, I made some view type judgments and provided a special method to determine whether the results reach the top or bottom through the view type. In general, the common view can accurately determine whether it has reached the top or bottom through the above method, but if you want to refresh a complex or custom view, you can also do this in the following ways:
refreshView.setOnTopRefreshTime(new OnTopRefreshTime() {@Overridepublic boolean isTop() {return stickyLv.getFirstVisiblePosition() == 0;}});refreshView.setOnBottomLoadMoreTime(new OnBottomLoadMoreTime() {@Overridepublic boolean isBottom() {return stickyLv.getLastVisiblePosition() == mTotalItemCount - 1;}});

XRefreshView has handed you the work to determine whether the view has reached the top and bottom. You just need to tell XRefreshView when it is the correct refresh time, unlike the method mentioned in the previous blog, XRefreshView provides two interfaces to separate the timing of top and bottom judgment, it mainly takes into account that pull-down refresh and pull-up loading are not all required.

2.2modification of headview and footview when moving up or down

At the beginning, I moved headview and footview through property animation.

Public static void moveChildAndAddedView (View child, View addView, float childY, float addY, int during, AnimatorListener... listener) {// property animation moves ObjectAnimator y = ObjectAnimator. ofFloat (child, "y", child. getY (), childY); ObjectAnimator y2 = ObjectAnimator. ofFloat (addView, "y", addView. getY (), addY); AnimatorSet animatorSet = new AnimatorSet (); animatorSet. playTogether (y, y2); animatorSet. setDuration (during); if (listener. length> 0) animatorSet. addListener (listener [0]); animatorSet. start ();}
Later I downloaded the open-source animation library NineOldAndroidsNineOldAndroids for compatibility with 2.3.3. What exactly did this library do? In API3.0 (Honeycomb), the SDK adds an android. the class in the animation package is related to the implementation of animation effects. The Honeycomb API can be used to achieve very complex animation effects. However, if a developer wants to use this API in less than 3.0, we need to use the open-source framework Nine Old Androids. In this library, we will judge the SDK version based on the running machine. If it is API3.0 or above, we will use the animation class that comes with Android, otherwise, use the Nine Old Androids library, which is a compatible library. (Note: I directly reference the original text of the blog of the great god Xia an. I have been reading his blog, so I have always admired him, his blog is of excellent quality .) After that, the compatibility problem was solved, but the Xutils group 4 Cannon told me that XRefreshView had a jitter during the drop-down. After knowing this, I began to look for problems, later, we found that the problem of moving headers using property animations is not necessary. Think about it carefully. Attribute animations are actually executed using the get/set method corresponding to the property through reflection, after all, reflection is triggered. When the finger moves, a large number of action_move operations will be triggered. Every action_move operation will perform a reflection, so a lot of reflection work will be done, A large amount of intensive reflection will lead to performance reduction, so jitter occurs. After giving up reflection, I use the view. offsetTopAndBottom (deltaY) method to view the comment of the method.
    /**     * Offset this view's vertical location by the specified number of pixels.     *     * @param offset the number of pixels to offset the view by     */
The translation is to move the view in pixels in the vertical direction. There's nothing to say. It's easy to use and you deserve it.

2.3demo uses stream Layout

Very simple. If you are interested, you can check it out.


2.4 click the button to refresh and support Rebound

You can click the button to refresh the page,

protected void onResume() {super.onResume();xRefreshView.startRefresh();}
You can also set whether to pull and refresh
// Set whether refreshView. setPullRefreshEnable (false) can be refreshed from the drop-down list; // set whether refreshView. setPullLoadEnable (false) can be loaded on top );
Cannon said that it would be better to have a rebound effect if the pull-down refresh and pull-up loading are not allowed, so the current version is supported.

Iii. Implementation
Changes Before and After 3.1

Previously, I treated headview, refreshed childview and footview as three parts, and recorded the positions of each view at the beginning.

/*** Record the Y axis coordinates at the beginning of childView when pulling and loading more data. */private float mChildY =-1; /*** record the Y axis coordinates at the beginning of FootView when pulling and loading more logs. */private float mFootY =-1; /*** record the Y axis coordinates at the beginning of HeadView when pulling and loading more data at the beginning */private float mHeadY =-1;
Then, when the fingers move, the Y axis coordinates of the current views are constantly updated, and then the views are moved one by one. This increases the workload and complexity of the work, later, I thought of taking the three parts as a whole, which is much simpler and there is no need for so many variables.


3.2 Implementation Process

3.2.1 Measurement

/** Measure the width and height of the view. The width is the width set by the user. The height is the sum of the heights of the three child controls, header, content view, and footer. ** @ See android. view. view # onMeasure (int, int) */@ Overrideprotected void onMeasure (int widthMeasureSpec, int heightMeasureSpec) {super. onMeasure (widthMeasureSpec, heightMeasureSpec); int width = MeasureSpec. getSize (widthMeasureSpec); int childCount = getChildCount (); int finalHeight = 0; for (int I = 0; I <childCount; I ++) {View child = getChildAt (I); measureChild (child, widthMeasureSpec, heightMeasureSpec); finalHeight + = child. getMeasuredHeight ();} setMeasuredDimension (width, finalHeight );}
3.2.2 Layout
@ Overrideprotected void onLayout (boolean changed, int l, int t, int r, int B) {super. onLayout (changed, l, t, r, B); LogUtils. d ("onLayout mHolder. mOffsetY = "+ mHolder. mOffsetY); mFootHeight = mFooterView. getMeasuredHeight (); int childCount = getChildCount (); int top = getPaddingTop () + mHolder. mOffsetY; for (int I = 0; I <childCount; I ++) {View child = getChildAt (I); if (child = mHeaderView) {// hide the headerview by moving the headerview height to a distance to the parent. layout (0, top-mHeaderViewHeight, child. getMeasuredWidth (), top);} else {child. layout (0, top, child. getMeasuredWidth (), child. getMeasuredHeight () + top); top + = child. getMeasuredHeight ();}}}

Where

int top = getPaddingTop() + mHolder.mOffsetY;
MHolder. mOffsetY is used to record the offset of the entire view on the Y axis. MHolder is added here. mOffsetY, because the view changes during the drag and refresh process will cause the system to re-measure and layout. With this offset, you can save the current position of the view when the system re-layout, do not restore to the initial position.

3.2.3 process the event and move the view

public boolean dispatchTouchEvent(MotionEvent ev) {final int action = MotionEventCompat.getActionMasked(ev);int deltaY = 0;switch (action) {case MotionEvent.ACTION_DOWN:mHasSendCancelEvent = false;mHasSendDownEvent = false;mLastY = (int) ev.getRawY();mInitialMotionY = mLastY;if (!mScroller.isFinished() && !mPullRefreshing && !mPullLoading) {mScroller.forceFinished(true);}break;case MotionEvent.ACTION_MOVE:if (mPullLoading || mPullRefreshing || !isEnabled()) {return super.dispatchTouchEvent(ev);}mLastMoveEvent = ev;int currentY = (int) ev.getRawY();deltaY = currentY - mLastY;mLastY = currentY;// intercept the MotionEvent only when user is not scrollingif (!isIntercepted && Math.abs(deltaY) < mTouchSlop) {isIntercepted = true;return super.dispatchTouchEvent(ev);}LogUtils.d("isTop=" + mContentView.isTop() + ";isBottom="+ mContentView.isBottom());deltaY = (int) (deltaY / OFFSET_RADIO);if (mContentView.isTop()&& (deltaY > 0 || (deltaY < 0 && mHolder.hasHeaderPullDown()))) {sendCancelEvent();updateHeaderHeight(currentY, deltaY);} else if (mContentView.isBottom()&& (deltaY < 0 || deltaY > 0 && mHolder.hasFooterPullUp())) {sendCancelEvent();updateFooterHeight(deltaY);} else if (mContentView.isTop() && !mHolder.hasHeaderPullDown()|| mContentView.isBottom() && !mHolder.hasFooterPullUp()) {if (deltaY > 0)sendDownEvent();}break;case MotionEvent.ACTION_CANCEL:case MotionEvent.ACTION_UP:// if (mHolder.mOffsetY != 0 && mRefreshViewListener != null// && !mPullRefreshing && !mPullLoading) {// mRefreshViewListener.onRelease(mHolder.mOffsetY);// }if (mContentView.isTop() && mHolder.hasHeaderPullDown()) {// invoke refreshif (mEnablePullRefresh && mHolder.mOffsetY > mHeaderViewHeight) {mPullRefreshing = true;mHeaderView.setState(XRefreshViewState.STATE_REFRESHING);if (mRefreshViewListener != null) {mRefreshViewListener.onRefresh();}}resetHeaderHeight();} else if (mContentView.isBottom() && mHolder.hasFooterPullUp()) {if (mEnablePullLoad) {int offset = 0 - mHolder.mOffsetY - mFootHeight;startScroll(offset, SCROLL_DURATION);startLoadMore();} else {int offset = 0 - mHolder.mOffsetY;startScroll(offset, SCROLL_DURATION);}}mLastY = -1; // resetmInitialMotionY = 0;isIntercepted = true;break;}return super.dispatchTouchEvent(ev);}
First, we can see that all event processing is performed in the dispatchTouchEvent (MotionEvent ev) method, which is previously divided into two parts and intercepted in the onInterceptTouchEvent (MotionEvent ev) method, event processing is performed in onTouchEvent (MotionEvent ev. This is because when the Cannon says that the sub-view is very complicated during the pull-down refresh process, the sub-view sometimes preempys the event and causes the sub-view to get stuck and not refresh. We all know that the child view can request the parent class to not intercept events through requestDisallowInterceptTouchEvent, so the onInterceptTouchEvent method will not be executed, and the pull-down refresh will be unreliable, so to solve this problem, I threw all the processing into the dispatchTouchEvent method.

Let's take a look at the sendCancelEvent () and sendDownEvent () methods.

private void sendCancelEvent() {if (!mHasSendCancelEvent) {setRefreshTime();mHasSendCancelEvent = true;mHasSendDownEvent = false;MotionEvent last = mLastMoveEvent;MotionEvent e = MotionEvent.obtain(last.getDownTime(),last.getEventTime()+ ViewConfiguration.getLongPressTimeout(),MotionEvent.ACTION_CANCEL, last.getX(), last.getY(),last.getMetaState());dispatchTouchEventSupper(e);}}private void sendDownEvent() {if (!mHasSendDownEvent) {LogUtils.d("sendDownEvent");mHasSendCancelEvent = false;mHasSendDownEvent = true;isIntercepted = false;final MotionEvent last = mLastMoveEvent;if (last == null)return;MotionEvent e = MotionEvent.obtain(last.getDownTime(),last.getEventTime(), MotionEvent.ACTION_DOWN, last.getX(),last.getY(), last.getMetaState());dispatchTouchEventSupper(e);}}
The Touch event will certainly be received by the quilt view at the beginning. If it is listview, the click effect of the item will appear. This is normal, but if the pull-down refresh is triggered at this time, at the same time, there is an item click effect, so it does not look very natural. At this time, you can send a cancel event to the sub-view through sendCancelEvent (), so that the item click effect will disappear. In addition, when we pull the headerview, the refresh condition is not met, and the headerview is completely hidden by pushing it up, we should return the event to the subview, the subview can receive and move events and achieve the effect through sendDownEvent.

Finally, let's talk about how to process a mobile view.

When your fingers are dragging,

public void moveView(int deltaY) {mHolder.move(deltaY);mChild.offsetTopAndBottom(deltaY);mHeaderView.offsetTopAndBottom(deltaY);mFooterView.offsetTopAndBottom(deltaY);invalidate();}
public int mOffsetY;public void move(int deltaY) {mOffsetY += deltaY;}
Use the moveView method to move the view and save the offset.

When the finger leaves, move the view through scroller.

mScroller = new Scroller(getContext(), new LinearInterpolator());
A linear interpolation tool is used to indicate constant speed changes when moving.
/***** @ Param offsetY * Sliding offset. The negative value slides upwards. The positive value * @ param duration * Sliding duration */public void startScroll (int offsetY, int duration) {mScroller. startScroll (0, mHolder. mOffsetY, 0, offsetY, duration); invalidate ();}
public void computeScroll() {super.computeScroll();if (mScroller.computeScrollOffset()) {int lastScrollY = mHolder.mOffsetY;int currentY = mScroller.getCurrY();int offsetY = currentY - lastScrollY;lastScrollY = currentY;moveView(offsetY);LogUtils.d("currentY=" + currentY + ";mHolder.mOffsetY="+ mHolder.mOffsetY);} else {LogUtils.d("scroll end mOffsetY=" + mHolder.mOffsetY);}}
From the above, we can see that only one mOffsetY variable is used to store the offset during the entire movement process, and the code is much simpler than the previous one.

Iv. Final description

If you are interested in XRefreshView, follow the XRefreshView on github.

You can also click here to download




Copyright Disclaimer: This article is an original article by the blogger and cannot be reproduced without the permission of the blogger.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.