From 5447a042944d768cf9b68162f192f129ecdf30d4 Mon Sep 17 00:00:00 2001
From: wxj <986798656@qq.com>
Date: Sun, 22 Jan 2017 15:28:49 +0800
Subject: [PATCH 1/6] add horizontally layout
---
library/build.gradle | 1 +
.../HorSwipeToLoadLayout.java | 1583 +++++++++++++++++
library/src/main/res/values/attrs.xml | 3 +-
3 files changed, 1586 insertions(+), 1 deletion(-)
create mode 100644 library/src/main/java/com/aspsine/swipetoloadlayout/HorSwipeToLoadLayout.java
diff --git a/library/build.gradle b/library/build.gradle
index 62bd761..8055fb2 100644
--- a/library/build.gradle
+++ b/library/build.gradle
@@ -22,4 +22,5 @@ dependencies {
compile fileTree(include: ['*.jar'], dir: 'libs')
testCompile 'junit:junit:4.12'
compile 'com.android.support:support-compat:25.1.0'
+ compile 'com.android.support:appcompat-v7:25.1.0'
}
diff --git a/library/src/main/java/com/aspsine/swipetoloadlayout/HorSwipeToLoadLayout.java b/library/src/main/java/com/aspsine/swipetoloadlayout/HorSwipeToLoadLayout.java
new file mode 100644
index 0000000..3233a20
--- /dev/null
+++ b/library/src/main/java/com/aspsine/swipetoloadlayout/HorSwipeToLoadLayout.java
@@ -0,0 +1,1583 @@
+package com.aspsine.swipetoloadlayout;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.support.v4.view.MotionEventCompat;
+import android.support.v4.view.ViewCompat;
+import android.support.v4.view.ViewPager;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.ViewGroup;
+import android.widget.AbsListView;
+import android.widget.Scroller;
+
+public class HorSwipeToLoadLayout extends ViewGroup {
+ private static final String TAG = HorSwipeToLoadLayout.class.getSimpleName();
+ private static final int DEFAULT_SWIPING_TO_REFRESH_TO_DEFAULT_SCROLLING_DURATION = 200;
+ private static final int DEFAULT_RELEASE_TO_REFRESHING_SCROLLING_DURATION = 200;
+ private static final int DEFAULT_REFRESH_COMPLETE_DELAY_DURATION = 300;
+ private static final int DEFAULT_REFRESH_COMPLETE_TO_DEFAULT_SCROLLING_DURATION = 500;
+ private static final int DEFAULT_DEFAULT_TO_REFRESHING_SCROLLING_DURATION = 500;
+ private static final int DEFAULT_SWIPING_TO_LOAD_MORE_TO_DEFAULT_SCROLLING_DURATION = 200;
+ private static final int DEFAULT_RELEASE_TO_LOADING_MORE_SCROLLING_DURATION = 200;
+ private static final int DEFAULT_LOAD_MORE_COMPLETE_DELAY_DURATION = 300;
+ private static final int DEFAULT_LOAD_MORE_COMPLETE_TO_DEFAULT_SCROLLING_DURATION = 300;
+ private static final int DEFAULT_DEFAULT_TO_LOADING_MORE_SCROLLING_DURATION = 300;
+ private static final float DEFAULT_DRAG_RATIO = 0.5F;
+ private static final int INVALID_POINTER = -1;
+ private static final int INVALID_COORDINATE = -1;
+ private HorSwipeToLoadLayout.AutoScroller mAutoScroller;
+ private OnRefreshListener mRefreshListener;
+ private OnLoadMoreListener mLoadMoreListener;
+ private View mHeaderView;
+ private View mTargetView;
+ private View mFooterView;
+ private int mHeaderWidth;
+ private int mFooterWidth;
+ private boolean mHasHeaderView;
+ private boolean mHasFooterView;
+ private boolean mDebug;
+ private float mDragRatio = DEFAULT_DRAG_RATIO;
+ private boolean mAutoLoading;
+ private final int mTouchSlop;
+ private int mStatus = STATUS.STATUS_DEFAULT;
+ private int mHeaderOffset;
+ private int mTargetOffset;
+ private int mFooterOffset;
+ private float mInitDownY;
+ private float mInitDownX;
+ private float mLastY;
+ private float mLastX;
+ private int mActivePointerId;
+ private boolean mRefreshEnabled = true;
+ private boolean mLoadMoreEnabled = true;
+ private int mStyle = STYLE.CLASSIC;
+ private float mRefreshTriggerOffset;
+ private float mLoadMoreTriggerOffset;
+ private float mRefreshFinalDragOffset;
+ private float mLoadMoreFinalDragOffset;
+ /**
+ * ATTRIBUTE:
+ * Scrolling duration swiping to refresh -> default
+ */
+ private int mSwipingToRefreshToDefaultScrollingDuration = DEFAULT_SWIPING_TO_REFRESH_TO_DEFAULT_SCROLLING_DURATION;
+
+ /**
+ * ATTRIBUTE:
+ * Scrolling duration status release to refresh -> refreshing
+ */
+ private int mReleaseToRefreshToRefreshingScrollingDuration = DEFAULT_RELEASE_TO_REFRESHING_SCROLLING_DURATION;
+
+ /**
+ * ATTRIBUTE:
+ * Refresh complete delay duration
+ */
+ private int mRefreshCompleteDelayDuration = DEFAULT_REFRESH_COMPLETE_DELAY_DURATION;
+
+ /**
+ * ATTRIBUTE:
+ * Scrolling duration status refresh complete -> default
+ * {@link #setRefreshing(boolean)} false
+ */
+ private int mRefreshCompleteToDefaultScrollingDuration = DEFAULT_REFRESH_COMPLETE_TO_DEFAULT_SCROLLING_DURATION;
+
+ /**
+ * ATTRIBUTE:
+ * Scrolling duration status default -> refreshing, mainly for auto refresh
+ * {@link #setRefreshing(boolean)} true
+ */
+ private int mDefaultToRefreshingScrollingDuration = DEFAULT_DEFAULT_TO_REFRESHING_SCROLLING_DURATION;
+
+ /**
+ * ATTRIBUTE:
+ * Scrolling duration status release to loading more -> loading more
+ */
+ private int mReleaseToLoadMoreToLoadingMoreScrollingDuration = DEFAULT_RELEASE_TO_LOADING_MORE_SCROLLING_DURATION;
+
+
+ /**
+ * ATTRIBUTE:
+ * Load more complete delay duration
+ */
+ private int mLoadMoreCompleteDelayDuration = DEFAULT_LOAD_MORE_COMPLETE_DELAY_DURATION;
+
+ /**
+ * ATTRIBUTE:
+ * Scrolling duration status load more complete -> default
+ * {@link #setLoadingMore(boolean)} false
+ */
+ private int mLoadMoreCompleteToDefaultScrollingDuration = DEFAULT_LOAD_MORE_COMPLETE_TO_DEFAULT_SCROLLING_DURATION;
+
+ /**
+ * ATTRIBUTE:
+ * Scrolling duration swiping to load more -> default
+ */
+ private int mSwipingToLoadMoreToDefaultScrollingDuration = DEFAULT_SWIPING_TO_LOAD_MORE_TO_DEFAULT_SCROLLING_DURATION;
+
+ /**
+ * ATTRIBUTE:
+ * Scrolling duration status default -> loading more, mainly for auto load more
+ * {@link #setLoadingMore(boolean)} true
+ */
+ private int mDefaultToLoadingMoreScrollingDuration = DEFAULT_DEFAULT_TO_LOADING_MORE_SCROLLING_DURATION;
+
+ /**
+ * the style enum
+ */
+ public static final class STYLE {
+ public static final int CLASSIC = 0;
+ public static final int ABOVE = 1;
+ public static final int BLEW = 2;
+ public static final int SCALE = 3;
+ }
+
+
+ public HorSwipeToLoadLayout(Context context) {
+ this(context, (AttributeSet) null);
+ }
+
+ public HorSwipeToLoadLayout(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public HorSwipeToLoadLayout(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.HorSwipeToLoadLayout, defStyleAttr, 0);
+
+ try {
+ int N = a.getIndexCount();
+
+ for (int i = 0; i < N; ++i) {
+ int attr = a.getIndex(i);
+ if (attr == R.styleable.HorSwipeToLoadLayout_refresh_enabled) {
+ this.setRefreshEnabled(a.getBoolean(attr, mRefreshEnabled));
+ } else if (attr == R.styleable.HorSwipeToLoadLayout_load_more_enabled) {
+ this.setLoadMoreEnabled(a.getBoolean(attr, mLoadMoreEnabled));
+ } else if (attr == R.styleable.HorSwipeToLoadLayout_swipe_style) {
+ this.setSwipeStyle(a.getInt(attr, mStyle));
+ } else if (attr == R.styleable.HorSwipeToLoadLayout_drag_ratio) {
+ this.setDragRatio(a.getFloat(attr, mDragRatio));
+ } else if (attr == R.styleable.HorSwipeToLoadLayout_refresh_final_drag_offset) {
+ this.setRefreshFinalDragOffset(a.getDimensionPixelOffset(attr, 0));
+ } else if (attr == R.styleable.HorSwipeToLoadLayout_load_more_final_drag_offset) {
+ this.setLoadMoreFinalDragOffset(a.getDimensionPixelOffset(attr, 0));
+ } else if (attr == R.styleable.HorSwipeToLoadLayout_refresh_trigger_offset) {
+ this.setRefreshTriggerOffset(a.getDimensionPixelOffset(attr, 0));
+ } else if (attr == R.styleable.HorSwipeToLoadLayout_load_more_trigger_offset) {
+ this.setLoadMoreTriggerOffset(a.getDimensionPixelOffset(attr, 0));
+ } else if (attr == R.styleable.HorSwipeToLoadLayout_swiping_to_refresh_to_default_scrolling_duration) {
+ this.setSwipingToRefreshToDefaultScrollingDuration(a.getInt(attr, mSwipingToRefreshToDefaultScrollingDuration));
+ } else if (attr == R.styleable.HorSwipeToLoadLayout_release_to_refreshing_scrolling_duration) {
+ this.setReleaseToRefreshingScrollingDuration(a.getInt(attr, mReleaseToRefreshToRefreshingScrollingDuration));
+ } else if (attr == R.styleable.HorSwipeToLoadLayout_refresh_complete_delay_duration) {
+ this.setRefreshCompleteDelayDuration(a.getInt(attr, mRefreshCompleteDelayDuration));
+ } else if (attr == R.styleable.HorSwipeToLoadLayout_refresh_complete_to_default_scrolling_duration) {
+ this.setRefreshCompleteToDefaultScrollingDuration(a.getInt(attr, mRefreshCompleteToDefaultScrollingDuration));
+ } else if (attr == R.styleable.HorSwipeToLoadLayout_default_to_refreshing_scrolling_duration) {
+ this.setDefaultToRefreshingScrollingDuration(a.getInt(attr, mDefaultToRefreshingScrollingDuration));
+ } else if (attr == R.styleable.HorSwipeToLoadLayout_swiping_to_load_more_to_default_scrolling_duration) {
+ this.setSwipingToLoadMoreToDefaultScrollingDuration(a.getInt(attr, mSwipingToLoadMoreToDefaultScrollingDuration));
+ } else if (attr == R.styleable.HorSwipeToLoadLayout_release_to_loading_more_scrolling_duration) {
+ this.setReleaseToLoadingMoreScrollingDuration(a.getInt(attr, mReleaseToLoadMoreToLoadingMoreScrollingDuration));
+ } else if (attr == R.styleable.HorSwipeToLoadLayout_load_more_complete_delay_duration) {
+ this.setLoadMoreCompleteDelayDuration(a.getInt(attr, mLoadMoreCompleteDelayDuration));
+ } else if (attr == R.styleable.HorSwipeToLoadLayout_load_more_complete_to_default_scrolling_duration) {
+ this.setLoadMoreCompleteToDefaultScrollingDuration(a.getInt(attr, mLoadMoreCompleteToDefaultScrollingDuration));
+ } else if (attr == R.styleable.HorSwipeToLoadLayout_default_to_loading_more_scrolling_duration) {
+ this.setDefaultToLoadingMoreScrollingDuration(a.getInt(attr, mDefaultToLoadingMoreScrollingDuration));
+ }
+ }
+ } finally {
+ a.recycle();
+ }
+
+ mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
+ mAutoScroller = new HorSwipeToLoadLayout.AutoScroller();
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ final int childNum = getChildCount();
+ if (childNum == 0) {
+ // no child return
+ return;
+ } else if (0 < childNum && childNum < 4) {
+ mHeaderView = findViewById(R.id.swipe_refresh_header);
+ mTargetView = findViewById(R.id.swipe_target);
+ mFooterView = findViewById(R.id.swipe_load_more_footer);
+ } else {
+ // more than three children: unsupported!
+ throw new IllegalStateException("Children num must equal or less than 3");
+ }
+ if (mTargetView == null) {
+ return;
+ }
+ if (mHeaderView != null && mHeaderView instanceof SwipeTrigger) {
+ mHeaderView.setVisibility(GONE);
+ }
+ if (mFooterView != null && mFooterView instanceof SwipeTrigger) {
+ mFooterView.setVisibility(GONE);
+ }
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ // header
+ if (mHeaderView != null) {
+ final View headerView = mHeaderView;
+ measureChildWithMargins(headerView, widthMeasureSpec, 0, heightMeasureSpec, 0);
+ MarginLayoutParams lp = ((MarginLayoutParams) headerView.getLayoutParams());
+ mHeaderWidth = headerView.getMeasuredWidth() + lp.leftMargin + lp.rightMargin;
+ if (mRefreshTriggerOffset < mHeaderWidth) {
+ mRefreshTriggerOffset = mHeaderWidth;
+ }
+ }
+ // target
+ if (mTargetView != null) {
+ final View targetView = mTargetView;
+ measureChildWithMargins(targetView, widthMeasureSpec, 0, heightMeasureSpec, 0);
+ }
+ // footer
+ if (mFooterView != null) {
+ final View footerView = mFooterView;
+ measureChildWithMargins(footerView, widthMeasureSpec, 0, heightMeasureSpec, 0);
+ MarginLayoutParams lp = ((MarginLayoutParams) footerView.getLayoutParams());
+ mFooterWidth = footerView.getMeasuredWidth() + lp.leftMargin + lp.rightMargin;
+ if (mLoadMoreTriggerOffset < mFooterWidth) {
+ mLoadMoreTriggerOffset = mFooterWidth;
+ }
+ }
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ layoutChildren();
+
+ mHasHeaderView = (mHeaderView != null);
+ mHasFooterView = (mFooterView != null);
+ }
+
+ /**
+ * LayoutParams of RefreshLoadMoreLayout
+ */
+ public static class LayoutParams extends MarginLayoutParams {
+
+ public LayoutParams(Context c, AttributeSet attrs) {
+ super(c, attrs);
+ }
+
+ public LayoutParams(int width, int height) {
+ super(width, height);
+ }
+
+ public LayoutParams(MarginLayoutParams source) {
+ super(source);
+ }
+
+ public LayoutParams(ViewGroup.LayoutParams source) {
+ super(source);
+ }
+ }
+
+ @Override
+ protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
+ return new HorSwipeToLoadLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
+ }
+
+
+ @Override
+ protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
+ return new HorSwipeToLoadLayout.LayoutParams(p);
+ }
+
+ @Override
+ public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
+ return new HorSwipeToLoadLayout.LayoutParams(getContext(), attrs);
+ }
+
+ @Override
+ public boolean dispatchTouchEvent(MotionEvent ev) {
+ final int action = MotionEventCompat.getActionMasked(ev);
+ switch (action) {
+ case MotionEvent.ACTION_CANCEL:
+ case MotionEvent.ACTION_UP:
+ // swipeToRefresh -> finger up -> finger down if the status is still swipeToRefresh
+ // in onInterceptTouchEvent ACTION_DOWN event will stop the scroller
+ // if the event pass to the child view while ACTION_MOVE(condition is false)
+ // in onInterceptTouchEvent ACTION_MOVE the ACTION_UP or ACTION_CANCEL will not be
+ // passed to onInterceptTouchEvent and onTouchEvent. Instead It will be passed to
+ // child view's onTouchEvent. So we must deal this situation in dispatchTouchEvent
+ onActivePointerUp();
+ break;
+ }
+ return super.dispatchTouchEvent(ev);
+ }
+
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent event) {
+ final int action = MotionEventCompat.getActionMasked(event);
+ switch (action) {
+ case MotionEvent.ACTION_DOWN:
+
+ mActivePointerId = event.getPointerId(0);
+ mInitDownY = mLastY = getMotionEventY(event, mActivePointerId);
+ mInitDownX = mLastX = getMotionEventX(event, mActivePointerId);
+
+ // if it isn't an ing status or default status
+ if (STATUS.isSwipingToRefresh(mStatus) || STATUS.isSwipingToLoadMore(mStatus) ||
+ STATUS.isReleaseToRefresh(mStatus) || STATUS.isReleaseToLoadMore(mStatus)) {
+ // abort autoScrolling, not trigger the method #autoScrollFinished()
+ mAutoScroller.abortIfRunning();
+ if (mDebug) {
+ Log.i(TAG, "Another finger down, abort auto scrolling, let the new finger handle");
+ }
+ }
+
+ if (STATUS.isSwipingToRefresh(mStatus) || STATUS.isReleaseToRefresh(mStatus)
+ || STATUS.isSwipingToLoadMore(mStatus) || STATUS.isReleaseToLoadMore(mStatus)) {
+ return true;
+ }
+
+ // let children view handle the ACTION_DOWN;
+
+ // 1. children consumed:
+ // if at least one of children onTouchEvent() ACTION_DOWN return true.
+ // ACTION_DOWN event will not return to SwipeToLoadLayout#onTouchEvent().
+ // but the others action can be handled by SwipeToLoadLayout#onInterceptTouchEvent()
+
+ // 2. children not consumed:
+ // if children onTouchEvent() ACTION_DOWN return false.
+ // ACTION_DOWN event will return to SwipeToLoadLayout's onTouchEvent().
+ // SwipeToLoadLayout#onTouchEvent() ACTION_DOWN return true to consume the ACTION_DOWN event.
+
+ // anyway: handle action down in onInterceptTouchEvent() to init is an good option
+ break;
+ case MotionEvent.ACTION_MOVE:
+ if (mActivePointerId == INVALID_POINTER) {
+ return false;
+ }
+ float y = getMotionEventY(event, mActivePointerId);
+ float x = getMotionEventX(event, mActivePointerId);
+ final float yInitDiff = y - mInitDownY;
+ final float xInitDiff = x - mInitDownX;
+ mLastY = y;
+ mLastX = x;
+ boolean moved = Math.abs(xInitDiff) > Math.abs(yInitDiff)
+ && Math.abs(xInitDiff) > mTouchSlop;
+ boolean triggerCondition =
+ // refresh trigger condition
+ (xInitDiff > 0 && moved && onCheckCanRefresh()) ||
+ //load more trigger condition
+ (xInitDiff < 0 && moved && onCheckCanLoadMore());
+ if (triggerCondition) {
+ // if the refresh's or load more's trigger condition is true,
+ // intercept the move action event and pass it to SwipeToLoadLayout#onTouchEvent()
+ return true;
+ }
+ break;
+ case MotionEvent.ACTION_POINTER_UP: {
+ onSecondaryPointerUp(event);
+ mInitDownY = mLastY = getMotionEventY(event, mActivePointerId);
+ mInitDownX = mLastX = getMotionEventX(event, mActivePointerId);
+ break;
+ }
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_CANCEL:
+ mActivePointerId = INVALID_POINTER;
+ break;
+ }
+ return super.onInterceptTouchEvent(event);
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ final int action = MotionEventCompat.getActionMasked(event);
+
+ switch (action) {
+ case MotionEvent.ACTION_DOWN:
+ mActivePointerId = event.getPointerId(0);
+ return true;
+
+ case MotionEvent.ACTION_MOVE:
+ // take over the ACTION_MOVE event from SwipeToLoadLayout#onInterceptTouchEvent()
+ // if condition is true
+ final float y = getMotionEventY(event, mActivePointerId);
+ final float x = getMotionEventX(event, mActivePointerId);
+
+ final float yDiff = y - mLastY;
+ final float xDiff = x - mLastX;
+ mLastY = y;
+ mLastX = x;
+
+ if (Math.abs(yDiff) > Math.abs(xDiff) && Math.abs(yDiff) > mTouchSlop) {
+ return false;
+ }
+
+ if (STATUS.isStatusDefault(mStatus)) {
+ if (xDiff > 0 && onCheckCanRefresh()) {
+ mRefreshCallback.onPrepare();
+ setStatus(STATUS.STATUS_SWIPING_TO_REFRESH);
+ } else if (xDiff < 0 && onCheckCanLoadMore()) {
+ mLoadMoreCallback.onPrepare();
+ setStatus(STATUS.STATUS_SWIPING_TO_LOAD_MORE);
+ }
+ } else if (STATUS.isRefreshStatus(mStatus)) {
+ if (mTargetOffset <= 0) {
+ setStatus(STATUS.STATUS_DEFAULT);
+ fixCurrentStatusLayout();
+ return false;
+ }
+ } else if (STATUS.isLoadMoreStatus(mStatus)) {
+ if (mTargetOffset >= 0) {
+ setStatus(STATUS.STATUS_DEFAULT);
+ fixCurrentStatusLayout();
+ return false;
+ }
+ }
+
+ if (STATUS.isRefreshStatus(mStatus)) {
+ if (STATUS.isSwipingToRefresh(mStatus) || STATUS.isReleaseToRefresh(mStatus)) {
+ if (mTargetOffset >= mRefreshTriggerOffset) {
+ setStatus(STATUS.STATUS_RELEASE_TO_REFRESH);
+ } else {
+ setStatus(STATUS.STATUS_SWIPING_TO_REFRESH);
+ }
+ fingerScroll(xDiff);
+ }
+ } else if (STATUS.isLoadMoreStatus(mStatus)) {
+ if (STATUS.isSwipingToLoadMore(mStatus) || STATUS.isReleaseToLoadMore(mStatus)) {
+ if (-mTargetOffset >= mLoadMoreTriggerOffset) {
+ setStatus(STATUS.STATUS_RELEASE_TO_LOAD_MORE);
+ } else {
+ setStatus(STATUS.STATUS_SWIPING_TO_LOAD_MORE);
+ }
+ fingerScroll(xDiff);
+ }
+ }
+ return true;
+
+ case MotionEvent.ACTION_POINTER_DOWN: {
+ final int pointerIndex = MotionEventCompat.getActionIndex(event);
+ final int pointerId = event.getPointerId(pointerIndex);
+ if (pointerId != INVALID_POINTER) {
+ mActivePointerId = pointerId;
+ }
+ mInitDownY = mLastY = getMotionEventY(event, mActivePointerId);
+ mInitDownX = mLastX = getMotionEventX(event, mActivePointerId);
+ break;
+ }
+ case MotionEvent.ACTION_POINTER_UP: {
+ onSecondaryPointerUp(event);
+ mInitDownY = mLastY = getMotionEventY(event, mActivePointerId);
+ mInitDownX = mLastX = getMotionEventX(event, mActivePointerId);
+ break;
+ }
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_CANCEL:
+ if (mActivePointerId == INVALID_POINTER) {
+ return false;
+ }
+ mActivePointerId = INVALID_POINTER;
+ break;
+ default:
+ break;
+ }
+ return super.onTouchEvent(event);
+ }
+
+ /**
+ * set debug mode(default value false)
+ *
+ * @param debug if true log on, false log off
+ */
+ public void setDebug(boolean debug) {
+ this.mDebug = debug;
+ }
+
+ /**
+ * is refresh function is enabled
+ *
+ * @return
+ */
+ public boolean isRefreshEnabled() {
+ return mRefreshEnabled;
+ }
+
+ /**
+ * switch refresh function on or off
+ *
+ * @param enable
+ */
+ public void setRefreshEnabled(boolean enable) {
+ this.mRefreshEnabled = enable;
+ }
+
+ /**
+ * is load more function is enabled
+ *
+ * @return
+ */
+ public boolean isLoadMoreEnabled() {
+ return mLoadMoreEnabled;
+ }
+
+ /**
+ * switch load more function on or off
+ *
+ * @param enable
+ */
+ public void setLoadMoreEnabled(boolean enable) {
+ this.mLoadMoreEnabled = enable;
+ }
+
+ /**
+ * is current status refreshing
+ *
+ * @return
+ */
+ public boolean isRefreshing() {
+ return STATUS.isRefreshing(mStatus);
+ }
+
+ /**
+ * is current status loading more
+ *
+ * @return
+ */
+ public boolean isLoadingMore() {
+ return STATUS.isLoadingMore(mStatus);
+ }
+
+ /**
+ * set refresh header view, the view must at lease be an implement of {@code SwipeRefreshTrigger}.
+ * the view can also implement {@code SwipeTrigger} for more extension functions
+ *
+ * @param view
+ */
+ public void setRefreshHeaderView(View view) {
+ if (view instanceof SwipeRefreshTrigger) {
+ if (mHeaderView != null && mHeaderView != view) {
+ removeView(mHeaderView);
+ }
+ if (mHeaderView != view) {
+ this.mHeaderView = view;
+ addView(view);
+ }
+ } else {
+ Log.e(TAG, "Refresh header view must be an implement of SwipeRefreshTrigger");
+ }
+ }
+
+ /**
+ * set load more footer view, the view must at least be an implement of SwipeLoadTrigger
+ * the view can also implement {@code SwipeTrigger} for more extension functions
+ *
+ * @param view
+ */
+ public void setLoadMoreFooterView(View view) {
+ if (view instanceof SwipeLoadMoreTrigger) {
+ if (mFooterView != null && mFooterView != view) {
+ removeView(mFooterView);
+ }
+ if (mFooterView != view) {
+ this.mFooterView = view;
+ addView(mFooterView);
+ }
+ } else {
+ Log.e(TAG, "Load more footer view must be an implement of SwipeLoadTrigger");
+ }
+ }
+
+ /**
+ * set the style of the refresh header
+ *
+ * @param style
+ */
+ public void setSwipeStyle(int style) {
+ this.mStyle = style;
+ requestLayout();
+ }
+
+ /**
+ * set how hard to drag. bigger easier, smaller harder;
+ *
+ * @param dragRatio default value is {@link #DEFAULT_DRAG_RATIO}
+ */
+ public void setDragRatio(float dragRatio) {
+ this.mDragRatio = dragRatio;
+ }
+
+ /**
+ * set the value of {@link #mRefreshTriggerOffset}.
+ * Default value is the refresh header view height {@link #mHeaderWidth}
+ * If the offset you set is smaller than {@link #mHeaderWidth} or not set,
+ * using {@link #mHeaderWidth} as default value
+ *
+ * @param offset
+ */
+ public void setRefreshTriggerOffset(int offset) {
+ mRefreshTriggerOffset = offset;
+ }
+
+ /**
+ * set the value of {@link #mLoadMoreTriggerOffset}.
+ * Default value is the load more footer view height {@link #mFooterWidth}
+ * If the offset you set is smaller than {@link #mFooterWidth} or not set,
+ * using {@link #mFooterWidth} as default value
+ *
+ * @param offset
+ */
+ public void setLoadMoreTriggerOffset(int offset) {
+ mLoadMoreTriggerOffset = offset;
+ }
+
+ /**
+ * Set the final offset you can swipe to refresh.
+ * If the offset you set is 0(default value) or smaller than {@link #mRefreshTriggerOffset}
+ * there no final offset
+ *
+ * @param offset
+ */
+ public void setRefreshFinalDragOffset(int offset) {
+ mRefreshFinalDragOffset = offset;
+ }
+
+ /**
+ * Set the final offset you can swipe to load more.
+ * If the offset you set is 0(default value) or smaller than {@link #mLoadMoreTriggerOffset},
+ * there no final offset
+ *
+ * @param offset
+ */
+ public void setLoadMoreFinalDragOffset(int offset) {
+ mLoadMoreFinalDragOffset = offset;
+ }
+
+ /**
+ * set {@link #mSwipingToRefreshToDefaultScrollingDuration} in milliseconds
+ *
+ * @param duration
+ */
+ public void setSwipingToRefreshToDefaultScrollingDuration(int duration) {
+ this.mSwipingToRefreshToDefaultScrollingDuration = duration;
+ }
+
+ /**
+ * set {@link #mReleaseToRefreshToRefreshingScrollingDuration} in milliseconds
+ *
+ * @param duration
+ */
+ public void setReleaseToRefreshingScrollingDuration(int duration) {
+ this.mReleaseToRefreshToRefreshingScrollingDuration = duration;
+ }
+
+ /**
+ * set {@link #mRefreshCompleteDelayDuration} in milliseconds
+ *
+ * @param duration
+ */
+ public void setRefreshCompleteDelayDuration(int duration) {
+ this.mRefreshCompleteDelayDuration = duration;
+ }
+
+ /**
+ * set {@link #mRefreshCompleteToDefaultScrollingDuration} in milliseconds
+ *
+ * @param duration
+ */
+ public void setRefreshCompleteToDefaultScrollingDuration(int duration) {
+ this.mRefreshCompleteToDefaultScrollingDuration = duration;
+ }
+
+ /**
+ * set {@link #mDefaultToRefreshingScrollingDuration} in milliseconds
+ *
+ * @param duration
+ */
+ public void setDefaultToRefreshingScrollingDuration(int duration) {
+ this.mDefaultToRefreshingScrollingDuration = duration;
+ }
+
+ /**
+ * set {@link @mSwipingToLoadMoreToDefaultScrollingDuration} in milliseconds
+ *
+ * @param duration
+ */
+ public void setSwipingToLoadMoreToDefaultScrollingDuration(int duration) {
+ this.mSwipingToLoadMoreToDefaultScrollingDuration = duration;
+ }
+
+ /**
+ * set {@link #mReleaseToLoadMoreToLoadingMoreScrollingDuration} in milliseconds
+ *
+ * @param duration
+ */
+ public void setReleaseToLoadingMoreScrollingDuration(int duration) {
+ this.mReleaseToLoadMoreToLoadingMoreScrollingDuration = duration;
+ }
+
+ /**
+ * set {@link #mLoadMoreCompleteDelayDuration} in milliseconds
+ *
+ * @param duration
+ */
+ public void setLoadMoreCompleteDelayDuration(int duration) {
+ this.mLoadMoreCompleteDelayDuration = duration;
+ }
+
+ /**
+ * set {@link #mLoadMoreCompleteToDefaultScrollingDuration} in milliseconds
+ *
+ * @param duration
+ */
+ public void setLoadMoreCompleteToDefaultScrollingDuration(int duration) {
+ this.mLoadMoreCompleteToDefaultScrollingDuration = duration;
+ }
+
+ /**
+ * set {@link #mDefaultToLoadingMoreScrollingDuration} in milliseconds
+ *
+ * @param duration
+ */
+ public void setDefaultToLoadingMoreScrollingDuration(int duration) {
+ this.mDefaultToLoadingMoreScrollingDuration = duration;
+ }
+
+ /**
+ * set an {@link OnRefreshListener} to listening refresh event
+ *
+ * @param listener
+ */
+ public void setOnRefreshListener(OnRefreshListener listener) {
+ this.mRefreshListener = listener;
+ }
+
+ /**
+ * set an {@link OnLoadMoreListener} to listening load more event
+ *
+ * @param listener
+ */
+ public void setOnLoadMoreListener(OnLoadMoreListener listener) {
+ this.mLoadMoreListener = listener;
+ }
+
+ /**
+ * auto refresh or cancel
+ *
+ * @param refreshing
+ */
+ public void setRefreshing(boolean refreshing) {
+ if (!isRefreshEnabled() || mHeaderView == null) {
+ return;
+ }
+ this.mAutoLoading = refreshing;
+ if (refreshing) {
+ if (STATUS.isStatusDefault(mStatus)) {
+ setStatus(STATUS.STATUS_SWIPING_TO_REFRESH);
+ scrollDefaultToRefreshing();
+ }
+ } else {
+ if (STATUS.isRefreshing(mStatus)) {
+ mRefreshCallback.onComplete();
+ postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ scrollRefreshingToDefault();
+ }
+ }, mRefreshCompleteDelayDuration);
+ }
+ }
+ }
+
+ /**
+ * auto loading more or cancel
+ *
+ * @param loadingMore
+ */
+ public void setLoadingMore(boolean loadingMore) {
+ if (!isLoadMoreEnabled() || mFooterView == null) {
+ return;
+ }
+ this.mAutoLoading = loadingMore;
+ if (loadingMore) {
+ if (STATUS.isStatusDefault(mStatus)) {
+ setStatus(STATUS.STATUS_SWIPING_TO_LOAD_MORE);
+ scrollDefaultToLoadingMore();
+ }
+ } else {
+ if (STATUS.isLoadingMore(mStatus)) {
+ mLoadMoreCallback.onComplete();
+ postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ scrollLoadingMoreToDefault();
+ }
+ }, mLoadMoreCompleteDelayDuration);
+ }
+ }
+ }
+
+ /**
+ * copy from {@link android.support.v4.widget.SwipeRefreshLayout#canChildScrollUp()}
+ *
+ * @return Whether it is possible for the child view of this layout to
+ * scroll left. Override this if the child view is a custom view.
+ */
+ protected boolean canChildScrollLeft() {
+ if (android.os.Build.VERSION.SDK_INT < 14) {
+ if (mTargetView instanceof AbsListView) {
+ final AbsListView absListView = (AbsListView) mTargetView;
+ return absListView.getChildCount() > 0
+ && (absListView.getFirstVisiblePosition() > 0 || absListView.getChildAt(0)
+ .getLeft() < absListView.getPaddingLeft());
+ } else if (mTargetView instanceof ViewPager) {
+ ViewPager pager = (ViewPager) mTargetView;
+ return pager.getCurrentItem() > 0;
+ } else {
+ return ViewCompat.canScrollHorizontally(mTargetView, -1) || mTargetView.getScrollX() > 0;
+ }
+ } else {
+ if (mTargetView instanceof ViewPager) {
+ ViewPager pager = (ViewPager) mTargetView;
+ return pager.getCurrentItem() > 0;
+ }
+ return ViewCompat.canScrollHorizontally(mTargetView, -1);
+ }
+ }
+
+ /**
+ * Whether it is possible for the child view of this layout to
+ * scroll right. Override this if the child view is a custom view.
+ *
+ * @return
+ */
+ protected boolean canChildScrollRight() {
+ if (android.os.Build.VERSION.SDK_INT < 14) {
+ if (mTargetView instanceof AbsListView) {
+ final AbsListView absListView = (AbsListView) mTargetView;
+ return absListView.getChildCount() > 0
+ && (absListView.getLastVisiblePosition() < absListView.getChildCount() - 1
+ || absListView.getChildAt(absListView.getChildCount() - 1).getRight() > absListView.getPaddingRight());
+ } else if (mTargetView instanceof ViewPager) {
+ ViewPager pager = (ViewPager) mTargetView;
+ return pager.getCurrentItem() < pager.getAdapter().getCount() - 1;
+ } else {
+ return ViewCompat.canScrollHorizontally(mTargetView, 1) || mTargetView.getScrollX() < 0;
+ }
+ } else {
+ if (mTargetView instanceof ViewPager) {
+ ViewPager pager = (ViewPager) mTargetView;
+ return pager.getCurrentItem() < pager.getAdapter().getCount() - 1;
+ }
+ return ViewCompat.canScrollHorizontally(mTargetView, 1);
+ }
+ }
+
+ /**
+ * @see #onLayout(boolean, int, int, int, int)
+ */
+ private void layoutChildren() {
+ final int width = getMeasuredWidth();
+ final int height = getMeasuredHeight();
+
+ final int paddingLeft = getPaddingLeft();
+ final int paddingTop = getPaddingTop();
+ final int paddingRight = getPaddingRight();
+ final int paddingBottom = getPaddingBottom();
+
+ if (mTargetView == null) {
+ return;
+ }
+
+ // layout header
+ if (mHeaderView != null) {
+ final View headerView = mHeaderView;
+ MarginLayoutParams lp = (MarginLayoutParams) headerView.getLayoutParams();
+ final int headerLeft;
+ final int headerTop = paddingTop + lp.topMargin;
+ switch (mStyle) {
+ case STYLE.CLASSIC:
+ // classic
+ headerLeft = paddingLeft + lp.leftMargin - mHeaderWidth + mHeaderOffset;
+ break;
+ case STYLE.ABOVE:
+ // classic
+ headerLeft = paddingLeft + lp.leftMargin - mHeaderWidth + mHeaderOffset;
+ break;
+ case STYLE.BLEW:
+ // blew
+ headerLeft = paddingLeft + lp.leftMargin;
+ break;
+ case STYLE.SCALE:
+ // scale
+ headerLeft = paddingLeft + lp.leftMargin - mHeaderWidth / 2 + mHeaderOffset / 2;
+ break;
+ default:
+ // classic
+ headerLeft = paddingLeft + lp.leftMargin - mHeaderWidth + mHeaderOffset;
+ break;
+ }
+ final int headerRight = headerLeft + headerView.getMeasuredWidth();
+ final int headerBottom = headerTop + headerView.getMeasuredHeight();
+ headerView.layout(headerLeft, headerTop, headerRight, headerBottom);
+ }
+
+
+ // layout target
+ if (mTargetView != null) {
+ final View targetView = mTargetView;
+ MarginLayoutParams lp = (MarginLayoutParams) targetView.getLayoutParams();
+ final int targetLeft;
+ final int targetTop = paddingTop + lp.topMargin;
+
+ switch (mStyle) {
+ case STYLE.CLASSIC:
+ // classic
+ targetLeft = paddingLeft + lp.leftMargin + mTargetOffset;
+ break;
+ case STYLE.ABOVE:
+ // above
+ targetLeft = paddingLeft + lp.leftMargin;
+ break;
+ case STYLE.BLEW:
+ // classic
+ targetLeft = paddingLeft + lp.leftMargin + mTargetOffset;
+ break;
+ case STYLE.SCALE:
+ // classic
+ targetLeft = paddingLeft + lp.leftMargin + mTargetOffset;
+ break;
+ default:
+ // classic
+ targetLeft = paddingLeft + lp.leftMargin + mTargetOffset;
+ break;
+ }
+ final int targetRight = targetLeft + targetView.getMeasuredWidth();
+ final int targetBottom = targetTop + targetView.getMeasuredHeight();
+ targetView.layout(targetLeft, targetTop, targetRight, targetBottom);
+ }
+
+ // layout footer
+ if (mFooterView != null) {
+ final View footerView = mFooterView;
+ MarginLayoutParams lp = (MarginLayoutParams) footerView.getLayoutParams();
+ final int footerRight;
+ final int footerTop = paddingTop + lp.topMargin;
+ switch (mStyle) {
+ case STYLE.CLASSIC:
+ // classic
+ footerRight = width - paddingRight - lp.rightMargin + mFooterWidth + mFooterOffset;
+ break;
+ case STYLE.ABOVE:
+ // classic
+ footerRight = width - paddingRight - lp.rightMargin + mFooterWidth + mFooterOffset;
+ break;
+ case STYLE.BLEW:
+ // blew
+ footerRight = width - paddingRight - lp.rightMargin;
+ break;
+ case STYLE.SCALE:
+ // scale
+ footerRight = width - paddingRight - lp.rightMargin + mFooterWidth / 2 + mFooterOffset / 2;
+ break;
+ default:
+ // classic
+ footerRight = width - paddingRight - lp.rightMargin + mFooterWidth + mFooterOffset;
+ break;
+ }
+ final int footerLeft = footerRight - footerView.getMeasuredWidth();
+ final int footerBottom = footerTop + footerView.getMeasuredHeight();
+
+ footerView.layout(footerLeft, footerTop, footerRight, footerBottom);
+ }
+
+ if (mStyle == STYLE.CLASSIC
+ || mStyle == STYLE.ABOVE) {
+ if (mHeaderView != null) {
+ mHeaderView.bringToFront();
+ }
+ if (mFooterView != null) {
+ mFooterView.bringToFront();
+ }
+ } else if (mStyle == STYLE.BLEW || mStyle == STYLE.SCALE) {
+ if (mTargetView != null) {
+ mTargetView.bringToFront();
+ }
+ }
+ }
+
+ private void fixCurrentStatusLayout() {
+ if (STATUS.isRefreshing(mStatus)) {
+ mTargetOffset = (int) (mRefreshTriggerOffset + 0.5f);
+ mHeaderOffset = mTargetOffset;
+ mFooterOffset = 0;
+ layoutChildren();
+ invalidate();
+ } else if (STATUS.isStatusDefault(mStatus)) {
+ mTargetOffset = 0;
+ mHeaderOffset = 0;
+ mFooterOffset = 0;
+ layoutChildren();
+ invalidate();
+ } else if (STATUS.isLoadingMore(mStatus)) {
+ mTargetOffset = -(int) (mLoadMoreTriggerOffset + 0.5f);
+ mHeaderOffset = 0;
+ mFooterOffset = mTargetOffset;
+ layoutChildren();
+ invalidate();
+ }
+ }
+
+ /**
+ * scrolling by physical touch with your fingers
+ *
+ * @param xDiff
+ */
+ private void fingerScroll(final float xDiff) {
+ float ratio = mDragRatio;
+ float xScrolled = xDiff * ratio;
+
+ // make sure (targetOffset>0 -> targetOffset=0 -> default status)
+ // or (targetOffset<0 -> targetOffset=0 -> default status)
+ // forbidden fling (targetOffset>0 -> targetOffset=0 ->targetOffset<0 -> default status)
+ // or (targetOffset<0 -> targetOffset=0 ->targetOffset>0 -> default status)
+ // I am so smart :)
+
+ float tmpTargetOffset = xScrolled + mTargetOffset;
+ if ((tmpTargetOffset > 0 && mTargetOffset < 0)
+ || (tmpTargetOffset < 0 && mTargetOffset > 0)) {
+ xScrolled = -mTargetOffset;
+ }
+
+
+ if (mRefreshFinalDragOffset >= mRefreshTriggerOffset && tmpTargetOffset > mRefreshFinalDragOffset) {
+ xScrolled = mRefreshFinalDragOffset - mTargetOffset;
+ } else if (mLoadMoreFinalDragOffset >= mLoadMoreTriggerOffset && -tmpTargetOffset > mLoadMoreFinalDragOffset) {
+ xScrolled = -mLoadMoreFinalDragOffset - mTargetOffset;
+ }
+
+ if (STATUS.isRefreshStatus(mStatus)) {
+ mRefreshCallback.onMove(mTargetOffset, false, false);
+ } else if (STATUS.isLoadMoreStatus(mStatus)) {
+ mLoadMoreCallback.onMove(mTargetOffset, false, false);
+ }
+ updateScroll(xScrolled);
+ }
+
+ private void autoScroll(final float xScrolled) {
+
+ if (STATUS.isSwipingToRefresh(mStatus)) {
+ mRefreshCallback.onMove(mTargetOffset, false, true);
+ } else if (STATUS.isReleaseToRefresh(mStatus)) {
+ mRefreshCallback.onMove(mTargetOffset, false, true);
+ } else if (STATUS.isRefreshing(mStatus)) {
+ mRefreshCallback.onMove(mTargetOffset, true, true);
+ } else if (STATUS.isSwipingToLoadMore(mStatus)) {
+ mLoadMoreCallback.onMove(mTargetOffset, false, true);
+ } else if (STATUS.isReleaseToLoadMore(mStatus)) {
+ mLoadMoreCallback.onMove(mTargetOffset, false, true);
+ } else if (STATUS.isLoadingMore(mStatus)) {
+ mLoadMoreCallback.onMove(mTargetOffset, true, true);
+ }
+ updateScroll(xScrolled);
+ }
+
+ /**
+ * Process the scrolling(auto or physical) and append the diff values to mTargetOffset
+ * I think it's the most busy and core method. :) a ha ha ha ha...
+ *
+ * @param xScrolled
+ */
+ private void updateScroll(final float xScrolled) {
+ if (xScrolled == 0) {
+ return;
+ }
+ mTargetOffset += xScrolled;
+
+ if (STATUS.isRefreshStatus(mStatus)) {
+ mHeaderOffset = mTargetOffset;
+ mFooterOffset = 0;
+ } else if (STATUS.isLoadMoreStatus(mStatus)) {
+ mFooterOffset = mTargetOffset;
+ mHeaderOffset = 0;
+ }
+
+ if (mDebug) {
+ Log.i(TAG, "mTargetOffset = " + mTargetOffset);
+ }
+ layoutChildren();
+ invalidate();
+ }
+
+ /**
+ * on active finger up
+ */
+ private void onActivePointerUp() {
+ if (STATUS.isSwipingToRefresh(mStatus)) {
+ // simply return
+ scrollSwipingToRefreshToDefault();
+
+ } else if (STATUS.isSwipingToLoadMore(mStatus)) {
+ // simply return
+ scrollSwipingToLoadMoreToDefault();
+
+ } else if (STATUS.isReleaseToRefresh(mStatus)) {
+ // return to header height and perform refresh
+ mRefreshCallback.onRelease();
+ scrollReleaseToRefreshToRefreshing();
+
+ } else if (STATUS.isReleaseToLoadMore(mStatus)) {
+ // return to footer height and perform loadMore
+ mLoadMoreCallback.onRelease();
+ scrollReleaseToLoadMoreToLoadingMore();
+
+ }
+ }
+
+ /**
+ * on not active finger up
+ *
+ * @param ev
+ */
+ private void onSecondaryPointerUp(MotionEvent ev) {
+ final int pointerIndex = MotionEventCompat.getActionIndex(ev);
+ final int pointerId = ev.getPointerId(pointerIndex);
+ if (pointerId == mActivePointerId) {
+ // This was our active pointer going up. Choose a new
+ // active pointer and adjust accordingly.
+ final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
+ mActivePointerId = ev.getPointerId(newPointerIndex);
+ }
+ }
+
+ private void scrollDefaultToRefreshing() {
+ mAutoScroller.autoScroll((int) (mRefreshTriggerOffset + 0.5f), mDefaultToRefreshingScrollingDuration);
+ }
+
+ private void scrollDefaultToLoadingMore() {
+ mAutoScroller.autoScroll(-(int) (mLoadMoreTriggerOffset + 0.5f), mDefaultToLoadingMoreScrollingDuration);
+ }
+
+ private void scrollSwipingToRefreshToDefault() {
+ mAutoScroller.autoScroll(-mHeaderOffset, mSwipingToRefreshToDefaultScrollingDuration);
+ }
+
+ private void scrollSwipingToLoadMoreToDefault() {
+ mAutoScroller.autoScroll(-mFooterOffset, mSwipingToLoadMoreToDefaultScrollingDuration);
+ }
+
+ private void scrollReleaseToRefreshToRefreshing() {
+ mAutoScroller.autoScroll(mHeaderWidth - mHeaderOffset, mReleaseToRefreshToRefreshingScrollingDuration);
+ }
+
+ private void scrollReleaseToLoadMoreToLoadingMore() {
+ mAutoScroller.autoScroll(-mFooterOffset - mFooterWidth, mReleaseToLoadMoreToLoadingMoreScrollingDuration);
+ }
+
+ private void scrollRefreshingToDefault() {
+ mAutoScroller.autoScroll(-mHeaderOffset, mRefreshCompleteToDefaultScrollingDuration);
+ }
+
+ private void scrollLoadingMoreToDefault() {
+ mAutoScroller.autoScroll(-mFooterOffset, mLoadMoreCompleteToDefaultScrollingDuration);
+ }
+
+ /**
+ * invoke when {@link AutoScroller#finish()} is automatic
+ */
+ private void autoScrollFinished() {
+ int mLastStatus = mStatus;
+
+ if (STATUS.isReleaseToRefresh(mStatus)) {
+ setStatus(STATUS.STATUS_REFRESHING);
+ fixCurrentStatusLayout();
+ mRefreshCallback.onRefresh();
+
+ } else if (STATUS.isRefreshing(mStatus)) {
+ setStatus(STATUS.STATUS_DEFAULT);
+ fixCurrentStatusLayout();
+ mRefreshCallback.onReset();
+
+ } else if (STATUS.isSwipingToRefresh(mStatus)) {
+ if (mAutoLoading) {
+ mAutoLoading = false;
+ setStatus(STATUS.STATUS_REFRESHING);
+ fixCurrentStatusLayout();
+ mRefreshCallback.onRefresh();
+ } else {
+ setStatus(STATUS.STATUS_DEFAULT);
+ fixCurrentStatusLayout();
+ mRefreshCallback.onReset();
+ }
+ } else if (STATUS.isStatusDefault(mStatus)) {
+
+ } else if (STATUS.isSwipingToLoadMore(mStatus)) {
+ if (mAutoLoading) {
+ mAutoLoading = false;
+ setStatus(STATUS.STATUS_LOADING_MORE);
+ fixCurrentStatusLayout();
+ mLoadMoreCallback.onLoadMore();
+ } else {
+ setStatus(STATUS.STATUS_DEFAULT);
+ fixCurrentStatusLayout();
+ mLoadMoreCallback.onReset();
+ }
+ } else if (STATUS.isLoadingMore(mStatus)) {
+ setStatus(STATUS.STATUS_DEFAULT);
+ fixCurrentStatusLayout();
+ mLoadMoreCallback.onReset();
+ } else if (STATUS.isReleaseToLoadMore(mStatus)) {
+ setStatus(STATUS.STATUS_LOADING_MORE);
+ fixCurrentStatusLayout();
+ mLoadMoreCallback.onLoadMore();
+ } else {
+ throw new IllegalStateException("illegal state: " + STATUS.getStatus(mStatus));
+ }
+
+ if (mDebug) {
+ Log.i(TAG, STATUS.getStatus(mLastStatus) + " -> " + STATUS.getStatus(mStatus));
+ }
+ }
+
+ /**
+ * check if it can refresh
+ *
+ * @return
+ */
+ private boolean onCheckCanRefresh() {
+
+ return mRefreshEnabled && !canChildScrollLeft() && mHasHeaderView && mRefreshTriggerOffset > 0;
+ }
+
+ /**
+ * check if it can load more
+ *
+ * @return
+ */
+ private boolean onCheckCanLoadMore() {
+
+ return mLoadMoreEnabled && !canChildScrollRight() && mHasFooterView && mLoadMoreTriggerOffset > 0;
+ }
+
+ private float getMotionEventY(MotionEvent event, int activePointerId) {
+ final int index = event.findPointerIndex(activePointerId);
+ if (index < 0) {
+ return INVALID_COORDINATE;
+ }
+ return event.getY(index);
+ }
+
+ private float getMotionEventX(MotionEvent event, int activePointId) {
+ final int index = event.findPointerIndex(activePointId);
+ if (index < 0) {
+ return INVALID_COORDINATE;
+ }
+ return event.getX(index);
+ }
+
+ RefreshCallback mRefreshCallback = new RefreshCallback() {
+ @Override
+ public void onPrepare() {
+ if (mHeaderView != null && mHeaderView instanceof SwipeTrigger && STATUS.isStatusDefault(mStatus)) {
+ mHeaderView.setVisibility(VISIBLE);
+ ((SwipeTrigger) mHeaderView).onPrepare();
+ }
+ }
+
+ @Override
+ public void onMove(int x, boolean isComplete, boolean automatic) {
+ if (mHeaderView != null && mHeaderView instanceof SwipeTrigger && STATUS.isRefreshStatus(mStatus)) {
+ if (mHeaderView.getVisibility() != VISIBLE) {
+ mHeaderView.setVisibility(VISIBLE);
+ }
+ ((SwipeTrigger) mHeaderView).onMove(x, isComplete, automatic);
+ }
+ }
+
+ @Override
+ public void onRelease() {
+ if (mHeaderView != null && mHeaderView instanceof SwipeTrigger && STATUS.isReleaseToRefresh(mStatus)) {
+ ((SwipeTrigger) mHeaderView).onRelease();
+ }
+ }
+
+ @Override
+ public void onRefresh() {
+ if (mHeaderView != null && STATUS.isRefreshing(mStatus)) {
+ if (mHeaderView instanceof SwipeRefreshTrigger) {
+ ((SwipeRefreshTrigger) mHeaderView).onRefresh();
+ }
+ if (mRefreshListener != null) {
+ mRefreshListener.onRefresh();
+ }
+ }
+ }
+
+ @Override
+ public void onComplete() {
+ if (mHeaderView != null && mHeaderView instanceof SwipeTrigger) {
+ ((SwipeTrigger) mHeaderView).onComplete();
+ }
+ }
+
+ @Override
+ public void onReset() {
+ if (mHeaderView != null && mHeaderView instanceof SwipeTrigger && STATUS.isStatusDefault(mStatus)) {
+ ((SwipeTrigger) mHeaderView).onReset();
+ mHeaderView.setVisibility(GONE);
+ }
+ }
+ };
+
+ LoadMoreCallback mLoadMoreCallback = new LoadMoreCallback() {
+
+ @Override
+ public void onPrepare() {
+ if (mFooterView != null && mFooterView instanceof SwipeTrigger && STATUS.isStatusDefault(mStatus)) {
+ mFooterView.setVisibility(VISIBLE);
+ ((SwipeTrigger) mFooterView).onPrepare();
+ }
+ }
+
+ @Override
+ public void onMove(int x, boolean isComplete, boolean automatic) {
+ if (mFooterView != null && mFooterView instanceof SwipeTrigger && STATUS.isLoadMoreStatus(mStatus)) {
+ if (mFooterView.getVisibility() != VISIBLE) {
+ mFooterView.setVisibility(VISIBLE);
+ }
+ ((SwipeTrigger) mFooterView).onMove(x, isComplete, automatic);
+ }
+ }
+
+ @Override
+ public void onRelease() {
+ if (mFooterView != null && mFooterView instanceof SwipeTrigger && STATUS.isReleaseToLoadMore(mStatus)) {
+ ((SwipeTrigger) mFooterView).onRelease();
+ }
+ }
+
+ @Override
+ public void onLoadMore() {
+ if (mFooterView != null && STATUS.isLoadingMore(mStatus)) {
+ if (mFooterView instanceof SwipeLoadMoreTrigger) {
+ ((SwipeLoadMoreTrigger) mFooterView).onLoadMore();
+ }
+ if (mLoadMoreListener != null) {
+ mLoadMoreListener.onLoadMore();
+ }
+ }
+ }
+
+ @Override
+ public void onComplete() {
+ if (mFooterView != null && mFooterView instanceof SwipeTrigger) {
+ ((SwipeTrigger) mFooterView).onComplete();
+ }
+ }
+
+ @Override
+ public void onReset() {
+ if (mFooterView != null && mFooterView instanceof SwipeTrigger && STATUS.isStatusDefault(mStatus)) {
+ ((SwipeTrigger) mFooterView).onReset();
+ mFooterView.setVisibility(GONE);
+ }
+ }
+ };
+
+ /**
+ * refresh event callback
+ */
+ abstract class RefreshCallback implements SwipeTrigger, SwipeRefreshTrigger {
+ }
+
+ /**
+ * load more event callback
+ */
+ abstract class LoadMoreCallback implements SwipeTrigger, SwipeLoadMoreTrigger {
+ }
+
+ private class AutoScroller implements Runnable {
+
+ private Scroller mScroller;
+
+ private int mmLastX;
+
+ private boolean mRunning = false;
+
+ private boolean mAbort = false;
+
+ public AutoScroller() {
+ mScroller = new Scroller(getContext());
+ }
+
+ @Override
+ public void run() {
+ boolean finish = !mScroller.computeScrollOffset() || mScroller.isFinished();
+ int currx = mScroller.getCurrX();
+ int xDiff = currx - mmLastX;
+ if (finish) {
+ finish();
+ } else {
+ mmLastX = currx;
+ HorSwipeToLoadLayout.this.autoScroll(xDiff);
+ post(this);
+ }
+ }
+
+ /**
+ * remove the post callbacks and reset default values
+ */
+ private void finish() {
+ mmLastX = 0;
+ mRunning = false;
+ removeCallbacks(this);
+ // if abort by user, don't call
+ if (!mAbort) {
+ autoScrollFinished();
+ }
+ }
+
+ /**
+ * abort scroll if it is scrolling
+ */
+ public void abortIfRunning() {
+ if (mRunning) {
+ if (!mScroller.isFinished()) {
+ mAbort = true;
+ mScroller.forceFinished(true);
+ }
+ finish();
+ mAbort = false;
+ }
+ }
+
+ /**
+ * The param xScrolled here isn't final pos of y.
+ * It's just like the yScrolled param in the
+ * {@link #updateScroll(float yScrolled)}
+ *
+ * @param xScrolled
+ * @param duration
+ */
+ private void autoScroll(int xScrolled, int duration) {
+ removeCallbacks(this);
+ mmLastX = 0;
+ if (!mScroller.isFinished()) {
+ mScroller.forceFinished(true);
+ }
+ mScroller.startScroll(0, 0, xScrolled, 0, duration);
+ post(this);
+ mRunning = true;
+ }
+ }
+
+ /**
+ * Set the current status for better control
+ *
+ * @param status
+ */
+ private void setStatus(int status) {
+ mStatus = status;
+ if (mDebug) {
+ STATUS.printStatus(status);
+ }
+ }
+
+ /**
+ * an inner util class.
+ * enum of status
+ */
+ private final static class STATUS {
+ private static final int STATUS_REFRESH_RETURNING = -4;
+ private static final int STATUS_REFRESHING = -3;
+ private static final int STATUS_RELEASE_TO_REFRESH = -2;
+ private static final int STATUS_SWIPING_TO_REFRESH = -1;
+ private static final int STATUS_DEFAULT = 0;
+ private static final int STATUS_SWIPING_TO_LOAD_MORE = 1;
+ private static final int STATUS_RELEASE_TO_LOAD_MORE = 2;
+ private static final int STATUS_LOADING_MORE = 3;
+ private static final int STATUS_LOAD_MORE_RETURNING = 4;
+
+ private static boolean isRefreshing(final int status) {
+ return status == STATUS.STATUS_REFRESHING;
+ }
+
+ private static boolean isLoadingMore(final int status) {
+ return status == STATUS.STATUS_LOADING_MORE;
+ }
+
+ private static boolean isReleaseToRefresh(final int status) {
+ return status == STATUS.STATUS_RELEASE_TO_REFRESH;
+ }
+
+ private static boolean isReleaseToLoadMore(final int status) {
+ return status == STATUS.STATUS_RELEASE_TO_LOAD_MORE;
+ }
+
+ private static boolean isSwipingToRefresh(final int status) {
+ return status == STATUS.STATUS_SWIPING_TO_REFRESH;
+ }
+
+ private static boolean isSwipingToLoadMore(final int status) {
+ return status == STATUS.STATUS_SWIPING_TO_LOAD_MORE;
+ }
+
+ private static boolean isRefreshStatus(final int status) {
+ return status < STATUS.STATUS_DEFAULT;
+ }
+
+ public static boolean isLoadMoreStatus(final int status) {
+ return status > STATUS.STATUS_DEFAULT;
+ }
+
+ private static boolean isStatusDefault(final int status) {
+ return status == STATUS.STATUS_DEFAULT;
+ }
+
+ private static String getStatus(int status) {
+ final String statusInfo;
+ switch (status) {
+ case STATUS_REFRESH_RETURNING:
+ statusInfo = "status_refresh_returning";
+ break;
+ case STATUS_REFRESHING:
+ statusInfo = "status_refreshing";
+ break;
+ case STATUS_RELEASE_TO_REFRESH:
+ statusInfo = "status_release_to_refresh";
+ break;
+ case STATUS_SWIPING_TO_REFRESH:
+ statusInfo = "status_swiping_to_refresh";
+ break;
+ case STATUS_DEFAULT:
+ statusInfo = "status_default";
+ break;
+ case STATUS_SWIPING_TO_LOAD_MORE:
+ statusInfo = "status_swiping_to_load_more";
+ break;
+ case STATUS_RELEASE_TO_LOAD_MORE:
+ statusInfo = "status_release_to_load_more";
+ break;
+ case STATUS_LOADING_MORE:
+ statusInfo = "status_loading_more";
+ break;
+ case STATUS_LOAD_MORE_RETURNING:
+ statusInfo = "status_load_more_returning";
+ break;
+ default:
+ statusInfo = "status_illegal!";
+ break;
+ }
+ return statusInfo;
+ }
+
+ private static void printStatus(int status) {
+ Log.i(TAG, "printStatus:" + getStatus(status));
+ }
+ }
+}
diff --git a/library/src/main/res/values/attrs.xml b/library/src/main/res/values/attrs.xml
index bf0a634..3ce545f 100644
--- a/library/src/main/res/values/attrs.xml
+++ b/library/src/main/res/values/attrs.xml
@@ -28,4 +28,5 @@
-
\ No newline at end of file
+
+
\ No newline at end of file
From dcfc74430eb3a36059067dbeefdc8e61943ba3fb Mon Sep 17 00:00:00 2001
From: wxj <986798656@qq.com>
Date: Sun, 22 Jan 2017 17:53:23 +0800
Subject: [PATCH 2/6] loading
---
.../com/aspsine/swipetoloadlayout/SwipeToLoadLayout.java | 5 +++++
library/src/main/res/values/attrs.xml | 1 +
2 files changed, 6 insertions(+)
diff --git a/library/src/main/java/com/aspsine/swipetoloadlayout/SwipeToLoadLayout.java b/library/src/main/java/com/aspsine/swipetoloadlayout/SwipeToLoadLayout.java
index 6802d40..6bbd878 100644
--- a/library/src/main/java/com/aspsine/swipetoloadlayout/SwipeToLoadLayout.java
+++ b/library/src/main/java/com/aspsine/swipetoloadlayout/SwipeToLoadLayout.java
@@ -235,6 +235,11 @@ public class SwipeToLoadLayout extends ViewGroup {
*/
private int mDefaultToLoadingMoreScrollingDuration = DEFAULT_DEFAULT_TO_LOADING_MORE_SCROLLING_DURATION;
+ /**
+ *true : the target is vertically scroll
+ */
+ private boolean mIsVertically = true;
+
/**
* the style enum
*/
diff --git a/library/src/main/res/values/attrs.xml b/library/src/main/res/values/attrs.xml
index 3ce545f..ec603d5 100644
--- a/library/src/main/res/values/attrs.xml
+++ b/library/src/main/res/values/attrs.xml
@@ -27,6 +27,7 @@
+
\ No newline at end of file
From 837a5a12e444ddbe26b447948f0eb233158ce4a1 Mon Sep 17 00:00:00 2001
From: wxj <986798656@qq.com>
Date: Tue, 7 Feb 2017 14:32:42 +0800
Subject: [PATCH 3/6] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=8F=B3=E6=8B=89?=
=?UTF-8?q?=E5=88=B7=E6=96=B0=EF=BC=8C=20=E5=B7=A6=E6=8B=89=E5=8A=A0?=
=?UTF-8?q?=E8=BD=BD=E6=9B=B4=E5=A4=9A?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../HorSwipeToLoadLayout.java | 1583 -----------------
.../swipetoloadlayout/SwipeToLoadLayout.java | 328 +++-
library/src/main/res/values/attrs.xml | 2 +-
3 files changed, 271 insertions(+), 1642 deletions(-)
delete mode 100644 library/src/main/java/com/aspsine/swipetoloadlayout/HorSwipeToLoadLayout.java
diff --git a/library/src/main/java/com/aspsine/swipetoloadlayout/HorSwipeToLoadLayout.java b/library/src/main/java/com/aspsine/swipetoloadlayout/HorSwipeToLoadLayout.java
deleted file mode 100644
index 3233a20..0000000
--- a/library/src/main/java/com/aspsine/swipetoloadlayout/HorSwipeToLoadLayout.java
+++ /dev/null
@@ -1,1583 +0,0 @@
-package com.aspsine.swipetoloadlayout;
-
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.support.v4.view.MotionEventCompat;
-import android.support.v4.view.ViewCompat;
-import android.support.v4.view.ViewPager;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewConfiguration;
-import android.view.ViewGroup;
-import android.widget.AbsListView;
-import android.widget.Scroller;
-
-public class HorSwipeToLoadLayout extends ViewGroup {
- private static final String TAG = HorSwipeToLoadLayout.class.getSimpleName();
- private static final int DEFAULT_SWIPING_TO_REFRESH_TO_DEFAULT_SCROLLING_DURATION = 200;
- private static final int DEFAULT_RELEASE_TO_REFRESHING_SCROLLING_DURATION = 200;
- private static final int DEFAULT_REFRESH_COMPLETE_DELAY_DURATION = 300;
- private static final int DEFAULT_REFRESH_COMPLETE_TO_DEFAULT_SCROLLING_DURATION = 500;
- private static final int DEFAULT_DEFAULT_TO_REFRESHING_SCROLLING_DURATION = 500;
- private static final int DEFAULT_SWIPING_TO_LOAD_MORE_TO_DEFAULT_SCROLLING_DURATION = 200;
- private static final int DEFAULT_RELEASE_TO_LOADING_MORE_SCROLLING_DURATION = 200;
- private static final int DEFAULT_LOAD_MORE_COMPLETE_DELAY_DURATION = 300;
- private static final int DEFAULT_LOAD_MORE_COMPLETE_TO_DEFAULT_SCROLLING_DURATION = 300;
- private static final int DEFAULT_DEFAULT_TO_LOADING_MORE_SCROLLING_DURATION = 300;
- private static final float DEFAULT_DRAG_RATIO = 0.5F;
- private static final int INVALID_POINTER = -1;
- private static final int INVALID_COORDINATE = -1;
- private HorSwipeToLoadLayout.AutoScroller mAutoScroller;
- private OnRefreshListener mRefreshListener;
- private OnLoadMoreListener mLoadMoreListener;
- private View mHeaderView;
- private View mTargetView;
- private View mFooterView;
- private int mHeaderWidth;
- private int mFooterWidth;
- private boolean mHasHeaderView;
- private boolean mHasFooterView;
- private boolean mDebug;
- private float mDragRatio = DEFAULT_DRAG_RATIO;
- private boolean mAutoLoading;
- private final int mTouchSlop;
- private int mStatus = STATUS.STATUS_DEFAULT;
- private int mHeaderOffset;
- private int mTargetOffset;
- private int mFooterOffset;
- private float mInitDownY;
- private float mInitDownX;
- private float mLastY;
- private float mLastX;
- private int mActivePointerId;
- private boolean mRefreshEnabled = true;
- private boolean mLoadMoreEnabled = true;
- private int mStyle = STYLE.CLASSIC;
- private float mRefreshTriggerOffset;
- private float mLoadMoreTriggerOffset;
- private float mRefreshFinalDragOffset;
- private float mLoadMoreFinalDragOffset;
- /**
- * ATTRIBUTE:
- * Scrolling duration swiping to refresh -> default
- */
- private int mSwipingToRefreshToDefaultScrollingDuration = DEFAULT_SWIPING_TO_REFRESH_TO_DEFAULT_SCROLLING_DURATION;
-
- /**
- * ATTRIBUTE:
- * Scrolling duration status release to refresh -> refreshing
- */
- private int mReleaseToRefreshToRefreshingScrollingDuration = DEFAULT_RELEASE_TO_REFRESHING_SCROLLING_DURATION;
-
- /**
- * ATTRIBUTE:
- * Refresh complete delay duration
- */
- private int mRefreshCompleteDelayDuration = DEFAULT_REFRESH_COMPLETE_DELAY_DURATION;
-
- /**
- * ATTRIBUTE:
- * Scrolling duration status refresh complete -> default
- * {@link #setRefreshing(boolean)} false
- */
- private int mRefreshCompleteToDefaultScrollingDuration = DEFAULT_REFRESH_COMPLETE_TO_DEFAULT_SCROLLING_DURATION;
-
- /**
- * ATTRIBUTE:
- * Scrolling duration status default -> refreshing, mainly for auto refresh
- * {@link #setRefreshing(boolean)} true
- */
- private int mDefaultToRefreshingScrollingDuration = DEFAULT_DEFAULT_TO_REFRESHING_SCROLLING_DURATION;
-
- /**
- * ATTRIBUTE:
- * Scrolling duration status release to loading more -> loading more
- */
- private int mReleaseToLoadMoreToLoadingMoreScrollingDuration = DEFAULT_RELEASE_TO_LOADING_MORE_SCROLLING_DURATION;
-
-
- /**
- * ATTRIBUTE:
- * Load more complete delay duration
- */
- private int mLoadMoreCompleteDelayDuration = DEFAULT_LOAD_MORE_COMPLETE_DELAY_DURATION;
-
- /**
- * ATTRIBUTE:
- * Scrolling duration status load more complete -> default
- * {@link #setLoadingMore(boolean)} false
- */
- private int mLoadMoreCompleteToDefaultScrollingDuration = DEFAULT_LOAD_MORE_COMPLETE_TO_DEFAULT_SCROLLING_DURATION;
-
- /**
- * ATTRIBUTE:
- * Scrolling duration swiping to load more -> default
- */
- private int mSwipingToLoadMoreToDefaultScrollingDuration = DEFAULT_SWIPING_TO_LOAD_MORE_TO_DEFAULT_SCROLLING_DURATION;
-
- /**
- * ATTRIBUTE:
- * Scrolling duration status default -> loading more, mainly for auto load more
- * {@link #setLoadingMore(boolean)} true
- */
- private int mDefaultToLoadingMoreScrollingDuration = DEFAULT_DEFAULT_TO_LOADING_MORE_SCROLLING_DURATION;
-
- /**
- * the style enum
- */
- public static final class STYLE {
- public static final int CLASSIC = 0;
- public static final int ABOVE = 1;
- public static final int BLEW = 2;
- public static final int SCALE = 3;
- }
-
-
- public HorSwipeToLoadLayout(Context context) {
- this(context, (AttributeSet) null);
- }
-
- public HorSwipeToLoadLayout(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public HorSwipeToLoadLayout(Context context, AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
- TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.HorSwipeToLoadLayout, defStyleAttr, 0);
-
- try {
- int N = a.getIndexCount();
-
- for (int i = 0; i < N; ++i) {
- int attr = a.getIndex(i);
- if (attr == R.styleable.HorSwipeToLoadLayout_refresh_enabled) {
- this.setRefreshEnabled(a.getBoolean(attr, mRefreshEnabled));
- } else if (attr == R.styleable.HorSwipeToLoadLayout_load_more_enabled) {
- this.setLoadMoreEnabled(a.getBoolean(attr, mLoadMoreEnabled));
- } else if (attr == R.styleable.HorSwipeToLoadLayout_swipe_style) {
- this.setSwipeStyle(a.getInt(attr, mStyle));
- } else if (attr == R.styleable.HorSwipeToLoadLayout_drag_ratio) {
- this.setDragRatio(a.getFloat(attr, mDragRatio));
- } else if (attr == R.styleable.HorSwipeToLoadLayout_refresh_final_drag_offset) {
- this.setRefreshFinalDragOffset(a.getDimensionPixelOffset(attr, 0));
- } else if (attr == R.styleable.HorSwipeToLoadLayout_load_more_final_drag_offset) {
- this.setLoadMoreFinalDragOffset(a.getDimensionPixelOffset(attr, 0));
- } else if (attr == R.styleable.HorSwipeToLoadLayout_refresh_trigger_offset) {
- this.setRefreshTriggerOffset(a.getDimensionPixelOffset(attr, 0));
- } else if (attr == R.styleable.HorSwipeToLoadLayout_load_more_trigger_offset) {
- this.setLoadMoreTriggerOffset(a.getDimensionPixelOffset(attr, 0));
- } else if (attr == R.styleable.HorSwipeToLoadLayout_swiping_to_refresh_to_default_scrolling_duration) {
- this.setSwipingToRefreshToDefaultScrollingDuration(a.getInt(attr, mSwipingToRefreshToDefaultScrollingDuration));
- } else if (attr == R.styleable.HorSwipeToLoadLayout_release_to_refreshing_scrolling_duration) {
- this.setReleaseToRefreshingScrollingDuration(a.getInt(attr, mReleaseToRefreshToRefreshingScrollingDuration));
- } else if (attr == R.styleable.HorSwipeToLoadLayout_refresh_complete_delay_duration) {
- this.setRefreshCompleteDelayDuration(a.getInt(attr, mRefreshCompleteDelayDuration));
- } else if (attr == R.styleable.HorSwipeToLoadLayout_refresh_complete_to_default_scrolling_duration) {
- this.setRefreshCompleteToDefaultScrollingDuration(a.getInt(attr, mRefreshCompleteToDefaultScrollingDuration));
- } else if (attr == R.styleable.HorSwipeToLoadLayout_default_to_refreshing_scrolling_duration) {
- this.setDefaultToRefreshingScrollingDuration(a.getInt(attr, mDefaultToRefreshingScrollingDuration));
- } else if (attr == R.styleable.HorSwipeToLoadLayout_swiping_to_load_more_to_default_scrolling_duration) {
- this.setSwipingToLoadMoreToDefaultScrollingDuration(a.getInt(attr, mSwipingToLoadMoreToDefaultScrollingDuration));
- } else if (attr == R.styleable.HorSwipeToLoadLayout_release_to_loading_more_scrolling_duration) {
- this.setReleaseToLoadingMoreScrollingDuration(a.getInt(attr, mReleaseToLoadMoreToLoadingMoreScrollingDuration));
- } else if (attr == R.styleable.HorSwipeToLoadLayout_load_more_complete_delay_duration) {
- this.setLoadMoreCompleteDelayDuration(a.getInt(attr, mLoadMoreCompleteDelayDuration));
- } else if (attr == R.styleable.HorSwipeToLoadLayout_load_more_complete_to_default_scrolling_duration) {
- this.setLoadMoreCompleteToDefaultScrollingDuration(a.getInt(attr, mLoadMoreCompleteToDefaultScrollingDuration));
- } else if (attr == R.styleable.HorSwipeToLoadLayout_default_to_loading_more_scrolling_duration) {
- this.setDefaultToLoadingMoreScrollingDuration(a.getInt(attr, mDefaultToLoadingMoreScrollingDuration));
- }
- }
- } finally {
- a.recycle();
- }
-
- mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
- mAutoScroller = new HorSwipeToLoadLayout.AutoScroller();
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
- final int childNum = getChildCount();
- if (childNum == 0) {
- // no child return
- return;
- } else if (0 < childNum && childNum < 4) {
- mHeaderView = findViewById(R.id.swipe_refresh_header);
- mTargetView = findViewById(R.id.swipe_target);
- mFooterView = findViewById(R.id.swipe_load_more_footer);
- } else {
- // more than three children: unsupported!
- throw new IllegalStateException("Children num must equal or less than 3");
- }
- if (mTargetView == null) {
- return;
- }
- if (mHeaderView != null && mHeaderView instanceof SwipeTrigger) {
- mHeaderView.setVisibility(GONE);
- }
- if (mFooterView != null && mFooterView instanceof SwipeTrigger) {
- mFooterView.setVisibility(GONE);
- }
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- // header
- if (mHeaderView != null) {
- final View headerView = mHeaderView;
- measureChildWithMargins(headerView, widthMeasureSpec, 0, heightMeasureSpec, 0);
- MarginLayoutParams lp = ((MarginLayoutParams) headerView.getLayoutParams());
- mHeaderWidth = headerView.getMeasuredWidth() + lp.leftMargin + lp.rightMargin;
- if (mRefreshTriggerOffset < mHeaderWidth) {
- mRefreshTriggerOffset = mHeaderWidth;
- }
- }
- // target
- if (mTargetView != null) {
- final View targetView = mTargetView;
- measureChildWithMargins(targetView, widthMeasureSpec, 0, heightMeasureSpec, 0);
- }
- // footer
- if (mFooterView != null) {
- final View footerView = mFooterView;
- measureChildWithMargins(footerView, widthMeasureSpec, 0, heightMeasureSpec, 0);
- MarginLayoutParams lp = ((MarginLayoutParams) footerView.getLayoutParams());
- mFooterWidth = footerView.getMeasuredWidth() + lp.leftMargin + lp.rightMargin;
- if (mLoadMoreTriggerOffset < mFooterWidth) {
- mLoadMoreTriggerOffset = mFooterWidth;
- }
- }
- }
-
- @Override
- protected void onLayout(boolean changed, int l, int t, int r, int b) {
- layoutChildren();
-
- mHasHeaderView = (mHeaderView != null);
- mHasFooterView = (mFooterView != null);
- }
-
- /**
- * LayoutParams of RefreshLoadMoreLayout
- */
- public static class LayoutParams extends MarginLayoutParams {
-
- public LayoutParams(Context c, AttributeSet attrs) {
- super(c, attrs);
- }
-
- public LayoutParams(int width, int height) {
- super(width, height);
- }
-
- public LayoutParams(MarginLayoutParams source) {
- super(source);
- }
-
- public LayoutParams(ViewGroup.LayoutParams source) {
- super(source);
- }
- }
-
- @Override
- protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
- return new HorSwipeToLoadLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
- }
-
-
- @Override
- protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
- return new HorSwipeToLoadLayout.LayoutParams(p);
- }
-
- @Override
- public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
- return new HorSwipeToLoadLayout.LayoutParams(getContext(), attrs);
- }
-
- @Override
- public boolean dispatchTouchEvent(MotionEvent ev) {
- final int action = MotionEventCompat.getActionMasked(ev);
- switch (action) {
- case MotionEvent.ACTION_CANCEL:
- case MotionEvent.ACTION_UP:
- // swipeToRefresh -> finger up -> finger down if the status is still swipeToRefresh
- // in onInterceptTouchEvent ACTION_DOWN event will stop the scroller
- // if the event pass to the child view while ACTION_MOVE(condition is false)
- // in onInterceptTouchEvent ACTION_MOVE the ACTION_UP or ACTION_CANCEL will not be
- // passed to onInterceptTouchEvent and onTouchEvent. Instead It will be passed to
- // child view's onTouchEvent. So we must deal this situation in dispatchTouchEvent
- onActivePointerUp();
- break;
- }
- return super.dispatchTouchEvent(ev);
- }
-
- @Override
- public boolean onInterceptTouchEvent(MotionEvent event) {
- final int action = MotionEventCompat.getActionMasked(event);
- switch (action) {
- case MotionEvent.ACTION_DOWN:
-
- mActivePointerId = event.getPointerId(0);
- mInitDownY = mLastY = getMotionEventY(event, mActivePointerId);
- mInitDownX = mLastX = getMotionEventX(event, mActivePointerId);
-
- // if it isn't an ing status or default status
- if (STATUS.isSwipingToRefresh(mStatus) || STATUS.isSwipingToLoadMore(mStatus) ||
- STATUS.isReleaseToRefresh(mStatus) || STATUS.isReleaseToLoadMore(mStatus)) {
- // abort autoScrolling, not trigger the method #autoScrollFinished()
- mAutoScroller.abortIfRunning();
- if (mDebug) {
- Log.i(TAG, "Another finger down, abort auto scrolling, let the new finger handle");
- }
- }
-
- if (STATUS.isSwipingToRefresh(mStatus) || STATUS.isReleaseToRefresh(mStatus)
- || STATUS.isSwipingToLoadMore(mStatus) || STATUS.isReleaseToLoadMore(mStatus)) {
- return true;
- }
-
- // let children view handle the ACTION_DOWN;
-
- // 1. children consumed:
- // if at least one of children onTouchEvent() ACTION_DOWN return true.
- // ACTION_DOWN event will not return to SwipeToLoadLayout#onTouchEvent().
- // but the others action can be handled by SwipeToLoadLayout#onInterceptTouchEvent()
-
- // 2. children not consumed:
- // if children onTouchEvent() ACTION_DOWN return false.
- // ACTION_DOWN event will return to SwipeToLoadLayout's onTouchEvent().
- // SwipeToLoadLayout#onTouchEvent() ACTION_DOWN return true to consume the ACTION_DOWN event.
-
- // anyway: handle action down in onInterceptTouchEvent() to init is an good option
- break;
- case MotionEvent.ACTION_MOVE:
- if (mActivePointerId == INVALID_POINTER) {
- return false;
- }
- float y = getMotionEventY(event, mActivePointerId);
- float x = getMotionEventX(event, mActivePointerId);
- final float yInitDiff = y - mInitDownY;
- final float xInitDiff = x - mInitDownX;
- mLastY = y;
- mLastX = x;
- boolean moved = Math.abs(xInitDiff) > Math.abs(yInitDiff)
- && Math.abs(xInitDiff) > mTouchSlop;
- boolean triggerCondition =
- // refresh trigger condition
- (xInitDiff > 0 && moved && onCheckCanRefresh()) ||
- //load more trigger condition
- (xInitDiff < 0 && moved && onCheckCanLoadMore());
- if (triggerCondition) {
- // if the refresh's or load more's trigger condition is true,
- // intercept the move action event and pass it to SwipeToLoadLayout#onTouchEvent()
- return true;
- }
- break;
- case MotionEvent.ACTION_POINTER_UP: {
- onSecondaryPointerUp(event);
- mInitDownY = mLastY = getMotionEventY(event, mActivePointerId);
- mInitDownX = mLastX = getMotionEventX(event, mActivePointerId);
- break;
- }
- case MotionEvent.ACTION_UP:
- case MotionEvent.ACTION_CANCEL:
- mActivePointerId = INVALID_POINTER;
- break;
- }
- return super.onInterceptTouchEvent(event);
- }
-
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- final int action = MotionEventCompat.getActionMasked(event);
-
- switch (action) {
- case MotionEvent.ACTION_DOWN:
- mActivePointerId = event.getPointerId(0);
- return true;
-
- case MotionEvent.ACTION_MOVE:
- // take over the ACTION_MOVE event from SwipeToLoadLayout#onInterceptTouchEvent()
- // if condition is true
- final float y = getMotionEventY(event, mActivePointerId);
- final float x = getMotionEventX(event, mActivePointerId);
-
- final float yDiff = y - mLastY;
- final float xDiff = x - mLastX;
- mLastY = y;
- mLastX = x;
-
- if (Math.abs(yDiff) > Math.abs(xDiff) && Math.abs(yDiff) > mTouchSlop) {
- return false;
- }
-
- if (STATUS.isStatusDefault(mStatus)) {
- if (xDiff > 0 && onCheckCanRefresh()) {
- mRefreshCallback.onPrepare();
- setStatus(STATUS.STATUS_SWIPING_TO_REFRESH);
- } else if (xDiff < 0 && onCheckCanLoadMore()) {
- mLoadMoreCallback.onPrepare();
- setStatus(STATUS.STATUS_SWIPING_TO_LOAD_MORE);
- }
- } else if (STATUS.isRefreshStatus(mStatus)) {
- if (mTargetOffset <= 0) {
- setStatus(STATUS.STATUS_DEFAULT);
- fixCurrentStatusLayout();
- return false;
- }
- } else if (STATUS.isLoadMoreStatus(mStatus)) {
- if (mTargetOffset >= 0) {
- setStatus(STATUS.STATUS_DEFAULT);
- fixCurrentStatusLayout();
- return false;
- }
- }
-
- if (STATUS.isRefreshStatus(mStatus)) {
- if (STATUS.isSwipingToRefresh(mStatus) || STATUS.isReleaseToRefresh(mStatus)) {
- if (mTargetOffset >= mRefreshTriggerOffset) {
- setStatus(STATUS.STATUS_RELEASE_TO_REFRESH);
- } else {
- setStatus(STATUS.STATUS_SWIPING_TO_REFRESH);
- }
- fingerScroll(xDiff);
- }
- } else if (STATUS.isLoadMoreStatus(mStatus)) {
- if (STATUS.isSwipingToLoadMore(mStatus) || STATUS.isReleaseToLoadMore(mStatus)) {
- if (-mTargetOffset >= mLoadMoreTriggerOffset) {
- setStatus(STATUS.STATUS_RELEASE_TO_LOAD_MORE);
- } else {
- setStatus(STATUS.STATUS_SWIPING_TO_LOAD_MORE);
- }
- fingerScroll(xDiff);
- }
- }
- return true;
-
- case MotionEvent.ACTION_POINTER_DOWN: {
- final int pointerIndex = MotionEventCompat.getActionIndex(event);
- final int pointerId = event.getPointerId(pointerIndex);
- if (pointerId != INVALID_POINTER) {
- mActivePointerId = pointerId;
- }
- mInitDownY = mLastY = getMotionEventY(event, mActivePointerId);
- mInitDownX = mLastX = getMotionEventX(event, mActivePointerId);
- break;
- }
- case MotionEvent.ACTION_POINTER_UP: {
- onSecondaryPointerUp(event);
- mInitDownY = mLastY = getMotionEventY(event, mActivePointerId);
- mInitDownX = mLastX = getMotionEventX(event, mActivePointerId);
- break;
- }
- case MotionEvent.ACTION_UP:
- case MotionEvent.ACTION_CANCEL:
- if (mActivePointerId == INVALID_POINTER) {
- return false;
- }
- mActivePointerId = INVALID_POINTER;
- break;
- default:
- break;
- }
- return super.onTouchEvent(event);
- }
-
- /**
- * set debug mode(default value false)
- *
- * @param debug if true log on, false log off
- */
- public void setDebug(boolean debug) {
- this.mDebug = debug;
- }
-
- /**
- * is refresh function is enabled
- *
- * @return
- */
- public boolean isRefreshEnabled() {
- return mRefreshEnabled;
- }
-
- /**
- * switch refresh function on or off
- *
- * @param enable
- */
- public void setRefreshEnabled(boolean enable) {
- this.mRefreshEnabled = enable;
- }
-
- /**
- * is load more function is enabled
- *
- * @return
- */
- public boolean isLoadMoreEnabled() {
- return mLoadMoreEnabled;
- }
-
- /**
- * switch load more function on or off
- *
- * @param enable
- */
- public void setLoadMoreEnabled(boolean enable) {
- this.mLoadMoreEnabled = enable;
- }
-
- /**
- * is current status refreshing
- *
- * @return
- */
- public boolean isRefreshing() {
- return STATUS.isRefreshing(mStatus);
- }
-
- /**
- * is current status loading more
- *
- * @return
- */
- public boolean isLoadingMore() {
- return STATUS.isLoadingMore(mStatus);
- }
-
- /**
- * set refresh header view, the view must at lease be an implement of {@code SwipeRefreshTrigger}.
- * the view can also implement {@code SwipeTrigger} for more extension functions
- *
- * @param view
- */
- public void setRefreshHeaderView(View view) {
- if (view instanceof SwipeRefreshTrigger) {
- if (mHeaderView != null && mHeaderView != view) {
- removeView(mHeaderView);
- }
- if (mHeaderView != view) {
- this.mHeaderView = view;
- addView(view);
- }
- } else {
- Log.e(TAG, "Refresh header view must be an implement of SwipeRefreshTrigger");
- }
- }
-
- /**
- * set load more footer view, the view must at least be an implement of SwipeLoadTrigger
- * the view can also implement {@code SwipeTrigger} for more extension functions
- *
- * @param view
- */
- public void setLoadMoreFooterView(View view) {
- if (view instanceof SwipeLoadMoreTrigger) {
- if (mFooterView != null && mFooterView != view) {
- removeView(mFooterView);
- }
- if (mFooterView != view) {
- this.mFooterView = view;
- addView(mFooterView);
- }
- } else {
- Log.e(TAG, "Load more footer view must be an implement of SwipeLoadTrigger");
- }
- }
-
- /**
- * set the style of the refresh header
- *
- * @param style
- */
- public void setSwipeStyle(int style) {
- this.mStyle = style;
- requestLayout();
- }
-
- /**
- * set how hard to drag. bigger easier, smaller harder;
- *
- * @param dragRatio default value is {@link #DEFAULT_DRAG_RATIO}
- */
- public void setDragRatio(float dragRatio) {
- this.mDragRatio = dragRatio;
- }
-
- /**
- * set the value of {@link #mRefreshTriggerOffset}.
- * Default value is the refresh header view height {@link #mHeaderWidth}
- * If the offset you set is smaller than {@link #mHeaderWidth} or not set,
- * using {@link #mHeaderWidth} as default value
- *
- * @param offset
- */
- public void setRefreshTriggerOffset(int offset) {
- mRefreshTriggerOffset = offset;
- }
-
- /**
- * set the value of {@link #mLoadMoreTriggerOffset}.
- * Default value is the load more footer view height {@link #mFooterWidth}
- * If the offset you set is smaller than {@link #mFooterWidth} or not set,
- * using {@link #mFooterWidth} as default value
- *
- * @param offset
- */
- public void setLoadMoreTriggerOffset(int offset) {
- mLoadMoreTriggerOffset = offset;
- }
-
- /**
- * Set the final offset you can swipe to refresh.
- * If the offset you set is 0(default value) or smaller than {@link #mRefreshTriggerOffset}
- * there no final offset
- *
- * @param offset
- */
- public void setRefreshFinalDragOffset(int offset) {
- mRefreshFinalDragOffset = offset;
- }
-
- /**
- * Set the final offset you can swipe to load more.
- * If the offset you set is 0(default value) or smaller than {@link #mLoadMoreTriggerOffset},
- * there no final offset
- *
- * @param offset
- */
- public void setLoadMoreFinalDragOffset(int offset) {
- mLoadMoreFinalDragOffset = offset;
- }
-
- /**
- * set {@link #mSwipingToRefreshToDefaultScrollingDuration} in milliseconds
- *
- * @param duration
- */
- public void setSwipingToRefreshToDefaultScrollingDuration(int duration) {
- this.mSwipingToRefreshToDefaultScrollingDuration = duration;
- }
-
- /**
- * set {@link #mReleaseToRefreshToRefreshingScrollingDuration} in milliseconds
- *
- * @param duration
- */
- public void setReleaseToRefreshingScrollingDuration(int duration) {
- this.mReleaseToRefreshToRefreshingScrollingDuration = duration;
- }
-
- /**
- * set {@link #mRefreshCompleteDelayDuration} in milliseconds
- *
- * @param duration
- */
- public void setRefreshCompleteDelayDuration(int duration) {
- this.mRefreshCompleteDelayDuration = duration;
- }
-
- /**
- * set {@link #mRefreshCompleteToDefaultScrollingDuration} in milliseconds
- *
- * @param duration
- */
- public void setRefreshCompleteToDefaultScrollingDuration(int duration) {
- this.mRefreshCompleteToDefaultScrollingDuration = duration;
- }
-
- /**
- * set {@link #mDefaultToRefreshingScrollingDuration} in milliseconds
- *
- * @param duration
- */
- public void setDefaultToRefreshingScrollingDuration(int duration) {
- this.mDefaultToRefreshingScrollingDuration = duration;
- }
-
- /**
- * set {@link @mSwipingToLoadMoreToDefaultScrollingDuration} in milliseconds
- *
- * @param duration
- */
- public void setSwipingToLoadMoreToDefaultScrollingDuration(int duration) {
- this.mSwipingToLoadMoreToDefaultScrollingDuration = duration;
- }
-
- /**
- * set {@link #mReleaseToLoadMoreToLoadingMoreScrollingDuration} in milliseconds
- *
- * @param duration
- */
- public void setReleaseToLoadingMoreScrollingDuration(int duration) {
- this.mReleaseToLoadMoreToLoadingMoreScrollingDuration = duration;
- }
-
- /**
- * set {@link #mLoadMoreCompleteDelayDuration} in milliseconds
- *
- * @param duration
- */
- public void setLoadMoreCompleteDelayDuration(int duration) {
- this.mLoadMoreCompleteDelayDuration = duration;
- }
-
- /**
- * set {@link #mLoadMoreCompleteToDefaultScrollingDuration} in milliseconds
- *
- * @param duration
- */
- public void setLoadMoreCompleteToDefaultScrollingDuration(int duration) {
- this.mLoadMoreCompleteToDefaultScrollingDuration = duration;
- }
-
- /**
- * set {@link #mDefaultToLoadingMoreScrollingDuration} in milliseconds
- *
- * @param duration
- */
- public void setDefaultToLoadingMoreScrollingDuration(int duration) {
- this.mDefaultToLoadingMoreScrollingDuration = duration;
- }
-
- /**
- * set an {@link OnRefreshListener} to listening refresh event
- *
- * @param listener
- */
- public void setOnRefreshListener(OnRefreshListener listener) {
- this.mRefreshListener = listener;
- }
-
- /**
- * set an {@link OnLoadMoreListener} to listening load more event
- *
- * @param listener
- */
- public void setOnLoadMoreListener(OnLoadMoreListener listener) {
- this.mLoadMoreListener = listener;
- }
-
- /**
- * auto refresh or cancel
- *
- * @param refreshing
- */
- public void setRefreshing(boolean refreshing) {
- if (!isRefreshEnabled() || mHeaderView == null) {
- return;
- }
- this.mAutoLoading = refreshing;
- if (refreshing) {
- if (STATUS.isStatusDefault(mStatus)) {
- setStatus(STATUS.STATUS_SWIPING_TO_REFRESH);
- scrollDefaultToRefreshing();
- }
- } else {
- if (STATUS.isRefreshing(mStatus)) {
- mRefreshCallback.onComplete();
- postDelayed(new Runnable() {
- @Override
- public void run() {
- scrollRefreshingToDefault();
- }
- }, mRefreshCompleteDelayDuration);
- }
- }
- }
-
- /**
- * auto loading more or cancel
- *
- * @param loadingMore
- */
- public void setLoadingMore(boolean loadingMore) {
- if (!isLoadMoreEnabled() || mFooterView == null) {
- return;
- }
- this.mAutoLoading = loadingMore;
- if (loadingMore) {
- if (STATUS.isStatusDefault(mStatus)) {
- setStatus(STATUS.STATUS_SWIPING_TO_LOAD_MORE);
- scrollDefaultToLoadingMore();
- }
- } else {
- if (STATUS.isLoadingMore(mStatus)) {
- mLoadMoreCallback.onComplete();
- postDelayed(new Runnable() {
- @Override
- public void run() {
- scrollLoadingMoreToDefault();
- }
- }, mLoadMoreCompleteDelayDuration);
- }
- }
- }
-
- /**
- * copy from {@link android.support.v4.widget.SwipeRefreshLayout#canChildScrollUp()}
- *
- * @return Whether it is possible for the child view of this layout to
- * scroll left. Override this if the child view is a custom view.
- */
- protected boolean canChildScrollLeft() {
- if (android.os.Build.VERSION.SDK_INT < 14) {
- if (mTargetView instanceof AbsListView) {
- final AbsListView absListView = (AbsListView) mTargetView;
- return absListView.getChildCount() > 0
- && (absListView.getFirstVisiblePosition() > 0 || absListView.getChildAt(0)
- .getLeft() < absListView.getPaddingLeft());
- } else if (mTargetView instanceof ViewPager) {
- ViewPager pager = (ViewPager) mTargetView;
- return pager.getCurrentItem() > 0;
- } else {
- return ViewCompat.canScrollHorizontally(mTargetView, -1) || mTargetView.getScrollX() > 0;
- }
- } else {
- if (mTargetView instanceof ViewPager) {
- ViewPager pager = (ViewPager) mTargetView;
- return pager.getCurrentItem() > 0;
- }
- return ViewCompat.canScrollHorizontally(mTargetView, -1);
- }
- }
-
- /**
- * Whether it is possible for the child view of this layout to
- * scroll right. Override this if the child view is a custom view.
- *
- * @return
- */
- protected boolean canChildScrollRight() {
- if (android.os.Build.VERSION.SDK_INT < 14) {
- if (mTargetView instanceof AbsListView) {
- final AbsListView absListView = (AbsListView) mTargetView;
- return absListView.getChildCount() > 0
- && (absListView.getLastVisiblePosition() < absListView.getChildCount() - 1
- || absListView.getChildAt(absListView.getChildCount() - 1).getRight() > absListView.getPaddingRight());
- } else if (mTargetView instanceof ViewPager) {
- ViewPager pager = (ViewPager) mTargetView;
- return pager.getCurrentItem() < pager.getAdapter().getCount() - 1;
- } else {
- return ViewCompat.canScrollHorizontally(mTargetView, 1) || mTargetView.getScrollX() < 0;
- }
- } else {
- if (mTargetView instanceof ViewPager) {
- ViewPager pager = (ViewPager) mTargetView;
- return pager.getCurrentItem() < pager.getAdapter().getCount() - 1;
- }
- return ViewCompat.canScrollHorizontally(mTargetView, 1);
- }
- }
-
- /**
- * @see #onLayout(boolean, int, int, int, int)
- */
- private void layoutChildren() {
- final int width = getMeasuredWidth();
- final int height = getMeasuredHeight();
-
- final int paddingLeft = getPaddingLeft();
- final int paddingTop = getPaddingTop();
- final int paddingRight = getPaddingRight();
- final int paddingBottom = getPaddingBottom();
-
- if (mTargetView == null) {
- return;
- }
-
- // layout header
- if (mHeaderView != null) {
- final View headerView = mHeaderView;
- MarginLayoutParams lp = (MarginLayoutParams) headerView.getLayoutParams();
- final int headerLeft;
- final int headerTop = paddingTop + lp.topMargin;
- switch (mStyle) {
- case STYLE.CLASSIC:
- // classic
- headerLeft = paddingLeft + lp.leftMargin - mHeaderWidth + mHeaderOffset;
- break;
- case STYLE.ABOVE:
- // classic
- headerLeft = paddingLeft + lp.leftMargin - mHeaderWidth + mHeaderOffset;
- break;
- case STYLE.BLEW:
- // blew
- headerLeft = paddingLeft + lp.leftMargin;
- break;
- case STYLE.SCALE:
- // scale
- headerLeft = paddingLeft + lp.leftMargin - mHeaderWidth / 2 + mHeaderOffset / 2;
- break;
- default:
- // classic
- headerLeft = paddingLeft + lp.leftMargin - mHeaderWidth + mHeaderOffset;
- break;
- }
- final int headerRight = headerLeft + headerView.getMeasuredWidth();
- final int headerBottom = headerTop + headerView.getMeasuredHeight();
- headerView.layout(headerLeft, headerTop, headerRight, headerBottom);
- }
-
-
- // layout target
- if (mTargetView != null) {
- final View targetView = mTargetView;
- MarginLayoutParams lp = (MarginLayoutParams) targetView.getLayoutParams();
- final int targetLeft;
- final int targetTop = paddingTop + lp.topMargin;
-
- switch (mStyle) {
- case STYLE.CLASSIC:
- // classic
- targetLeft = paddingLeft + lp.leftMargin + mTargetOffset;
- break;
- case STYLE.ABOVE:
- // above
- targetLeft = paddingLeft + lp.leftMargin;
- break;
- case STYLE.BLEW:
- // classic
- targetLeft = paddingLeft + lp.leftMargin + mTargetOffset;
- break;
- case STYLE.SCALE:
- // classic
- targetLeft = paddingLeft + lp.leftMargin + mTargetOffset;
- break;
- default:
- // classic
- targetLeft = paddingLeft + lp.leftMargin + mTargetOffset;
- break;
- }
- final int targetRight = targetLeft + targetView.getMeasuredWidth();
- final int targetBottom = targetTop + targetView.getMeasuredHeight();
- targetView.layout(targetLeft, targetTop, targetRight, targetBottom);
- }
-
- // layout footer
- if (mFooterView != null) {
- final View footerView = mFooterView;
- MarginLayoutParams lp = (MarginLayoutParams) footerView.getLayoutParams();
- final int footerRight;
- final int footerTop = paddingTop + lp.topMargin;
- switch (mStyle) {
- case STYLE.CLASSIC:
- // classic
- footerRight = width - paddingRight - lp.rightMargin + mFooterWidth + mFooterOffset;
- break;
- case STYLE.ABOVE:
- // classic
- footerRight = width - paddingRight - lp.rightMargin + mFooterWidth + mFooterOffset;
- break;
- case STYLE.BLEW:
- // blew
- footerRight = width - paddingRight - lp.rightMargin;
- break;
- case STYLE.SCALE:
- // scale
- footerRight = width - paddingRight - lp.rightMargin + mFooterWidth / 2 + mFooterOffset / 2;
- break;
- default:
- // classic
- footerRight = width - paddingRight - lp.rightMargin + mFooterWidth + mFooterOffset;
- break;
- }
- final int footerLeft = footerRight - footerView.getMeasuredWidth();
- final int footerBottom = footerTop + footerView.getMeasuredHeight();
-
- footerView.layout(footerLeft, footerTop, footerRight, footerBottom);
- }
-
- if (mStyle == STYLE.CLASSIC
- || mStyle == STYLE.ABOVE) {
- if (mHeaderView != null) {
- mHeaderView.bringToFront();
- }
- if (mFooterView != null) {
- mFooterView.bringToFront();
- }
- } else if (mStyle == STYLE.BLEW || mStyle == STYLE.SCALE) {
- if (mTargetView != null) {
- mTargetView.bringToFront();
- }
- }
- }
-
- private void fixCurrentStatusLayout() {
- if (STATUS.isRefreshing(mStatus)) {
- mTargetOffset = (int) (mRefreshTriggerOffset + 0.5f);
- mHeaderOffset = mTargetOffset;
- mFooterOffset = 0;
- layoutChildren();
- invalidate();
- } else if (STATUS.isStatusDefault(mStatus)) {
- mTargetOffset = 0;
- mHeaderOffset = 0;
- mFooterOffset = 0;
- layoutChildren();
- invalidate();
- } else if (STATUS.isLoadingMore(mStatus)) {
- mTargetOffset = -(int) (mLoadMoreTriggerOffset + 0.5f);
- mHeaderOffset = 0;
- mFooterOffset = mTargetOffset;
- layoutChildren();
- invalidate();
- }
- }
-
- /**
- * scrolling by physical touch with your fingers
- *
- * @param xDiff
- */
- private void fingerScroll(final float xDiff) {
- float ratio = mDragRatio;
- float xScrolled = xDiff * ratio;
-
- // make sure (targetOffset>0 -> targetOffset=0 -> default status)
- // or (targetOffset<0 -> targetOffset=0 -> default status)
- // forbidden fling (targetOffset>0 -> targetOffset=0 ->targetOffset<0 -> default status)
- // or (targetOffset<0 -> targetOffset=0 ->targetOffset>0 -> default status)
- // I am so smart :)
-
- float tmpTargetOffset = xScrolled + mTargetOffset;
- if ((tmpTargetOffset > 0 && mTargetOffset < 0)
- || (tmpTargetOffset < 0 && mTargetOffset > 0)) {
- xScrolled = -mTargetOffset;
- }
-
-
- if (mRefreshFinalDragOffset >= mRefreshTriggerOffset && tmpTargetOffset > mRefreshFinalDragOffset) {
- xScrolled = mRefreshFinalDragOffset - mTargetOffset;
- } else if (mLoadMoreFinalDragOffset >= mLoadMoreTriggerOffset && -tmpTargetOffset > mLoadMoreFinalDragOffset) {
- xScrolled = -mLoadMoreFinalDragOffset - mTargetOffset;
- }
-
- if (STATUS.isRefreshStatus(mStatus)) {
- mRefreshCallback.onMove(mTargetOffset, false, false);
- } else if (STATUS.isLoadMoreStatus(mStatus)) {
- mLoadMoreCallback.onMove(mTargetOffset, false, false);
- }
- updateScroll(xScrolled);
- }
-
- private void autoScroll(final float xScrolled) {
-
- if (STATUS.isSwipingToRefresh(mStatus)) {
- mRefreshCallback.onMove(mTargetOffset, false, true);
- } else if (STATUS.isReleaseToRefresh(mStatus)) {
- mRefreshCallback.onMove(mTargetOffset, false, true);
- } else if (STATUS.isRefreshing(mStatus)) {
- mRefreshCallback.onMove(mTargetOffset, true, true);
- } else if (STATUS.isSwipingToLoadMore(mStatus)) {
- mLoadMoreCallback.onMove(mTargetOffset, false, true);
- } else if (STATUS.isReleaseToLoadMore(mStatus)) {
- mLoadMoreCallback.onMove(mTargetOffset, false, true);
- } else if (STATUS.isLoadingMore(mStatus)) {
- mLoadMoreCallback.onMove(mTargetOffset, true, true);
- }
- updateScroll(xScrolled);
- }
-
- /**
- * Process the scrolling(auto or physical) and append the diff values to mTargetOffset
- * I think it's the most busy and core method. :) a ha ha ha ha...
- *
- * @param xScrolled
- */
- private void updateScroll(final float xScrolled) {
- if (xScrolled == 0) {
- return;
- }
- mTargetOffset += xScrolled;
-
- if (STATUS.isRefreshStatus(mStatus)) {
- mHeaderOffset = mTargetOffset;
- mFooterOffset = 0;
- } else if (STATUS.isLoadMoreStatus(mStatus)) {
- mFooterOffset = mTargetOffset;
- mHeaderOffset = 0;
- }
-
- if (mDebug) {
- Log.i(TAG, "mTargetOffset = " + mTargetOffset);
- }
- layoutChildren();
- invalidate();
- }
-
- /**
- * on active finger up
- */
- private void onActivePointerUp() {
- if (STATUS.isSwipingToRefresh(mStatus)) {
- // simply return
- scrollSwipingToRefreshToDefault();
-
- } else if (STATUS.isSwipingToLoadMore(mStatus)) {
- // simply return
- scrollSwipingToLoadMoreToDefault();
-
- } else if (STATUS.isReleaseToRefresh(mStatus)) {
- // return to header height and perform refresh
- mRefreshCallback.onRelease();
- scrollReleaseToRefreshToRefreshing();
-
- } else if (STATUS.isReleaseToLoadMore(mStatus)) {
- // return to footer height and perform loadMore
- mLoadMoreCallback.onRelease();
- scrollReleaseToLoadMoreToLoadingMore();
-
- }
- }
-
- /**
- * on not active finger up
- *
- * @param ev
- */
- private void onSecondaryPointerUp(MotionEvent ev) {
- final int pointerIndex = MotionEventCompat.getActionIndex(ev);
- final int pointerId = ev.getPointerId(pointerIndex);
- if (pointerId == mActivePointerId) {
- // This was our active pointer going up. Choose a new
- // active pointer and adjust accordingly.
- final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
- mActivePointerId = ev.getPointerId(newPointerIndex);
- }
- }
-
- private void scrollDefaultToRefreshing() {
- mAutoScroller.autoScroll((int) (mRefreshTriggerOffset + 0.5f), mDefaultToRefreshingScrollingDuration);
- }
-
- private void scrollDefaultToLoadingMore() {
- mAutoScroller.autoScroll(-(int) (mLoadMoreTriggerOffset + 0.5f), mDefaultToLoadingMoreScrollingDuration);
- }
-
- private void scrollSwipingToRefreshToDefault() {
- mAutoScroller.autoScroll(-mHeaderOffset, mSwipingToRefreshToDefaultScrollingDuration);
- }
-
- private void scrollSwipingToLoadMoreToDefault() {
- mAutoScroller.autoScroll(-mFooterOffset, mSwipingToLoadMoreToDefaultScrollingDuration);
- }
-
- private void scrollReleaseToRefreshToRefreshing() {
- mAutoScroller.autoScroll(mHeaderWidth - mHeaderOffset, mReleaseToRefreshToRefreshingScrollingDuration);
- }
-
- private void scrollReleaseToLoadMoreToLoadingMore() {
- mAutoScroller.autoScroll(-mFooterOffset - mFooterWidth, mReleaseToLoadMoreToLoadingMoreScrollingDuration);
- }
-
- private void scrollRefreshingToDefault() {
- mAutoScroller.autoScroll(-mHeaderOffset, mRefreshCompleteToDefaultScrollingDuration);
- }
-
- private void scrollLoadingMoreToDefault() {
- mAutoScroller.autoScroll(-mFooterOffset, mLoadMoreCompleteToDefaultScrollingDuration);
- }
-
- /**
- * invoke when {@link AutoScroller#finish()} is automatic
- */
- private void autoScrollFinished() {
- int mLastStatus = mStatus;
-
- if (STATUS.isReleaseToRefresh(mStatus)) {
- setStatus(STATUS.STATUS_REFRESHING);
- fixCurrentStatusLayout();
- mRefreshCallback.onRefresh();
-
- } else if (STATUS.isRefreshing(mStatus)) {
- setStatus(STATUS.STATUS_DEFAULT);
- fixCurrentStatusLayout();
- mRefreshCallback.onReset();
-
- } else if (STATUS.isSwipingToRefresh(mStatus)) {
- if (mAutoLoading) {
- mAutoLoading = false;
- setStatus(STATUS.STATUS_REFRESHING);
- fixCurrentStatusLayout();
- mRefreshCallback.onRefresh();
- } else {
- setStatus(STATUS.STATUS_DEFAULT);
- fixCurrentStatusLayout();
- mRefreshCallback.onReset();
- }
- } else if (STATUS.isStatusDefault(mStatus)) {
-
- } else if (STATUS.isSwipingToLoadMore(mStatus)) {
- if (mAutoLoading) {
- mAutoLoading = false;
- setStatus(STATUS.STATUS_LOADING_MORE);
- fixCurrentStatusLayout();
- mLoadMoreCallback.onLoadMore();
- } else {
- setStatus(STATUS.STATUS_DEFAULT);
- fixCurrentStatusLayout();
- mLoadMoreCallback.onReset();
- }
- } else if (STATUS.isLoadingMore(mStatus)) {
- setStatus(STATUS.STATUS_DEFAULT);
- fixCurrentStatusLayout();
- mLoadMoreCallback.onReset();
- } else if (STATUS.isReleaseToLoadMore(mStatus)) {
- setStatus(STATUS.STATUS_LOADING_MORE);
- fixCurrentStatusLayout();
- mLoadMoreCallback.onLoadMore();
- } else {
- throw new IllegalStateException("illegal state: " + STATUS.getStatus(mStatus));
- }
-
- if (mDebug) {
- Log.i(TAG, STATUS.getStatus(mLastStatus) + " -> " + STATUS.getStatus(mStatus));
- }
- }
-
- /**
- * check if it can refresh
- *
- * @return
- */
- private boolean onCheckCanRefresh() {
-
- return mRefreshEnabled && !canChildScrollLeft() && mHasHeaderView && mRefreshTriggerOffset > 0;
- }
-
- /**
- * check if it can load more
- *
- * @return
- */
- private boolean onCheckCanLoadMore() {
-
- return mLoadMoreEnabled && !canChildScrollRight() && mHasFooterView && mLoadMoreTriggerOffset > 0;
- }
-
- private float getMotionEventY(MotionEvent event, int activePointerId) {
- final int index = event.findPointerIndex(activePointerId);
- if (index < 0) {
- return INVALID_COORDINATE;
- }
- return event.getY(index);
- }
-
- private float getMotionEventX(MotionEvent event, int activePointId) {
- final int index = event.findPointerIndex(activePointId);
- if (index < 0) {
- return INVALID_COORDINATE;
- }
- return event.getX(index);
- }
-
- RefreshCallback mRefreshCallback = new RefreshCallback() {
- @Override
- public void onPrepare() {
- if (mHeaderView != null && mHeaderView instanceof SwipeTrigger && STATUS.isStatusDefault(mStatus)) {
- mHeaderView.setVisibility(VISIBLE);
- ((SwipeTrigger) mHeaderView).onPrepare();
- }
- }
-
- @Override
- public void onMove(int x, boolean isComplete, boolean automatic) {
- if (mHeaderView != null && mHeaderView instanceof SwipeTrigger && STATUS.isRefreshStatus(mStatus)) {
- if (mHeaderView.getVisibility() != VISIBLE) {
- mHeaderView.setVisibility(VISIBLE);
- }
- ((SwipeTrigger) mHeaderView).onMove(x, isComplete, automatic);
- }
- }
-
- @Override
- public void onRelease() {
- if (mHeaderView != null && mHeaderView instanceof SwipeTrigger && STATUS.isReleaseToRefresh(mStatus)) {
- ((SwipeTrigger) mHeaderView).onRelease();
- }
- }
-
- @Override
- public void onRefresh() {
- if (mHeaderView != null && STATUS.isRefreshing(mStatus)) {
- if (mHeaderView instanceof SwipeRefreshTrigger) {
- ((SwipeRefreshTrigger) mHeaderView).onRefresh();
- }
- if (mRefreshListener != null) {
- mRefreshListener.onRefresh();
- }
- }
- }
-
- @Override
- public void onComplete() {
- if (mHeaderView != null && mHeaderView instanceof SwipeTrigger) {
- ((SwipeTrigger) mHeaderView).onComplete();
- }
- }
-
- @Override
- public void onReset() {
- if (mHeaderView != null && mHeaderView instanceof SwipeTrigger && STATUS.isStatusDefault(mStatus)) {
- ((SwipeTrigger) mHeaderView).onReset();
- mHeaderView.setVisibility(GONE);
- }
- }
- };
-
- LoadMoreCallback mLoadMoreCallback = new LoadMoreCallback() {
-
- @Override
- public void onPrepare() {
- if (mFooterView != null && mFooterView instanceof SwipeTrigger && STATUS.isStatusDefault(mStatus)) {
- mFooterView.setVisibility(VISIBLE);
- ((SwipeTrigger) mFooterView).onPrepare();
- }
- }
-
- @Override
- public void onMove(int x, boolean isComplete, boolean automatic) {
- if (mFooterView != null && mFooterView instanceof SwipeTrigger && STATUS.isLoadMoreStatus(mStatus)) {
- if (mFooterView.getVisibility() != VISIBLE) {
- mFooterView.setVisibility(VISIBLE);
- }
- ((SwipeTrigger) mFooterView).onMove(x, isComplete, automatic);
- }
- }
-
- @Override
- public void onRelease() {
- if (mFooterView != null && mFooterView instanceof SwipeTrigger && STATUS.isReleaseToLoadMore(mStatus)) {
- ((SwipeTrigger) mFooterView).onRelease();
- }
- }
-
- @Override
- public void onLoadMore() {
- if (mFooterView != null && STATUS.isLoadingMore(mStatus)) {
- if (mFooterView instanceof SwipeLoadMoreTrigger) {
- ((SwipeLoadMoreTrigger) mFooterView).onLoadMore();
- }
- if (mLoadMoreListener != null) {
- mLoadMoreListener.onLoadMore();
- }
- }
- }
-
- @Override
- public void onComplete() {
- if (mFooterView != null && mFooterView instanceof SwipeTrigger) {
- ((SwipeTrigger) mFooterView).onComplete();
- }
- }
-
- @Override
- public void onReset() {
- if (mFooterView != null && mFooterView instanceof SwipeTrigger && STATUS.isStatusDefault(mStatus)) {
- ((SwipeTrigger) mFooterView).onReset();
- mFooterView.setVisibility(GONE);
- }
- }
- };
-
- /**
- * refresh event callback
- */
- abstract class RefreshCallback implements SwipeTrigger, SwipeRefreshTrigger {
- }
-
- /**
- * load more event callback
- */
- abstract class LoadMoreCallback implements SwipeTrigger, SwipeLoadMoreTrigger {
- }
-
- private class AutoScroller implements Runnable {
-
- private Scroller mScroller;
-
- private int mmLastX;
-
- private boolean mRunning = false;
-
- private boolean mAbort = false;
-
- public AutoScroller() {
- mScroller = new Scroller(getContext());
- }
-
- @Override
- public void run() {
- boolean finish = !mScroller.computeScrollOffset() || mScroller.isFinished();
- int currx = mScroller.getCurrX();
- int xDiff = currx - mmLastX;
- if (finish) {
- finish();
- } else {
- mmLastX = currx;
- HorSwipeToLoadLayout.this.autoScroll(xDiff);
- post(this);
- }
- }
-
- /**
- * remove the post callbacks and reset default values
- */
- private void finish() {
- mmLastX = 0;
- mRunning = false;
- removeCallbacks(this);
- // if abort by user, don't call
- if (!mAbort) {
- autoScrollFinished();
- }
- }
-
- /**
- * abort scroll if it is scrolling
- */
- public void abortIfRunning() {
- if (mRunning) {
- if (!mScroller.isFinished()) {
- mAbort = true;
- mScroller.forceFinished(true);
- }
- finish();
- mAbort = false;
- }
- }
-
- /**
- * The param xScrolled here isn't final pos of y.
- * It's just like the yScrolled param in the
- * {@link #updateScroll(float yScrolled)}
- *
- * @param xScrolled
- * @param duration
- */
- private void autoScroll(int xScrolled, int duration) {
- removeCallbacks(this);
- mmLastX = 0;
- if (!mScroller.isFinished()) {
- mScroller.forceFinished(true);
- }
- mScroller.startScroll(0, 0, xScrolled, 0, duration);
- post(this);
- mRunning = true;
- }
- }
-
- /**
- * Set the current status for better control
- *
- * @param status
- */
- private void setStatus(int status) {
- mStatus = status;
- if (mDebug) {
- STATUS.printStatus(status);
- }
- }
-
- /**
- * an inner util class.
- * enum of status
- */
- private final static class STATUS {
- private static final int STATUS_REFRESH_RETURNING = -4;
- private static final int STATUS_REFRESHING = -3;
- private static final int STATUS_RELEASE_TO_REFRESH = -2;
- private static final int STATUS_SWIPING_TO_REFRESH = -1;
- private static final int STATUS_DEFAULT = 0;
- private static final int STATUS_SWIPING_TO_LOAD_MORE = 1;
- private static final int STATUS_RELEASE_TO_LOAD_MORE = 2;
- private static final int STATUS_LOADING_MORE = 3;
- private static final int STATUS_LOAD_MORE_RETURNING = 4;
-
- private static boolean isRefreshing(final int status) {
- return status == STATUS.STATUS_REFRESHING;
- }
-
- private static boolean isLoadingMore(final int status) {
- return status == STATUS.STATUS_LOADING_MORE;
- }
-
- private static boolean isReleaseToRefresh(final int status) {
- return status == STATUS.STATUS_RELEASE_TO_REFRESH;
- }
-
- private static boolean isReleaseToLoadMore(final int status) {
- return status == STATUS.STATUS_RELEASE_TO_LOAD_MORE;
- }
-
- private static boolean isSwipingToRefresh(final int status) {
- return status == STATUS.STATUS_SWIPING_TO_REFRESH;
- }
-
- private static boolean isSwipingToLoadMore(final int status) {
- return status == STATUS.STATUS_SWIPING_TO_LOAD_MORE;
- }
-
- private static boolean isRefreshStatus(final int status) {
- return status < STATUS.STATUS_DEFAULT;
- }
-
- public static boolean isLoadMoreStatus(final int status) {
- return status > STATUS.STATUS_DEFAULT;
- }
-
- private static boolean isStatusDefault(final int status) {
- return status == STATUS.STATUS_DEFAULT;
- }
-
- private static String getStatus(int status) {
- final String statusInfo;
- switch (status) {
- case STATUS_REFRESH_RETURNING:
- statusInfo = "status_refresh_returning";
- break;
- case STATUS_REFRESHING:
- statusInfo = "status_refreshing";
- break;
- case STATUS_RELEASE_TO_REFRESH:
- statusInfo = "status_release_to_refresh";
- break;
- case STATUS_SWIPING_TO_REFRESH:
- statusInfo = "status_swiping_to_refresh";
- break;
- case STATUS_DEFAULT:
- statusInfo = "status_default";
- break;
- case STATUS_SWIPING_TO_LOAD_MORE:
- statusInfo = "status_swiping_to_load_more";
- break;
- case STATUS_RELEASE_TO_LOAD_MORE:
- statusInfo = "status_release_to_load_more";
- break;
- case STATUS_LOADING_MORE:
- statusInfo = "status_loading_more";
- break;
- case STATUS_LOAD_MORE_RETURNING:
- statusInfo = "status_load_more_returning";
- break;
- default:
- statusInfo = "status_illegal!";
- break;
- }
- return statusInfo;
- }
-
- private static void printStatus(int status) {
- Log.i(TAG, "printStatus:" + getStatus(status));
- }
- }
-}
diff --git a/library/src/main/java/com/aspsine/swipetoloadlayout/SwipeToLoadLayout.java b/library/src/main/java/com/aspsine/swipetoloadlayout/SwipeToLoadLayout.java
index 6bbd878..a2f0c6e 100644
--- a/library/src/main/java/com/aspsine/swipetoloadlayout/SwipeToLoadLayout.java
+++ b/library/src/main/java/com/aspsine/swipetoloadlayout/SwipeToLoadLayout.java
@@ -4,6 +4,7 @@
import android.content.res.TypedArray;
import android.support.v4.view.MotionEventCompat;
import android.support.v4.view.ViewCompat;
+import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
@@ -61,9 +62,9 @@ public class SwipeToLoadLayout extends ViewGroup {
private View mFooterView;
- private int mHeaderHeight;
+ private int mHeaderLength;
- private int mFooterHeight;
+ private int mFooterLength;
private boolean mHasHeaderView;
@@ -236,7 +237,7 @@ public class SwipeToLoadLayout extends ViewGroup {
private int mDefaultToLoadingMoreScrollingDuration = DEFAULT_DEFAULT_TO_LOADING_MORE_SCROLLING_DURATION;
/**
- *true : the target is vertically scroll
+ * true : the target is vertically scroll
*/
private boolean mIsVertically = true;
@@ -318,7 +319,8 @@ public SwipeToLoadLayout(Context context, AttributeSet attrs, int defStyleAttr)
} else if (attr == R.styleable.SwipeToLoadLayout_default_to_loading_more_scrolling_duration) {
setDefaultToLoadingMoreScrollingDuration(a.getInt(attr, DEFAULT_DEFAULT_TO_LOADING_MORE_SCROLLING_DURATION));
-
+ } else if (attr == R.styleable.SwipeToLoadLayout_swipe_vertically) {
+ mIsVertically = a.getBoolean(attr, mIsVertically);
}
}
} finally {
@@ -363,9 +365,9 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
final View headerView = mHeaderView;
measureChildWithMargins(headerView, widthMeasureSpec, 0, heightMeasureSpec, 0);
MarginLayoutParams lp = ((MarginLayoutParams) headerView.getLayoutParams());
- mHeaderHeight = headerView.getMeasuredHeight() + lp.topMargin + lp.bottomMargin;
- if (mRefreshTriggerOffset < mHeaderHeight) {
- mRefreshTriggerOffset = mHeaderHeight;
+ mHeaderLength = mIsVertically ? (headerView.getMeasuredHeight() + lp.topMargin + lp.bottomMargin) : (headerView.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
+ if (mRefreshTriggerOffset < mHeaderLength) {
+ mRefreshTriggerOffset = mHeaderLength;
}
}
// target
@@ -378,16 +380,20 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
final View footerView = mFooterView;
measureChildWithMargins(footerView, widthMeasureSpec, 0, heightMeasureSpec, 0);
MarginLayoutParams lp = ((MarginLayoutParams) footerView.getLayoutParams());
- mFooterHeight = footerView.getMeasuredHeight() + lp.topMargin + lp.bottomMargin;
- if (mLoadMoreTriggerOffset < mFooterHeight) {
- mLoadMoreTriggerOffset = mFooterHeight;
+ mFooterLength = mIsVertically ? (footerView.getMeasuredHeight() + lp.topMargin + lp.bottomMargin) : (footerView.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
+ if (mLoadMoreTriggerOffset < mFooterLength) {
+ mLoadMoreTriggerOffset = mFooterLength;
}
}
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
- layoutChildren();
+ if (mIsVertically) {
+ layoutVerChildren();
+ }else {
+ layoutHorChildren();
+ }
mHasHeaderView = (mHeaderView != null);
mHasFooterView = (mFooterView != null);
@@ -503,8 +509,15 @@ public boolean onInterceptTouchEvent(MotionEvent event) {
}
float y = getMotionEventY(event, mActivePointerId);
float x = getMotionEventX(event, mActivePointerId);
- final float yInitDiff = y - mInitDownY;
- final float xInitDiff = x - mInitDownX;
+ final float yInitDiff;
+ final float xInitDiff;
+ if (mIsVertically) {
+ yInitDiff = y - mInitDownY;
+ xInitDiff = x - mInitDownX;
+ } else {
+ yInitDiff = x - mInitDownX;
+ xInitDiff = y - mInitDownY;
+ }
mLastY = y;
mLastX = x;
boolean moved = Math.abs(yInitDiff) > Math.abs(xInitDiff)
@@ -549,8 +562,15 @@ public boolean onTouchEvent(MotionEvent event) {
final float y = getMotionEventY(event, mActivePointerId);
final float x = getMotionEventX(event, mActivePointerId);
- final float yDiff = y - mLastY;
- final float xDiff = x - mLastX;
+ final float yDiff;
+ final float xDiff;
+ if (mIsVertically) {
+ yDiff = y - mLastY;
+ xDiff = x - mLastX;
+ } else {
+ yDiff = x - mLastX;
+ xDiff = y - mLastY;
+ }
mLastY = y;
mLastX = x;
@@ -743,6 +763,11 @@ public void setSwipeStyle(int style) {
requestLayout();
}
+ public void setVertically(boolean vertically) {
+ mIsVertically = vertically;
+ requestLayout();
+ }
+
/**
* set how hard to drag. bigger easier, smaller harder;
*
@@ -754,9 +779,9 @@ public void setDragRatio(float dragRatio) {
/**
* set the value of {@link #mRefreshTriggerOffset}.
- * Default value is the refresh header view height {@link #mHeaderHeight}
- * If the offset you set is smaller than {@link #mHeaderHeight} or not set,
- * using {@link #mHeaderHeight} as default value
+ * Default value is the refresh header view height {@link #mHeaderLength}
+ * If the offset you set is smaller than {@link #mHeaderLength} or not set,
+ * using {@link #mHeaderLength} as default value
*
* @param offset
*/
@@ -766,9 +791,9 @@ public void setRefreshTriggerOffset(int offset) {
/**
* set the value of {@link #mLoadMoreTriggerOffset}.
- * Default value is the load more footer view height {@link #mFooterHeight}
- * If the offset you set is smaller than {@link #mFooterHeight} or not set,
- * using {@link #mFooterHeight} as default value
+ * Default value is the load more footer view height {@link #mFooterLength}
+ * If the offset you set is smaller than {@link #mFooterLength} or not set,
+ * using {@link #mFooterLength} as default value
*
* @param offset
*/
@@ -969,17 +994,34 @@ public void run() {
* scroll up. Override this if the child view is a custom view.
*/
protected boolean canChildScrollUp() {
- if (android.os.Build.VERSION.SDK_INT < 14) {
+ if (mTargetView instanceof ViewPager) {
+ ViewPager pager = (ViewPager) mTargetView;
+ return pager.getCurrentItem() > 0;
+ } else if (android.os.Build.VERSION.SDK_INT < 14) {
if (mTargetView instanceof AbsListView) {
final AbsListView absListView = (AbsListView) mTargetView;
- return absListView.getChildCount() > 0
- && (absListView.getFirstVisiblePosition() > 0 || absListView.getChildAt(0)
- .getTop() < absListView.getPaddingTop());
+ if (mIsVertically) {
+ return absListView.getChildCount() > 0
+ && (absListView.getFirstVisiblePosition() > 0 || absListView.getChildAt(0)
+ .getTop() < absListView.getPaddingTop());
+ } else {
+ return absListView.getChildCount() > 0
+ && (absListView.getFirstVisiblePosition() > 0 || absListView.getChildAt(0)
+ .getLeft() < absListView.getPaddingLeft());
+ }
} else {
- return ViewCompat.canScrollVertically(mTargetView, -1) || mTargetView.getScrollY() > 0;
+ if (mIsVertically) {
+ return ViewCompat.canScrollVertically(mTargetView, -1) || mTargetView.getScrollY() > 0;
+ } else {
+ return ViewCompat.canScrollHorizontally(mTargetView, -1) || mTargetView.getScrollX() > 0;
+ }
}
} else {
- return ViewCompat.canScrollVertically(mTargetView, -1);
+ if (mIsVertically) {
+ return ViewCompat.canScrollVertically(mTargetView, -1);
+ } else {
+ return ViewCompat.canScrollHorizontally(mTargetView, -1);
+ }
}
}
@@ -990,24 +1032,41 @@ protected boolean canChildScrollUp() {
* @return
*/
protected boolean canChildScrollDown() {
- if (android.os.Build.VERSION.SDK_INT < 14) {
+ if (mTargetView instanceof ViewPager) {
+ ViewPager pager = (ViewPager) mTargetView;
+ return pager.getCurrentItem() < pager.getAdapter().getCount() - 1;
+ } else if (android.os.Build.VERSION.SDK_INT < 14) {
if (mTargetView instanceof AbsListView) {
final AbsListView absListView = (AbsListView) mTargetView;
- return absListView.getChildCount() > 0
- && (absListView.getLastVisiblePosition() < absListView.getChildCount() - 1
- || absListView.getChildAt(absListView.getChildCount() - 1).getBottom() > absListView.getPaddingBottom());
+ if (mIsVertically) {
+ return absListView.getChildCount() > 0
+ && (absListView.getLastVisiblePosition() < absListView.getChildCount() - 1
+ || absListView.getChildAt(absListView.getChildCount() - 1).getBottom() > absListView.getPaddingBottom());
+ } else {
+ return absListView.getChildCount() > 0
+ && (absListView.getLastVisiblePosition() < absListView.getChildCount() - 1
+ || absListView.getChildAt(absListView.getChildCount() - 1).getRight() > absListView.getPaddingRight());
+ }
} else {
- return ViewCompat.canScrollVertically(mTargetView, 1) || mTargetView.getScrollY() < 0;
+ if (mIsVertically) {
+ return ViewCompat.canScrollVertically(mTargetView, 1) || mTargetView.getScrollY() < 0;
+ } else {
+ return ViewCompat.canScrollHorizontally(mTargetView, 1) || mTargetView.getScrollX() < 0;
+ }
}
} else {
- return ViewCompat.canScrollVertically(mTargetView, 1);
+ if (mIsVertically) {
+ return ViewCompat.canScrollVertically(mTargetView, 1);
+ } else {
+ return ViewCompat.canScrollHorizontally(mTargetView, 1);
+ }
}
}
/**
* @see #onLayout(boolean, int, int, int, int)
*/
- private void layoutChildren() {
+ private void layoutVerChildren() {
final int width = getMeasuredWidth();
final int height = getMeasuredHeight();
@@ -1029,11 +1088,11 @@ private void layoutChildren() {
switch (mStyle) {
case STYLE.CLASSIC:
// classic
- headerTop = paddingTop + lp.topMargin - mHeaderHeight + mHeaderOffset;
+ headerTop = paddingTop + lp.topMargin - mHeaderLength + mHeaderOffset;
break;
case STYLE.ABOVE:
// classic
- headerTop = paddingTop + lp.topMargin - mHeaderHeight + mHeaderOffset;
+ headerTop = paddingTop + lp.topMargin - mHeaderLength + mHeaderOffset;
break;
case STYLE.BLEW:
// blew
@@ -1041,11 +1100,11 @@ private void layoutChildren() {
break;
case STYLE.SCALE:
// scale
- headerTop = paddingTop + lp.topMargin - mHeaderHeight / 2 + mHeaderOffset / 2;
+ headerTop = paddingTop + lp.topMargin - mHeaderLength / 2 + mHeaderOffset / 2;
break;
default:
// classic
- headerTop = paddingTop + lp.topMargin - mHeaderHeight + mHeaderOffset;
+ headerTop = paddingTop + lp.topMargin - mHeaderLength + mHeaderOffset;
break;
}
final int headerRight = headerLeft + headerView.getMeasuredWidth();
@@ -1097,11 +1156,11 @@ private void layoutChildren() {
switch (mStyle) {
case STYLE.CLASSIC:
// classic
- footerBottom = height - paddingBottom - lp.bottomMargin + mFooterHeight + mFooterOffset;
+ footerBottom = height - paddingBottom - lp.bottomMargin + mFooterLength + mFooterOffset;
break;
case STYLE.ABOVE:
// classic
- footerBottom = height - paddingBottom - lp.bottomMargin + mFooterHeight + mFooterOffset;
+ footerBottom = height - paddingBottom - lp.bottomMargin + mFooterLength + mFooterOffset;
break;
case STYLE.BLEW:
// blew
@@ -1109,11 +1168,11 @@ private void layoutChildren() {
break;
case STYLE.SCALE:
// scale
- footerBottom = height - paddingBottom - lp.bottomMargin + mFooterHeight / 2 + mFooterOffset / 2;
+ footerBottom = height - paddingBottom - lp.bottomMargin + mFooterLength / 2 + mFooterOffset / 2;
break;
default:
// classic
- footerBottom = height - paddingBottom - lp.bottomMargin + mFooterHeight + mFooterOffset;
+ footerBottom = height - paddingBottom - lp.bottomMargin + mFooterLength + mFooterOffset;
break;
}
final int footerTop = footerBottom - footerView.getMeasuredHeight();
@@ -1137,24 +1196,169 @@ private void layoutChildren() {
}
}
+ /**
+ * @see #onLayout(boolean, int, int, int, int)
+ */
+ private void layoutHorChildren() {
+ final int width = getMeasuredWidth();
+ final int height = getMeasuredHeight();
+
+ final int paddingLeft = getPaddingLeft();
+ final int paddingTop = getPaddingTop();
+ final int paddingRight = getPaddingRight();
+ final int paddingBottom = getPaddingBottom();
+
+ if (mTargetView == null) {
+ return;
+ }
+
+ // layout header
+ if (mHeaderView != null) {
+ final View headerView = mHeaderView;
+ MarginLayoutParams lp = (MarginLayoutParams) headerView.getLayoutParams();
+ final int headerLeft;
+ final int headerTop = paddingTop + lp.topMargin;
+ switch (mStyle) {
+ case STYLE.CLASSIC:
+ // classic
+ headerLeft = paddingLeft + lp.leftMargin - mHeaderLength + mHeaderOffset;
+ break;
+ case STYLE.ABOVE:
+ // classic
+ headerLeft = paddingLeft + lp.leftMargin - mHeaderLength + mHeaderOffset;
+ break;
+ case STYLE.BLEW:
+ // blew
+ headerLeft = paddingLeft + lp.leftMargin;
+ break;
+ case STYLE.SCALE:
+ // scale
+ headerLeft = paddingLeft + lp.leftMargin - mHeaderLength / 2 + mHeaderOffset / 2;
+ break;
+ default:
+ // classic
+ headerLeft = paddingLeft + lp.leftMargin - mHeaderLength + mHeaderOffset;
+ break;
+ }
+ final int headerRight = headerLeft + headerView.getMeasuredWidth();
+ final int headerBottom = headerTop + headerView.getMeasuredHeight();
+ headerView.layout(headerLeft, headerTop, headerRight, headerBottom);
+ }
+
+
+ // layout target
+ if (mTargetView != null) {
+ final View targetView = mTargetView;
+ MarginLayoutParams lp = (MarginLayoutParams) targetView.getLayoutParams();
+ final int targetLeft;
+ final int targetTop = paddingTop + lp.topMargin;
+
+ switch (mStyle) {
+ case STYLE.CLASSIC:
+ // classic
+ targetLeft = paddingLeft + lp.leftMargin + mTargetOffset;
+ break;
+ case STYLE.ABOVE:
+ // above
+ targetLeft = paddingLeft + lp.leftMargin;
+ break;
+ case STYLE.BLEW:
+ // classic
+ targetLeft = paddingLeft + lp.leftMargin + mTargetOffset;
+ break;
+ case STYLE.SCALE:
+ // classic
+ targetLeft = paddingLeft + lp.leftMargin + mTargetOffset;
+ break;
+ default:
+ // classic
+ targetLeft = paddingLeft + lp.leftMargin + mTargetOffset;
+ break;
+ }
+ final int targetRight = targetLeft + targetView.getMeasuredWidth();
+ final int targetBottom = targetTop + targetView.getMeasuredHeight();
+ targetView.layout(targetLeft, targetTop, targetRight, targetBottom);
+ }
+
+ // layout footer
+ if (mFooterView != null) {
+ final View footerView = mFooterView;
+ MarginLayoutParams lp = (MarginLayoutParams) footerView.getLayoutParams();
+ final int footerRight;
+ final int footerTop = paddingTop + lp.topMargin;
+ switch (mStyle) {
+ case STYLE.CLASSIC:
+ // classic
+ footerRight = width - paddingRight - lp.rightMargin + mHeaderLength + mFooterOffset;
+ break;
+ case STYLE.ABOVE:
+ // classic
+ footerRight = width - paddingRight - lp.rightMargin + mHeaderLength + mFooterOffset;
+ break;
+ case STYLE.BLEW:
+ // blew
+ footerRight = width - paddingRight - lp.rightMargin;
+ break;
+ case STYLE.SCALE:
+ // scale
+ footerRight = width - paddingRight - lp.rightMargin + mHeaderLength / 2 + mFooterOffset / 2;
+ break;
+ default:
+ // classic
+ footerRight = width - paddingRight - lp.rightMargin + mHeaderLength + mFooterOffset;
+ break;
+ }
+ final int footerLeft = footerRight - footerView.getMeasuredWidth();
+ final int footerBottom = footerTop + footerView.getMeasuredHeight();
+
+ footerView.layout(footerLeft, footerTop, footerRight, footerBottom);
+ }
+
+ if (mStyle == STYLE.CLASSIC
+ || mStyle == STYLE.ABOVE) {
+ if (mHeaderView != null) {
+ mHeaderView.bringToFront();
+ }
+ if (mFooterView != null) {
+ mFooterView.bringToFront();
+ }
+ } else if (mStyle == STYLE.BLEW || mStyle == STYLE.SCALE) {
+ if (mTargetView != null) {
+ mTargetView.bringToFront();
+ }
+ }
+ }
+
private void fixCurrentStatusLayout() {
if (STATUS.isRefreshing(mStatus)) {
mTargetOffset = (int) (mRefreshTriggerOffset + 0.5f);
mHeaderOffset = mTargetOffset;
mFooterOffset = 0;
- layoutChildren();
+ if (mIsVertically) {
+ layoutVerChildren();
+ }else {
+ layoutHorChildren();
+ }
invalidate();
} else if (STATUS.isStatusDefault(mStatus)) {
mTargetOffset = 0;
mHeaderOffset = 0;
mFooterOffset = 0;
- layoutChildren();
+ if (mIsVertically) {
+ layoutVerChildren();
+ }else {
+ layoutHorChildren();
+ }
invalidate();
} else if (STATUS.isLoadingMore(mStatus)) {
mTargetOffset = -(int) (mLoadMoreTriggerOffset + 0.5f);
mHeaderOffset = 0;
mFooterOffset = mTargetOffset;
- layoutChildren();
+ if (mIsVertically) {
+ layoutVerChildren();
+ }else {
+ layoutHorChildren();
+ }
invalidate();
}
}
@@ -1236,7 +1440,11 @@ private void updateScroll(final float yScrolled) {
if (mDebug) {
Log.i(TAG, "mTargetOffset = " + mTargetOffset);
}
- layoutChildren();
+ if (mIsVertically) {
+ layoutVerChildren();
+ }else {
+ layoutHorChildren();
+ }
invalidate();
}
@@ -1298,11 +1506,11 @@ private void scrollSwipingToLoadMoreToDefault() {
}
private void scrollReleaseToRefreshToRefreshing() {
- mAutoScroller.autoScroll(mHeaderHeight - mHeaderOffset, mReleaseToRefreshToRefreshingScrollingDuration);
+ mAutoScroller.autoScroll(mHeaderLength - mHeaderOffset, mReleaseToRefreshToRefreshingScrollingDuration);
}
private void scrollReleaseToLoadMoreToLoadingMore() {
- mAutoScroller.autoScroll(-mFooterOffset - mFooterHeight, mReleaseToLoadMoreToLoadingMoreScrollingDuration);
+ mAutoScroller.autoScroll(-mFooterOffset - mFooterLength, mReleaseToLoadMoreToLoadingMoreScrollingDuration);
}
private void scrollRefreshingToDefault() {
@@ -1531,7 +1739,7 @@ private class AutoScroller implements Runnable {
private Scroller mScroller;
- private int mmLastY;
+ private int mmLastPoint;
private boolean mRunning = false;
@@ -1544,13 +1752,13 @@ public AutoScroller() {
@Override
public void run() {
boolean finish = !mScroller.computeScrollOffset() || mScroller.isFinished();
- int currY = mScroller.getCurrY();
- int yDiff = currY - mmLastY;
+ int currPoint = mIsVertically ? mScroller.getCurrY() : mScroller.getCurrX();
+ int diff = currPoint - mmLastPoint;
if (finish) {
finish();
} else {
- mmLastY = currY;
- SwipeToLoadLayout.this.autoScroll(yDiff);
+ mmLastPoint = currPoint;
+ SwipeToLoadLayout.this.autoScroll(diff);
post(this);
}
}
@@ -1559,7 +1767,7 @@ public void run() {
* remove the post callbacks and reset default values
*/
private void finish() {
- mmLastY = 0;
+ mmLastPoint = 0;
mRunning = false;
removeCallbacks(this);
// if abort by user, don't call
@@ -1587,16 +1795,20 @@ public void abortIfRunning() {
* It's just like the yScrolled param in the
* {@link #updateScroll(float yScrolled)}
*
- * @param yScrolled
+ * @param scrolled
* @param duration
*/
- private void autoScroll(int yScrolled, int duration) {
+ private void autoScroll(int scrolled, int duration) {
removeCallbacks(this);
- mmLastY = 0;
+ mmLastPoint = 0;
if (!mScroller.isFinished()) {
mScroller.forceFinished(true);
}
- mScroller.startScroll(0, 0, 0, yScrolled, duration);
+ if (mIsVertically) {
+ mScroller.startScroll(0, 0, 0, scrolled, duration);
+ } else {
+ mScroller.startScroll(0, 0, scrolled, 0, duration);
+ }
post(this);
mRunning = true;
}
diff --git a/library/src/main/res/values/attrs.xml b/library/src/main/res/values/attrs.xml
index ec603d5..82153b2 100644
--- a/library/src/main/res/values/attrs.xml
+++ b/library/src/main/res/values/attrs.xml
@@ -27,7 +27,7 @@
-
+
\ No newline at end of file
From ce94b94efe391219e7bedd8c134f8a3dbdf4596c Mon Sep 17 00:00:00 2001
From: wxj <986798656@qq.com>
Date: Tue, 7 Feb 2017 15:14:14 +0800
Subject: [PATCH 4/6] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=AF=B9VierPager?=
=?UTF-8?q?=E7=9A=84=E6=94=AF=E6=8C=81=EF=BC=8C=E5=8A=A0=E5=85=A5=E4=B8=A4?=
=?UTF-8?q?=E4=B8=AA=E5=AF=B9=E5=BA=94=E7=9A=84=E6=A8=AA=E5=90=91=E6=BB=91?=
=?UTF-8?q?=E5=8A=A8recyclerView=E7=9A=84demo=E5=92=8Cviewpager=E7=9A=84de?=
=?UTF-8?q?mo?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
app/build.gradle | 4 +
app/src/main/AndroidManifest.xml | 3 +
.../swipetoloadlayout/demo/MainActivity.java | 100 ++-----
.../demo/OnRecyclerClickListener.java | 12 +
.../demo/RecyclerActivity.java | 68 +++++
.../swipetoloadlayout/demo/VerActivity.java | 90 +++++++
.../demo/ViewPagerActivity.java | 75 ++++++
.../demo/adapter/RecyclerAdapter.java | 65 +++++
.../demo/adapter/ViewPagerAdapter.java | 96 +++++++
.../demo/view/VerticalTextView2.java | 244 ++++++++++++++++++
.../demo/view/footer/WRefreshFooterView.java | 96 +++++++
.../demo/view/header/WRefreshHeaderView.java | 132 ++++++++++
app/src/main/res/drawable/refresh_loading.xml | 42 +++
app/src/main/res/layout/activity_main.xml | 43 +--
app/src/main/res/layout/activity_recycler.xml | 10 +
app/src/main/res/layout/activity_ver.xml | 24 ++
.../main/res/layout/activity_viewpager.xml | 26 ++
app/src/main/res/layout/item_pager_text.xml | 14 +
app/src/main/res/layout/item_text.xml | 21 ++
.../res/layout/layout_refresh_footer_view.xml | 26 ++
.../res/layout/layout_refresh_header_view.xml | 45 ++++
.../res/layout/recycler_view_head_foot.xml | 26 ++
app/src/main/res/menu/menu_main.xml | 2 +-
.../res/mipmap-hdpi/refresh_head_arrow.png | Bin 0 -> 568 bytes
.../res/mipmap-hdpi/refresh_loading01.png | Bin 0 -> 1335 bytes
.../res/mipmap-hdpi/refresh_loading02.png | Bin 0 -> 1305 bytes
.../res/mipmap-hdpi/refresh_loading03.png | Bin 0 -> 1282 bytes
.../res/mipmap-hdpi/refresh_loading04.png | Bin 0 -> 1316 bytes
.../res/mipmap-hdpi/refresh_loading05.png | Bin 0 -> 1327 bytes
.../res/mipmap-hdpi/refresh_loading06.png | Bin 0 -> 1294 bytes
.../res/mipmap-hdpi/refresh_loading07.png | Bin 0 -> 1280 bytes
.../res/mipmap-hdpi/refresh_loading08.png | Bin 0 -> 1293 bytes
.../res/mipmap-hdpi/refresh_loading09.png | Bin 0 -> 1296 bytes
.../res/mipmap-hdpi/refresh_loading10.png | Bin 0 -> 1277 bytes
.../res/mipmap-hdpi/refresh_loading11.png | Bin 0 -> 1303 bytes
.../res/mipmap-hdpi/refresh_loading12.png | Bin 0 -> 1309 bytes
.../main/res/mipmap-xhdpi/refresh_success.png | Bin 0 -> 770 bytes
app/src/main/res/values/attrs.xml | 12 +
app/src/main/res/values/dimens.xml | 4 +
39 files changed, 1188 insertions(+), 92 deletions(-)
create mode 100644 app/src/main/java/com/aspsine/swipetoloadlayout/demo/OnRecyclerClickListener.java
create mode 100644 app/src/main/java/com/aspsine/swipetoloadlayout/demo/RecyclerActivity.java
create mode 100644 app/src/main/java/com/aspsine/swipetoloadlayout/demo/VerActivity.java
create mode 100644 app/src/main/java/com/aspsine/swipetoloadlayout/demo/ViewPagerActivity.java
create mode 100644 app/src/main/java/com/aspsine/swipetoloadlayout/demo/adapter/RecyclerAdapter.java
create mode 100644 app/src/main/java/com/aspsine/swipetoloadlayout/demo/adapter/ViewPagerAdapter.java
create mode 100644 app/src/main/java/com/aspsine/swipetoloadlayout/demo/view/VerticalTextView2.java
create mode 100644 app/src/main/java/com/aspsine/swipetoloadlayout/demo/view/footer/WRefreshFooterView.java
create mode 100644 app/src/main/java/com/aspsine/swipetoloadlayout/demo/view/header/WRefreshHeaderView.java
create mode 100644 app/src/main/res/drawable/refresh_loading.xml
create mode 100644 app/src/main/res/layout/activity_recycler.xml
create mode 100644 app/src/main/res/layout/activity_ver.xml
create mode 100644 app/src/main/res/layout/activity_viewpager.xml
create mode 100644 app/src/main/res/layout/item_pager_text.xml
create mode 100644 app/src/main/res/layout/item_text.xml
create mode 100644 app/src/main/res/layout/layout_refresh_footer_view.xml
create mode 100644 app/src/main/res/layout/layout_refresh_header_view.xml
create mode 100644 app/src/main/res/layout/recycler_view_head_foot.xml
create mode 100644 app/src/main/res/mipmap-hdpi/refresh_head_arrow.png
create mode 100644 app/src/main/res/mipmap-hdpi/refresh_loading01.png
create mode 100644 app/src/main/res/mipmap-hdpi/refresh_loading02.png
create mode 100644 app/src/main/res/mipmap-hdpi/refresh_loading03.png
create mode 100644 app/src/main/res/mipmap-hdpi/refresh_loading04.png
create mode 100644 app/src/main/res/mipmap-hdpi/refresh_loading05.png
create mode 100644 app/src/main/res/mipmap-hdpi/refresh_loading06.png
create mode 100644 app/src/main/res/mipmap-hdpi/refresh_loading07.png
create mode 100644 app/src/main/res/mipmap-hdpi/refresh_loading08.png
create mode 100644 app/src/main/res/mipmap-hdpi/refresh_loading09.png
create mode 100644 app/src/main/res/mipmap-hdpi/refresh_loading10.png
create mode 100644 app/src/main/res/mipmap-hdpi/refresh_loading11.png
create mode 100644 app/src/main/res/mipmap-hdpi/refresh_loading12.png
create mode 100644 app/src/main/res/mipmap-xhdpi/refresh_success.png
diff --git a/app/build.gradle b/app/build.gradle
index cf2fb9f..ffd7cbb 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -35,4 +35,8 @@ dependencies {
compile 'com.squareup.picasso:picasso:2.5.2'
compile 'com.lsjwzh:materialloadingprogressbar:0.5.8-RELEASE'
compile 'com.github.Aspsine:FragmentNavigator:1.0.2'
+
+ compile 'com.android.support:cardview-v7:25.1.0'
+ compile 'com.jakewharton:butterknife:8.4.0'
+ annotationProcessor 'com.jakewharton:butterknife-compiler:8.4.0'
}
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index dec8147..24115e6 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -21,6 +21,9 @@
+
+
+
diff --git a/app/src/main/java/com/aspsine/swipetoloadlayout/demo/MainActivity.java b/app/src/main/java/com/aspsine/swipetoloadlayout/demo/MainActivity.java
index 5a860b8..44d2afa 100644
--- a/app/src/main/java/com/aspsine/swipetoloadlayout/demo/MainActivity.java
+++ b/app/src/main/java/com/aspsine/swipetoloadlayout/demo/MainActivity.java
@@ -1,90 +1,42 @@
package com.aspsine.swipetoloadlayout.demo;
-
import android.content.Intent;
import android.os.Bundle;
-import android.support.design.widget.NavigationView;
-import android.support.v4.view.GravityCompat;
-import android.support.v4.widget.DrawerLayout;
+import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
-import android.view.MenuItem;
-
-import com.aspsine.fragmentnavigator.FragmentNavigator;
-import com.aspsine.swipetoloadlayout.demo.fragment.BaseToolbarFragment;
-
-import java.util.Arrays;
-import java.util.List;
-
-public class MainActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener
- , BaseToolbarFragment.ToggleDrawerCallBack {
-
- private static final Integer ID_ARRAY[] = {
- R.id.nav_Twitter_style,
- R.id.nav_google_style,
- R.id.nav_yalantis_style,
- R.id.nav_jd_style,
- R.id.nav_set_header_footer_via_code
- };
-
- private static final List IDS = Arrays.asList(ID_ARRAY);
+import android.view.View;
- private static final int DEFAULT_POSITION = 0;
+import butterknife.ButterKnife;
+import butterknife.OnClick;
- private DrawerLayout drawerLayout;
+/**
+ * Created by wang
+ * on 2017/1/22
+ */
- /**
- * https://github.com/Aspsine/FragmentNavigator
- */
- private FragmentNavigator mFragmentNavigator;
+public class MainActivity extends AppCompatActivity {
@Override
- protected void onCreate(Bundle savedInstanceState) {
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
-
- mFragmentNavigator = new FragmentNavigator(getSupportFragmentManager(), new MainFragmentAdapter(), R.id.container);
-
- mFragmentNavigator.setDefaultPosition(DEFAULT_POSITION);
-
- mFragmentNavigator.onCreate(savedInstanceState);
-
setContentView(R.layout.activity_main);
-
- drawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
-
- NavigationView navigationView = (NavigationView) findViewById(R.id.navigationView);
-
- navigationView.setNavigationItemSelectedListener(this);
-
- navigationView.setCheckedItem(IDS.get(DEFAULT_POSITION));
-
- mFragmentNavigator.showFragment(mFragmentNavigator.getCurrentPosition());
+ ButterKnife.bind(this);
}
- @Override
- protected void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- mFragmentNavigator.onSaveInstanceState(outState);
- }
-
- @Override
- public void openDrawer() {
- drawerLayout.openDrawer(GravityCompat.START);
- }
-
- @Override
- public boolean onNavigationItemSelected(final MenuItem menuItem) {
- drawerLayout.closeDrawer(GravityCompat.START);
- drawerLayout.postDelayed(new Runnable() {
- @Override
- public void run() {
- int itemId = menuItem.getItemId();
- if (itemId == R.id.nav_about) {
- startActivity(new Intent(MainActivity.this, AboutActivity.class));
- } else {
- mFragmentNavigator.showFragment(IDS.indexOf(itemId));
- }
- }
- }, 200);
- return true;
+ @OnClick({R.id.recycler_btn, R.id.pager_btn, R.id.ver_btn})
+ public void onClick(View view) {
+ Intent intent = new Intent();
+ switch (view.getId()) {
+ case R.id.ver_btn:
+ intent.setClass(this, VerActivity.class);
+ break;
+ case R.id.recycler_btn:
+ intent.setClass(this, RecyclerActivity.class);
+ break;
+ case R.id.pager_btn:
+ intent.setClass(this, ViewPagerActivity.class);
+ break;
+ }
+ startActivity(intent);
}
}
diff --git a/app/src/main/java/com/aspsine/swipetoloadlayout/demo/OnRecyclerClickListener.java b/app/src/main/java/com/aspsine/swipetoloadlayout/demo/OnRecyclerClickListener.java
new file mode 100644
index 0000000..efab5b6
--- /dev/null
+++ b/app/src/main/java/com/aspsine/swipetoloadlayout/demo/OnRecyclerClickListener.java
@@ -0,0 +1,12 @@
+package com.aspsine.swipetoloadlayout.demo;
+
+/**
+ * Created by wang
+ * on 2017/1/22
+ */
+
+public interface OnRecyclerClickListener {
+
+ void onClick(int position);
+
+}
diff --git a/app/src/main/java/com/aspsine/swipetoloadlayout/demo/RecyclerActivity.java b/app/src/main/java/com/aspsine/swipetoloadlayout/demo/RecyclerActivity.java
new file mode 100644
index 0000000..6054bfd
--- /dev/null
+++ b/app/src/main/java/com/aspsine/swipetoloadlayout/demo/RecyclerActivity.java
@@ -0,0 +1,68 @@
+package com.aspsine.swipetoloadlayout.demo;
+
+import android.os.Bundle;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.widget.Toast;
+
+import com.aspsine.swipetoloadlayout.OnLoadMoreListener;
+import com.aspsine.swipetoloadlayout.OnRefreshListener;
+import com.aspsine.swipetoloadlayout.SwipeToLoadLayout;
+import com.aspsine.swipetoloadlayout.demo.adapter.RecyclerAdapter;
+
+import java.util.ArrayList;
+
+import butterknife.BindView;
+import butterknife.ButterKnife;
+
+public class RecyclerActivity extends AppCompatActivity implements OnRefreshListener, OnLoadMoreListener, OnRecyclerClickListener {
+
+ @BindView(R.id.swipe_target)
+ RecyclerView mRecyclerView;
+ @BindView(R.id.load_layout)
+ SwipeToLoadLayout mLoadLayout;
+
+ private ArrayList mNames;
+ private int mName;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_recycler);
+ ButterKnife.bind(this);
+ mLoadLayout.setOnRefreshListener(this);
+ mLoadLayout.setOnLoadMoreListener(this);
+ mNames = new ArrayList<>();
+ mRecyclerView.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false));
+ mRecyclerView.setAdapter(new RecyclerAdapter(mNames, this));
+ onRefresh();
+ }
+
+ @Override
+ public void onRefresh() {
+ mNames.clear();
+ mName = 0;
+ for (int i = 0; i < 3; i++) {
+ mNames.add("name: " + mName);
+ mName ++;
+ }
+ mRecyclerView.getAdapter().notifyDataSetChanged();
+ mLoadLayout.setRefreshing(false);
+ }
+
+ @Override
+ public void onLoadMore() {
+ for (int i = 0; i < 3; i++) {
+ mNames.add("name: " + mName);
+ mName ++;
+ }
+ mRecyclerView.getAdapter().notifyDataSetChanged();
+ mLoadLayout.setLoadingMore(false);
+ }
+
+ @Override
+ public void onClick(int position) {
+ Toast.makeText(this, mNames.get(position), Toast.LENGTH_SHORT).show();
+ }
+}
diff --git a/app/src/main/java/com/aspsine/swipetoloadlayout/demo/VerActivity.java b/app/src/main/java/com/aspsine/swipetoloadlayout/demo/VerActivity.java
new file mode 100644
index 0000000..a0fdcca
--- /dev/null
+++ b/app/src/main/java/com/aspsine/swipetoloadlayout/demo/VerActivity.java
@@ -0,0 +1,90 @@
+package com.aspsine.swipetoloadlayout.demo;
+
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.design.widget.NavigationView;
+import android.support.v4.view.GravityCompat;
+import android.support.v4.widget.DrawerLayout;
+import android.support.v7.app.AppCompatActivity;
+import android.view.MenuItem;
+
+import com.aspsine.fragmentnavigator.FragmentNavigator;
+import com.aspsine.swipetoloadlayout.demo.fragment.BaseToolbarFragment;
+
+import java.util.Arrays;
+import java.util.List;
+
+public class VerActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener
+ , BaseToolbarFragment.ToggleDrawerCallBack {
+
+ private static final Integer ID_ARRAY[] = {
+ R.id.nav_Twitter_style,
+ R.id.nav_google_style,
+ R.id.nav_yalantis_style,
+ R.id.nav_jd_style,
+ R.id.nav_set_header_footer_via_code
+ };
+
+ private static final List IDS = Arrays.asList(ID_ARRAY);
+
+ private static final int DEFAULT_POSITION = 0;
+
+ private DrawerLayout drawerLayout;
+
+ /**
+ * https://github.com/Aspsine/FragmentNavigator
+ */
+ private FragmentNavigator mFragmentNavigator;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ mFragmentNavigator = new FragmentNavigator(getSupportFragmentManager(), new MainFragmentAdapter(), R.id.container);
+
+ mFragmentNavigator.setDefaultPosition(DEFAULT_POSITION);
+
+ mFragmentNavigator.onCreate(savedInstanceState);
+
+ setContentView(R.layout.activity_ver);
+
+ drawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
+
+ NavigationView navigationView = (NavigationView) findViewById(R.id.navigationView);
+
+ navigationView.setNavigationItemSelectedListener(this);
+
+ navigationView.setCheckedItem(IDS.get(DEFAULT_POSITION));
+
+ mFragmentNavigator.showFragment(mFragmentNavigator.getCurrentPosition());
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ mFragmentNavigator.onSaveInstanceState(outState);
+ }
+
+ @Override
+ public void openDrawer() {
+ drawerLayout.openDrawer(GravityCompat.START);
+ }
+
+ @Override
+ public boolean onNavigationItemSelected(final MenuItem menuItem) {
+ drawerLayout.closeDrawer(GravityCompat.START);
+ drawerLayout.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ int itemId = menuItem.getItemId();
+ if (itemId == R.id.nav_about) {
+ startActivity(new Intent(VerActivity.this, AboutActivity.class));
+ } else {
+ mFragmentNavigator.showFragment(IDS.indexOf(itemId));
+ }
+ }
+ }, 200);
+ return true;
+ }
+}
diff --git a/app/src/main/java/com/aspsine/swipetoloadlayout/demo/ViewPagerActivity.java b/app/src/main/java/com/aspsine/swipetoloadlayout/demo/ViewPagerActivity.java
new file mode 100644
index 0000000..65fb288
--- /dev/null
+++ b/app/src/main/java/com/aspsine/swipetoloadlayout/demo/ViewPagerActivity.java
@@ -0,0 +1,75 @@
+package com.aspsine.swipetoloadlayout.demo;
+
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.support.v4.view.ViewPager;
+import android.support.v7.app.AppCompatActivity;
+import android.widget.Toast;
+
+import com.aspsine.swipetoloadlayout.OnLoadMoreListener;
+import com.aspsine.swipetoloadlayout.OnRefreshListener;
+import com.aspsine.swipetoloadlayout.SwipeToLoadLayout;
+import com.aspsine.swipetoloadlayout.demo.adapter.ViewPagerAdapter;
+
+import java.util.ArrayList;
+
+import butterknife.BindView;
+import butterknife.ButterKnife;
+
+/**
+ * Created by wang
+ * on 2017/1/22
+ */
+
+public class ViewPagerActivity extends AppCompatActivity implements OnRefreshListener, OnLoadMoreListener, OnRecyclerClickListener {
+
+ @BindView(R.id.swipe_target)
+ ViewPager mViewPager;
+ @BindView(R.id.load_layout)
+ SwipeToLoadLayout mLoadLayout;
+
+ private ArrayList mNames;
+
+ private int mName;
+
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_viewpager);
+ ButterKnife.bind(this);
+ mLoadLayout.setOnRefreshListener(this);
+ mLoadLayout.setOnLoadMoreListener(this);
+ mNames = new ArrayList<>();
+ mViewPager.setAdapter(new ViewPagerAdapter(mNames, this));
+ onRefresh();
+ }
+
+ @Override
+ public void onRefresh() {
+ mNames.clear();
+ mName = 0;
+ for (int i = 0; i < 3; i++) {
+ mNames.add("name: " + mName);
+ mName ++;
+ }
+ mViewPager.getAdapter().notifyDataSetChanged();
+ mLoadLayout.setRefreshing(false);
+ }
+
+ @Override
+ public void onLoadMore() {
+ for (int i = 0; i < 3; i++) {
+ mNames.add("name: " + mName);
+ mName ++;
+ }
+ mViewPager.getAdapter().notifyDataSetChanged();
+ mLoadLayout.setLoadingMore(false);
+ }
+
+ @Override
+ public void onClick(int position) {
+ Toast.makeText(this, mNames.get(position), Toast.LENGTH_SHORT).show();
+ }
+
+
+}
diff --git a/app/src/main/java/com/aspsine/swipetoloadlayout/demo/adapter/RecyclerAdapter.java b/app/src/main/java/com/aspsine/swipetoloadlayout/demo/adapter/RecyclerAdapter.java
new file mode 100644
index 0000000..b28d8da
--- /dev/null
+++ b/app/src/main/java/com/aspsine/swipetoloadlayout/demo/adapter/RecyclerAdapter.java
@@ -0,0 +1,65 @@
+package com.aspsine.swipetoloadlayout.demo.adapter;
+
+import android.support.v7.widget.AppCompatTextView;
+import android.support.v7.widget.RecyclerView;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.aspsine.swipetoloadlayout.demo.OnRecyclerClickListener;
+import com.aspsine.swipetoloadlayout.demo.R;
+
+import java.util.List;
+
+import butterknife.BindView;
+import butterknife.ButterKnife;
+
+/**
+ * Created by wang
+ * on 2017/1/22
+ */
+
+public class RecyclerAdapter extends RecyclerView.Adapter {
+
+ private List mNames;
+
+ private OnRecyclerClickListener mListener;
+
+ public RecyclerAdapter(List names, OnRecyclerClickListener listener) {
+ mNames = names;
+ mListener = listener;
+ }
+
+ @Override
+ public TextViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_text, parent, false);
+ return new TextViewHolder(itemView);
+ }
+
+ @Override
+ public void onBindViewHolder(TextViewHolder holder, int position) {
+ holder.mNameTv.setText(mNames.get(position));
+ }
+
+ @Override
+ public int getItemCount() {
+ return mNames.size();
+ }
+
+ class TextViewHolder extends RecyclerView.ViewHolder {
+
+ @BindView(R.id.name_tv)
+ AppCompatTextView mNameTv;
+
+ public TextViewHolder(View itemView) {
+ super(itemView);
+ ButterKnife.bind(this, itemView);
+ itemView.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ mListener.onClick(getAdapterPosition());
+ }
+ });
+ }
+ }
+}
diff --git a/app/src/main/java/com/aspsine/swipetoloadlayout/demo/adapter/ViewPagerAdapter.java b/app/src/main/java/com/aspsine/swipetoloadlayout/demo/adapter/ViewPagerAdapter.java
new file mode 100644
index 0000000..69604f8
--- /dev/null
+++ b/app/src/main/java/com/aspsine/swipetoloadlayout/demo/adapter/ViewPagerAdapter.java
@@ -0,0 +1,96 @@
+package com.aspsine.swipetoloadlayout.demo.adapter;
+
+import android.support.v4.view.PagerAdapter;
+import android.support.v7.widget.AppCompatTextView;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.aspsine.swipetoloadlayout.demo.OnRecyclerClickListener;
+import com.aspsine.swipetoloadlayout.demo.R;
+
+import java.util.List;
+
+import butterknife.BindView;
+import butterknife.ButterKnife;
+import butterknife.OnClick;
+
+/**
+ * Created by wang
+ * on 2017/1/22
+ */
+
+public class ViewPagerAdapter extends PagerAdapter {
+
+ private List mName;
+ private OnRecyclerClickListener mListener;
+
+ public ViewPagerAdapter(List name, OnRecyclerClickListener listener) {
+ mName = name;
+ mListener = listener;
+ }
+
+ public ViewPagerAdapter(List name) {
+ mName = name;
+ }
+
+ public List getName() {
+ return mName;
+ }
+
+ @Override
+ public int getCount() {
+ return mName.size();
+ }
+
+ @Override
+ public boolean isViewFromObject(View view, Object object) {
+ return view == ((ViewHolder) object).itemView;
+ }
+
+ @Override
+ public Object instantiateItem(ViewGroup container, int position) {
+ View convertView = LayoutInflater.from(container.getContext()).inflate(R.layout.item_pager_text, container, false);
+ container.addView(convertView);
+ return new ViewHolder(convertView, position);
+ }
+
+ @Override
+ public void destroyItem(ViewGroup container, int position, Object object) {
+ container.removeView(((ViewHolder) object).itemView);
+ }
+
+ @Override
+ public int getItemPosition(Object object) {
+ return POSITION_NONE;
+ }
+
+
+ class ViewHolder {
+
+ @BindView(R.id.name_tv)
+ AppCompatTextView mNameTv;
+
+ View itemView;
+
+ private int mPosition;
+
+ public ViewHolder(View view, int position) {
+ itemView = view;
+ mPosition = position;
+ ButterKnife.bind(this, view);
+ onBindView(mName.get(position));
+ }
+
+ @OnClick(R.id.name_tv)
+ public void onClick() {
+ if (mListener != null) {
+ mListener.onClick(mPosition);
+ }
+ }
+
+ private void onBindView(String name) {
+ mNameTv.setText(name);
+ }
+ }
+}
diff --git a/app/src/main/java/com/aspsine/swipetoloadlayout/demo/view/VerticalTextView2.java b/app/src/main/java/com/aspsine/swipetoloadlayout/demo/view/VerticalTextView2.java
new file mode 100644
index 0000000..3ae693f
--- /dev/null
+++ b/app/src/main/java/com/aspsine/swipetoloadlayout/demo/view/VerticalTextView2.java
@@ -0,0 +1,244 @@
+package com.aspsine.swipetoloadlayout.demo.view;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.util.AttributeSet;
+import android.view.View;
+
+import com.aspsine.swipetoloadlayout.demo.R;
+
+/**
+ * Created on 2016/10/31.
+ * Author: wang
+ */
+
+public class VerticalTextView2 extends View {
+
+ public static enum StartAlign {
+
+ LEFT(0x0),
+ RIGHT(0x1);
+
+ private int value;
+
+ StartAlign(int value) {
+ this.value = value;
+ }
+
+ public int getValue() {
+ return value;
+ }
+ }
+
+ /**
+ * 需要绘制的文字
+ */
+ private String mText = "";
+ /**
+ * 文本的颜色
+ */
+ private int mTextColor = Color.BLACK;
+ /**
+ * 文本的大小
+ */
+ private float mTextSize = 40;
+
+ /**
+ * 每列最多显示数量
+ */
+ private int vTextNum = -1;
+
+
+ /**
+ * 字体高度
+ */
+ private float mFontHeight;
+ private float mFontBaseLine;
+
+ // 列宽度
+ private int mLineWidth = 0;
+ // 列间距
+ private int mLineSpacing = 20;
+
+ private int specHeight;
+ private int specWidth;
+ //能够显示文本的最大高度
+
+ // 绘制字体的默认方向
+ private StartAlign textStartAlign = StartAlign.LEFT;
+
+ /**
+ * 绘制时控制文本绘制的范围
+ */
+ private Paint mPaint;
+
+ private boolean isMeasure;
+
+ public VerticalTextView2(Context context) {
+ this(context, null);
+ }
+
+ public VerticalTextView2(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public VerticalTextView2(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ if (attrs != null) {
+ //获取自定义属性的值
+ TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.VerticalTextView2, defStyleAttr, 0);
+ try {
+ mText = a.getString(R.styleable.VerticalTextView2_ver_text);
+ mTextColor = a.getColor(R.styleable.VerticalTextView2_ver_textColor, Color.BLACK);
+ mTextSize = a.getDimensionPixelSize(R.styleable.VerticalTextView2_ver_textSize, 40);
+ vTextNum = a.getInt(R.styleable.VerticalTextView2_ver_textNum, -1);
+ mLineSpacing = a.getDimensionPixelOffset(R.styleable.VerticalTextView2_ver_lineSpacing, 20);
+ int align = a.getInt(R.styleable.VerticalTextView2_ver_textStartAlign, StartAlign.RIGHT.getValue());
+ if (StartAlign.LEFT.getValue() == align) {
+ textStartAlign = StartAlign.LEFT;
+ } else {
+ textStartAlign = StartAlign.RIGHT;
+ }
+ } finally {
+ a.recycle();
+ }
+ }
+ mPaint = new Paint();
+ mPaint.setTextSize(mTextSize);
+ mPaint.setColor(mTextColor);
+ // 文字居中
+ mPaint.setTextAlign(Paint.Align.CENTER);
+ // 平滑处理
+ mPaint.setAntiAlias(true);
+
+ }
+
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ //绘制文字
+ char StringItem;
+ boolean isRight = StartAlign.RIGHT == textStartAlign;
+ float textHeight = mFontHeight;
+ int viewHeight = specHeight;
+ float mTextPosY = mFontBaseLine;
+ float mTextPosX = isRight ? getWidth() : 0;
+ mTextPosX += isRight ? -mLineWidth >> 1 : mLineWidth >> 1;//字体居住绘制 x起始位置-mLineWidth>>1
+ int textLength = mText.length();
+ for (int i = 0; i < textLength; i++) {
+ StringItem = mText.charAt(i);
+ if (mTextPosY > viewHeight) {
+ mTextPosY = mFontBaseLine;
+ mTextPosX += isRight ? -mLineWidth : mLineWidth;
+ if (mTextPosX > specWidth) {
+ return;
+ }
+ }
+ canvas.drawText(String.valueOf(StringItem), mTextPosX, mTextPosY, mPaint);
+ mTextPosY += textHeight;
+ }
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+
+ //onMeasure方法会调用多次,为了避免重复分段,做判断
+
+ if (!isMeasure) {
+ isMeasure = true;
+ //计算字体高度大小
+ measureTextSize();
+ }
+ measureWH(heightMeasureSpec);
+ //保存测量宽度和测量高度
+ setMeasuredDimension(specWidth, specHeight);
+ }
+
+
+
+ private void measureWH(int heightMeasureSpec) {
+ int heightMode = MeasureSpec.getMode(heightMeasureSpec); //获取高的模式
+ int heightSize = MeasureSpec.getSize(heightMeasureSpec); //获取高的尺寸
+
+ int textHeight = (int) mFontHeight;
+ int textLength = null == mText ? 0 : mText.length();
+
+ //高度
+ //match_parent或者具体的值
+ if (heightMode == MeasureSpec.EXACTLY) {
+ specHeight = heightSize;
+ } else {
+ //wrap_content
+ float tempHeight = Math.min(textHeight * textLength, heightSize);
+ if (vTextNum != -1) {
+ tempHeight = vTextNum * textHeight;
+ }
+ specHeight = (int) (getPaddingTop() + tempHeight + getPaddingBottom());
+ }
+
+ float lineNum = textHeight * textLength / specHeight;
+
+ int textWidth = (int) (Math.ceil(lineNum) * mLineWidth);
+ specWidth = getPaddingLeft() + textWidth + getPaddingRight();
+ }
+
+ public String getText() {
+ return mText;
+ }
+
+ public void setText(String mText) {
+ this.mText = mText;
+ resetMeasure();
+ }
+
+ public int getTextColor() {
+ return mTextColor;
+ }
+
+ public void setTextColor(int mTextColor) {
+ this.mTextColor = mTextColor;
+ mPaint.setColor(mTextColor);
+ }
+
+ public float getTextSize() {
+ return mTextSize;
+ }
+
+ public void setTextSize(float mTextSize) {
+ this.mTextSize = mTextSize;
+ mPaint.setTextSize(mTextSize);
+ resetMeasure();
+ }
+
+ private void resetMeasure() {
+ isMeasure = false;
+ requestLayout();
+ }
+
+ /**
+ * 计算文字大小高度
+ */
+ private void measureTextSize() {
+
+ mPaint.setTextSize(mTextSize);
+ // 获得行宽包括间隔
+ if (mLineWidth == 0) {
+ float[] widths = new float[1];
+ // 获取单个汉字的宽度
+ mPaint.getTextWidths("汉", widths);
+
+ mLineWidth = mLineSpacing + (int) widths[0];
+ }
+
+ Paint.FontMetrics fm = mPaint.getFontMetrics();
+
+ // 获得字体高度
+ mFontHeight = fm.bottom - fm.top;
+ mFontBaseLine = - fm.top;
+
+ }
+
+}
diff --git a/app/src/main/java/com/aspsine/swipetoloadlayout/demo/view/footer/WRefreshFooterView.java b/app/src/main/java/com/aspsine/swipetoloadlayout/demo/view/footer/WRefreshFooterView.java
new file mode 100644
index 0000000..ade1651
--- /dev/null
+++ b/app/src/main/java/com/aspsine/swipetoloadlayout/demo/view/footer/WRefreshFooterView.java
@@ -0,0 +1,96 @@
+package com.aspsine.swipetoloadlayout.demo.view.footer;
+
+import android.content.Context;
+import android.graphics.drawable.AnimationDrawable;
+import android.util.AttributeSet;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+
+import com.aspsine.swipetoloadlayout.SwipeLoadMoreTrigger;
+import com.aspsine.swipetoloadlayout.SwipeTrigger;
+import com.aspsine.swipetoloadlayout.demo.R;
+import com.aspsine.swipetoloadlayout.demo.view.VerticalTextView2;
+
+
+/**
+ * Created on 2016/1/14.
+ * Author: wang
+ */
+public class WRefreshFooterView extends LinearLayout implements SwipeTrigger, SwipeLoadMoreTrigger {
+
+ private ImageView mLoadingImg;
+
+ private VerticalTextView2 mLoadMoreTV;
+
+ private AnimationDrawable mAnimDrawable;
+
+ private int mFooterHeight;
+
+
+ public WRefreshFooterView(Context context) {
+ super(context);
+ mFooterHeight = getResources().getDimensionPixelOffset(R.dimen.refresh_footer_height);
+ }
+
+ public WRefreshFooterView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ mFooterHeight = getResources().getDimensionPixelOffset(R.dimen.refresh_footer_height);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mLoadingImg = (ImageView) findViewById(R.id.loading_img);
+ mLoadMoreTV = (VerticalTextView2) findViewById(R.id.load_more_tv);
+ mAnimDrawable = (AnimationDrawable) mLoadingImg.getDrawable();
+ }
+
+
+ @Override
+ public void onPrepare() {
+
+ }
+
+ @Override
+ public void onMove(int y, boolean isComplete, boolean automatic) {
+ if (!isComplete) {
+ mAnimDrawable.stop();
+ mLoadingImg.setVisibility(GONE);
+ mLoadMoreTV.setVisibility(VISIBLE);
+ if (-y >= mFooterHeight) {
+ mLoadMoreTV.setText("释放加载更多");
+ } else {
+ mLoadMoreTV.setText("左滑加载更多");
+ }
+ }
+ }
+
+ @Override
+ public void onRelease() {
+
+ }
+
+ @Override
+ public void onLoadMore() {
+ mLoadMoreTV.setVisibility(GONE);
+ mLoadingImg.setVisibility(VISIBLE);
+ mAnimDrawable.start();
+ }
+
+ @Override
+ public void onComplete() {
+ mAnimDrawable.stop();
+ mLoadingImg.setVisibility(GONE);
+ mLoadMoreTV.setVisibility(VISIBLE);
+ mLoadMoreTV.setText("加载完成");
+ }
+
+ @Override
+ public void onReset() {
+ mAnimDrawable.stop();
+ mLoadingImg.setVisibility(GONE);
+ mLoadMoreTV.setVisibility(VISIBLE);
+ mLoadMoreTV.setText("左滑加载更多");
+ }
+
+}
diff --git a/app/src/main/java/com/aspsine/swipetoloadlayout/demo/view/header/WRefreshHeaderView.java b/app/src/main/java/com/aspsine/swipetoloadlayout/demo/view/header/WRefreshHeaderView.java
new file mode 100644
index 0000000..e9ba271
--- /dev/null
+++ b/app/src/main/java/com/aspsine/swipetoloadlayout/demo/view/header/WRefreshHeaderView.java
@@ -0,0 +1,132 @@
+package com.aspsine.swipetoloadlayout.demo.view.header;
+
+import android.content.Context;
+import android.graphics.drawable.AnimationDrawable;
+import android.util.AttributeSet;
+import android.view.animation.Animation;
+import android.view.animation.AnimationUtils;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+
+import com.aspsine.swipetoloadlayout.SwipeRefreshTrigger;
+import com.aspsine.swipetoloadlayout.SwipeTrigger;
+import com.aspsine.swipetoloadlayout.demo.R;
+import com.aspsine.swipetoloadlayout.demo.view.VerticalTextView2;
+
+/**
+ * Created on 2016/9/20.
+ * Author: wang
+ */
+public class WRefreshHeaderView extends LinearLayout implements SwipeTrigger, SwipeRefreshTrigger {
+
+ private ImageView mArrowImg;
+
+ private ImageView mLoadingImg;
+
+ private ImageView mSuccessImg;
+
+ private VerticalTextView2 mRefreshTV;
+
+ private AnimationDrawable mAnimDrawable;
+
+ private int mHeaderHeight;
+
+ private Animation rotateUp;
+
+ private Animation rotateDown;
+
+ private boolean rotated = false;
+
+ public WRefreshHeaderView(Context context) {
+ this(context, null);
+ }
+
+ public WRefreshHeaderView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public WRefreshHeaderView(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ mHeaderHeight = getResources().getDimensionPixelOffset(R.dimen.refresh_header_height);
+ rotateUp = AnimationUtils.loadAnimation(context, R.anim.rotate_up);
+ rotateDown = AnimationUtils.loadAnimation(context, R.anim.rotate_down);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+
+ mRefreshTV = (VerticalTextView2) findViewById(R.id.refresh_tv);
+ mArrowImg = (ImageView) findViewById(R.id.arrow_img);
+ mSuccessImg = (ImageView) findViewById(R.id.success_img);
+ mLoadingImg = (ImageView) findViewById(R.id.loading_img);
+ mAnimDrawable = (AnimationDrawable) mLoadingImg.getDrawable();
+ }
+
+ @Override
+ public void onRefresh() {
+ mSuccessImg.setVisibility(GONE);
+ mArrowImg.clearAnimation();
+ mArrowImg.setVisibility(GONE);
+ mLoadingImg.setVisibility(VISIBLE);
+ mAnimDrawable.start();
+ mRefreshTV.setText("加载中...");
+ }
+
+ @Override
+ public void onPrepare() {
+
+ }
+
+ @Override
+ public void onMove(int y, boolean isComplete, boolean automatic) {
+ if (!isComplete) {
+ mArrowImg.setVisibility(VISIBLE);
+ mAnimDrawable.stop();
+ mLoadingImg.setVisibility(GONE);
+ mSuccessImg.setVisibility(GONE);
+ if (y > mHeaderHeight) {
+ mRefreshTV.setText("释放刷新");
+ if (!rotated) {
+ mArrowImg.clearAnimation();
+ mArrowImg.startAnimation(rotateUp);
+ rotated = true;
+ }
+ } else if (y < mHeaderHeight) {
+ if (rotated) {
+ mArrowImg.clearAnimation();
+ mArrowImg.startAnimation(rotateDown);
+ rotated = false;
+ }
+ mRefreshTV.setText("右滑刷新");
+ }
+ }
+ }
+
+ @Override
+ public void onRelease() {
+
+ }
+
+ @Override
+ public void onComplete() {
+ rotated = false;
+ mSuccessImg.setVisibility(VISIBLE);
+ mArrowImg.clearAnimation();
+ mArrowImg.setVisibility(GONE);
+ mAnimDrawable.stop();
+ mLoadingImg.setVisibility(GONE);
+ mRefreshTV.setText("刷新完成");
+ }
+
+ @Override
+ public void onReset() {
+ rotated = false;
+ mSuccessImg.setVisibility(GONE);
+ mArrowImg.clearAnimation();
+ mArrowImg.setVisibility(VISIBLE);
+ mAnimDrawable.stop();
+ mLoadingImg.setVisibility(GONE);
+ }
+
+}
diff --git a/app/src/main/res/drawable/refresh_loading.xml b/app/src/main/res/drawable/refresh_loading.xml
new file mode 100644
index 0000000..8b18765
--- /dev/null
+++ b/app/src/main/res/drawable/refresh_loading.xml
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
index 10d525e..db5d8f1 100644
--- a/app/src/main/res/layout/activity_main.xml
+++ b/app/src/main/res/layout/activity_main.xml
@@ -1,24 +1,33 @@
-
+ android:layout_height="match_parent"
+ android:orientation="vertical">
-
+ android:layout_height="wrap_content"
+ android:text="下拉刷新 上拉加载更多"
+ android:layout_marginTop="64dp"
+ android:layout_marginStart="16dp"
+ android:layout_marginEnd="16dp"/>
-
+
-
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_recycler.xml b/app/src/main/res/layout/activity_recycler.xml
new file mode 100644
index 0000000..6113f2d
--- /dev/null
+++ b/app/src/main/res/layout/activity_recycler.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/activity_ver.xml b/app/src/main/res/layout/activity_ver.xml
new file mode 100644
index 0000000..10d525e
--- /dev/null
+++ b/app/src/main/res/layout/activity_ver.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/activity_viewpager.xml b/app/src/main/res/layout/activity_viewpager.xml
new file mode 100644
index 0000000..2db9bce
--- /dev/null
+++ b/app/src/main/res/layout/activity_viewpager.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/item_pager_text.xml b/app/src/main/res/layout/item_pager_text.xml
new file mode 100644
index 0000000..ecb30c1
--- /dev/null
+++ b/app/src/main/res/layout/item_pager_text.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/item_text.xml b/app/src/main/res/layout/item_text.xml
new file mode 100644
index 0000000..72290ef
--- /dev/null
+++ b/app/src/main/res/layout/item_text.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/layout_refresh_footer_view.xml b/app/src/main/res/layout/layout_refresh_footer_view.xml
new file mode 100644
index 0000000..0d98fdb
--- /dev/null
+++ b/app/src/main/res/layout/layout_refresh_footer_view.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/layout_refresh_header_view.xml b/app/src/main/res/layout/layout_refresh_header_view.xml
new file mode 100644
index 0000000..74a50c9
--- /dev/null
+++ b/app/src/main/res/layout/layout_refresh_header_view.xml
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/recycler_view_head_foot.xml b/app/src/main/res/layout/recycler_view_head_foot.xml
new file mode 100644
index 0000000..b15f421
--- /dev/null
+++ b/app/src/main/res/layout/recycler_view_head_foot.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/menu/menu_main.xml b/app/src/main/res/menu/menu_main.xml
index cd75729..1c66ea0 100644
--- a/app/src/main/res/menu/menu_main.xml
+++ b/app/src/main/res/menu/menu_main.xml
@@ -1,6 +1,6 @@
diff --git a/app/src/main/res/mipmap-hdpi/refresh_head_arrow.png b/app/src/main/res/mipmap-hdpi/refresh_head_arrow.png
new file mode 100644
index 0000000000000000000000000000000000000000..701605b82b247c707f4d7c40a2edd39bf89ba47d
GIT binary patch
literal 568
zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=EX7WqAsj$Z!;#Vf2?-
zyCBS15WI*T$dD{?jVKAuPb(=;EJ|f4FE7{2%*!rLPAo{(%P&fw{mw>;fq`+Wr;B4q
z#hkaZ4YLkA2)O=dnSU~rZ-QQL&<`2SFtt`r78Oye_U;|QkC~rDv=u5hd2A}%koKJ0
zhM#Aez@`8EnMb2Gt!IvX}Jwcf12IGx2HyB^-s$;p(-@QKfc3q`h
zJ!iTDe}Zk@FYVj2HnT9kK2t6}O`*XHuD*oHcG^I#W2NQm(@{;(YjxH_n=btM#F@4
zEd8Gx9y)#qn-;000F3Nkl<|Q+NT|))20=r-N4e2NElx<-8Tt(yN9SO&~KwQz95K1uKvNm9xgkjj4UD-)E
zZqo~ON>o(h1StSrd!gbfSOLFFyO^FRjd
zpa-zza)!eaHumXZTeq*y7B{yMdux-Izt`q_hLn2QugCa{KZM=nFfj}Hj*{K7d7HOe
z4lBDyEoPy8X}2AgwwK())@w=jJpOari!wTryucAI1qJs3`?W-|X+;$5LpSVxb1QC-
zopvePc7ob+Q3@_;Rer3L&T1%F(Y2tcC`P=oqFu8UH)LmB%HhvdyVPcOG(bhB?Wo3x
z1CU^ZVt_*g(Hza2tl~yo!WXL0=9qsaYzx99(=`L3e&D*DR9aZbLfw59d7);$io-3q
z(Bclu+2vPPx3eO&8CPS`nxFO+fW5Xvv0jd=R0%2jPo)L{x7!}FycLYs=m>=7Ixt%GL)j6fJ({;5fBg~Mx0*y$+65De{;tNWrb>{TB_EA6IXJXdya*t@5+#B
zNLeCA54|LLkUkQ`h#`Uoeep(Tgj>^nAA7Y#al;cm^Z*sgKo5?901atac@@+t?HJt$
zbdt1C$j4d^A~FKNQKbSHF>JS94&p*NF02yu>TjJN>BbtqV_d!gobN7awO4h3;T
zD6K~>&T0P5umfdELz>ia#l8E(V4(&eh?qB@BdS!XE>uGwhH-b?O=xotOTwJ&Wr}zYv;}?zT
zu-29B1cZX#-w?4%YuJ-=Lui5|Fkw@6!wZ*TOS@cvD+jZ0&vvAhX;9AyB{c4nPfmFt
zDZr9odCU2(dfXDO(|tt4>06k(m{(&7%Uanmtly4xu#af*0UA}*($JzNY|Z988KXPt
zBZ3|I0Idtbs(x&3^#Savtr~`{cdWUm$p-*@t>coup{$kyW*92l7xoNh-#4^*bRaLN
zu|DScylLBGjs99g=QjYB?X!)YnGO=sN=L(j%}}C5reTkvX>aPkAfk1{=T}YD#Qer*
tCmz^RvUImTyAZk%x)8b$x)AQ~@h_(tax0p5-edp(002ovPDHLkV1ntaZf^hp
literal 0
HcmV?d00001
diff --git a/app/src/main/res/mipmap-hdpi/refresh_loading02.png b/app/src/main/res/mipmap-hdpi/refresh_loading02.png
new file mode 100644
index 0000000000000000000000000000000000000000..bc930f2d4bdd1e8dd7aa2ee2515967574c4ef692
GIT binary patch
literal 1305
zcmV+!1?KvRP)n-;000EwNklw@L2^Xyjr9a}P
zr8`@Q2$d0~BBhiH(pj~Hm<9$C;wB+zI&bElF5aD&mqcuEUa^b0m${kTxnVvz=X=gQ
z=gD~4!_h(LAaoEq2pxnD!v7Yb`*|OZVZc=kw!PxH`7S+uvj1-T2p}x1=~w&-7z|Sa
zDlFJDz%p`#mahJ5;zh1K$$WyD6ToEd_WHG$Xzpgz5&fVX8FwO#|*o2eDHK|Bb1=ji2)*xQ3KrsYk#p;I7
z$_fJ1&qun*apgfwS6K`?I4qdeI_t~=hXcT(RSl2j?f4HDzyIiZe_N?3PDM;G0F3h{
zx2!63(=L!B(ww0=yA+~f&fc+{MgFGYv7()_9>aQVf@^JNM>ex!kKW^`-I!jw@&&K4
z$cmD11Y5E%ZOHzz$Nr#ASivWmG7CrC&}wXYp@Pn-fBLZ=XoXZ*8iuX-2mZ0|w{t!K
z7=G2?Xd6LjMOSrKQTQ#d5iyNYQf*yNhR2WiyvO75k-uwKbhmg)kz1;vpv}f+Cv-
znC5cogyHct@%VX*e9o|{-9Edzn~2c!d{s(38GJs-AzQSS)=hXBPlE;#5e*u6nizh-
zUbUJn8Sm3J!pW4FQo>7B2A|O&;&19iG-(EBv`$U7k8lbbrbHous8O+n7D(*zPubuJ
z8$_`i;y4(#5``olYE(X0cwsrZvI?zeaJYgiI~-j&y67fHHy;BVYy?X*XwaltZWdZV
z9$)a~cI4={-G+YWsXupadH(gUb{~|pT2FEUT6eRt6=ECY*~@-@XPGsgr0md`ZLT+I
z26gy#Kl{R3IK3l0H!rLhCC4l5V;_6T(?u7;rOtfVe)+2HRl}eTv8a_8zy&oxgF4WS
z$Kg=FhE&s%vMAz}Ui&zqqEMu-wcK(?7Y^_=aDdT;4d{Z)301kxY9A~)Za0HhIO@ipgt6z+(e
zKjx>d^tXxN5Smb*YvI&1BLpZ4_E^^~
zRfQYSdF2&prX@MU@CS8XIo16POYSRQ)G5~kVD&5zD(Yd69_qg8!lBbH?{GPdXi38=
z0To-Y5z84q&uOlKs_qXzrZ@Cdl`IgJ^f^hAR#8L2DUSqYdVn&Q?2+LSHe^L#b@;mO
zd~zp~@-_MDc+IO6bWv3e6dSw8pca>{9$&J&sS1LpX%S)hmSRs%-71TE)9zy*8d{
z5x;GYhM)Je2AfTtVc44853w<7S$9*Z`BVt4>Q{J10{(?n!?35VM;#euN2TrxUDq+B
zAF1zbY7MK~cbqiSUE`UEFV;tBD@aY>Dt&vb(F?3u-1K0}`o>a4UPfBj>Wp-s37VzV
z(4|_J-8M11&kNh;7Kk}65iCH`d67JkYP)>xJo@Y)bPzfS9fS_T%N_p!Z+};7egQ3n
P00000NkvXXu0mjfu~}&&
literal 0
HcmV?d00001
diff --git a/app/src/main/res/mipmap-hdpi/refresh_loading03.png b/app/src/main/res/mipmap-hdpi/refresh_loading03.png
new file mode 100644
index 0000000000000000000000000000000000000000..e461335e941e47b8e72fc4d3ac333dce759e13c4
GIT binary patch
literal 1282
zcmV+d1^xPoP)n-;000EZNklt*xL1sd6D;UPTu6?ggTb7~LydY1Re2@UB}r85!M+Grm84PcO0Eiomnc7hxT?Muxm+m_He-VSI}vCS6O?#`Q2>roX2ISTEC5GCSXxm@2xT={JgzN%I8)ubQ_k85
zFvZJUqXb;#JP|Q>1$Vg+CgQGMSEN{1h0wUpYCtH*ESL7KLbDC{JLiV&zTLK}bwT>7ZtK1u);XW|csxSu
zx{`)1RJmv;HA!R$vW&T^4}j_j$sUg%Q#RaxtVP|LiT8*Q28sEVJ9b**V6m1w4xjLo
z*6?`zl43h
zd}sN|J)&TF!*Go)?+T7S`pA$WA|gu!Jm3Kjh>3$5^+MApJRYA{;fYmnjhjri?)U8H
z-hVHA>{Yu_VZk;-M8M%T-9lT1EYGo@1N=#qP5z-q%yvVC1;5$UKxk77PwWxzbp#*l
zsv#U1BKpbkJo|{q;BXz8^@B9b(K;)K!*s0a!~U(KE9Dc5$|Zn
zM#HeIO$UIIl?~bpx^gI6(X_Ij)K^ojh&W|AhlOIEggGXP_neP
z(g|&7G3YQ6CgrIhY#D~F+wTtzzrSf+2yLafl(BA*AMF%oKU1Ttb`(N2`(9@xe%Z~x
z96Rxd(tXNekArA0=&&%b
sC60S8;(L!idk8&*9zqYHhwyCMzlHoz^h}NGJpcdz07*qoM6N<$f?pb5`Tzg`
literal 0
HcmV?d00001
diff --git a/app/src/main/res/mipmap-hdpi/refresh_loading04.png b/app/src/main/res/mipmap-hdpi/refresh_loading04.png
new file mode 100644
index 0000000000000000000000000000000000000000..071af1edd4abb38037f943ab8fe82e82af77604f
GIT binary patch
literal 1316
zcmV+<1>5?GP)n-;000E*Nkl$S!e~iSmWZ`C@7}Qciq@iPl5)_<;3m4LMCH+&n(u$}}H;NnE
z&6I*=O2itfh=s!7LI`OLOqehQnJ^HYm$}Eq-1(W(G|mINn7Pb*Gw)sIe(pWzd(J&?
z#Q48o$`E0QFhm$43=xJ1&n-gYX*bVffb|_a=iV!yLSN4}2S)(G5dYJHwXd~bQ)Gbx
zYghf%?qdvw5dQ}8pT&P~-_MaIO^#i|TRetn&cFM}G3G&**zm)Q0x;Sd(2g+=w#3G>
zL+oh?dQ3r%aj*$KqyB{MNj|U?Fm9h%oQCKz!3Y3jywTs+08Wtt!(P~rDfU3v)gJis
zoTQ{5B4x9F*xy<@i3Oo|9ps*D2?9y7bh+kLfGkNcY|HK$7FTH6wF-49c%TevkSzeV
zfHWB&B??yGk0JI{1TaC45gxKk9VpuCG6{wpw-R~Y1M*IytO&LuSDLYq72dMYBD-xEma%g-YLK!8t`CmTiio4U&na$E
zmM?fk)~zBwQW{`o`;tlf(;nLqTd;H|a1ZQ`irkWF%NH~)7i2;o%LA$GQbffb*khY;
z7hKxmFbr3G+O(^vj|JlO;0MHT)0Xr}EMnR2&qp!1qhOKz
zXb(9n8N(u5ZDmAlL$ord!&Y5np+V8^&R1hlgtT*{RP81|+fiG$ir{=YzN5e6vk=
z&0eNt<=8~olO;B6qdD7Fz&4yeIvRKn*tE^qGNocuWJ$L%N>*U4388)k8i1q04h`=O
zU{ho*4R@L#B?|AX{bxmJPSc7aD2k?ooDz^AL4uGZ2RHyk{6n1`c4*L`fy*`0j-YKJCH6TCX7@LNgx7f{wLSQPM@-)N!2&4ykv)R)%ng+>9GuuN+gR??j$L9L@r85T^3!LuOy@Ae2r0$K@n1rQ5lhkvMhB$
z(esj2G}LJ+GbLG|U~4vQpCvdIVsyx7#KmIET6ZxEJ#Ksz4{*sl4BIe
zam5}P4#7>5(V|1^EPc2XQyro0vm!~&dYqz^1nCrXB`Y@5lw~d1N?5U#GTMoIbCOcO
zneSu{U(6S*ZWxwwbLP|dzzDjlj?kIzlBN^34TCL~{qAWd)@rJ+Z3}`_Kpb8
zRl~6D{UGdFS@*S@at4v_n-;000E`Nkl6j}r#tK5Ngi*UfN=jwWuB?!^Ip1BpC&}AZ+GNWV+NabGNz0GR`p0s?hgwNt6DKwIkE<;Kzz?C7aUSl
zb@C;Gau0H(&+48eZ_v@xl`^ZoEil>Ih595RuyFcb3oN(|>Oqt^7{3}O<%e$<@VK^MAY$~53MHgM9$?ygTH~@Ug1`!+7
z2zes!v%bknR{2)s=r`rbhTXin`IvvUe&~0x_s)XD72KM`k&4G!s?e3-;1E6Z^OV2&
zmuldlhQ}BDTB565Q{j~(;*&%rv`sIEks?KgLmcKX2S}45c3E@Y#dgTC=>!98z>dd>
zIcBDd3|+tm5pl9|IIPbGDO;sYl`^sX5xYdru*luDrPwuHbm0I`4F{wU>bIMgHmqb1
zS!oLk23VSNHfm|Zu(UgAOR1m_7jlNDR(IOTFfV6s|1`tn%TbGE%3-)ZyX7vpbWF30
z++3<5&?W>QLb5jJqc}(Sw7;NR5Bgey5a$CMcSpkVxN>UBExF#*h&$>U?VMr|NBxNJ
z^-F$A<$5&uBN}f*nX)K6*W3du8Lr>mu#Ah`l&e^y(bqBEP+C<@`3hfK&aPOuVNh*@
z9Ia^GcF>;Kj8z?myXrCyOWUwjY=|*XQS%yB+Czq`?w-e$c*-cJSXZ%S2!>VoA~Y`;
zE@y+mdS;NP8_088&jR^_%2D#TD%0cBEqBn3UMVCU!)0tNSS2fig$n{UgFAJhaRmq@tvd3
l4ulSb4ulSb4uscx{RcjeQQV-{U}^vW002ovPDHLkV1nQTcx3n-;000ElNklRT;BXI~I2_3WB}xdYfMD+FSpDUCoL3qq|YO
z(wmNfv!mh&L&U-$S3>FPAf=_dX$e$vn)7&(lQc<7*DeS2BKZ&z1K0=`Knu27_mBDk=x=^;L>Gh>SUtQK(g$DO
z6NDKCg$?nAs@7s8c6~-4kp%{IQ%MG*ZS{d+1H5f}APUZr6+$m5sifQo!eX5fHc565
zfUG8B(|;a?4M&CWz_tv-GFIqhiV9?e(3Tz?g^q`W;CTJ0YV+~AyyXnr0$@uhM?O}O
zc~wP1boBbq4B3qOc;rP@BfJ#7Wusu&Day>*2f!R~DprIiD#g^Cc<-#X_2Yw$E|b#G
zw1$C6-r$~>BK$7bqQF91GHi((u?{ThT>^!=3qpBaQa>omG`ADc1w*J`A4cc=PPArU
z*{k-%Hri5lOmQKl>|)-jA#tr|itEO3a8*BYEyzqNi8b$nJvj9GzPQ~%1$Yo#6l;W$u(
z*ds!GUI#m-0y$W0-u33nbJLNTdLfRVD94O$G<=dGA_Y_k3Ax#%r4bbzyd#6afKgphg&Hhhx|f
zqgK|MB~vL|%n#T_%NrKjx4vHYL7H@&lr=n-C>!=`1csG#pUuR)V1b=y!UDsvz)$
ztmOExU-gWKK5I?N37v?pTA*q)>ub?BmbXjR55lTl)6uYb`mN6%S<$P8W0x&scwlF2
z&Bm+%%xgtw6hx5m%eoDe_=^e7vdw$~bhI`5a#sy3@1t=D9$PHMn&E~X$Ke=_@?HX}
zEKDsV&_R1!3-4IQ3-PCtmBHeHPEe*4v!bAklAI&F6#zDA+16&hZIGQ;RS0D@sbohP
zZ-p|xJJPhbxI$Yh##MA&Nl#P0>x*r}uuc1ewsAl1Cw|wa5ZYF9Ez6$ulTDe~&s42#
z^p7_~s`|m+m-scYjh=lVl=;4`cx{mdw&L9eY+c{&vn7zkE=wn%rkOb^RG8}+a7i=}
zbaVmMUci=iTe96o_h>Kc{WsNv(1Xx}(1Xx}@c)JIFYCrow`0$|Z~y=R07*qoM6N<$
Eg4?!h9smFU
literal 0
HcmV?d00001
diff --git a/app/src/main/res/mipmap-hdpi/refresh_loading07.png b/app/src/main/res/mipmap-hdpi/refresh_loading07.png
new file mode 100644
index 0000000000000000000000000000000000000000..797c5d8a1f4ee2518ca68d2e18b3e23d665088e4
GIT binary patch
literal 1280
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
literal 0
HcmV?d00001
diff --git a/app/src/main/res/mipmap-hdpi/refresh_loading11.png b/app/src/main/res/mipmap-hdpi/refresh_loading11.png
new file mode 100644
index 0000000000000000000000000000000000000000..587c1df6ca26891df1299e474b48fa326f67022e
GIT binary patch
literal 1303
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
i`2=qc_rCw@KjVJ}-R2o6
literal 0
HcmV?d00001
diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml
index 0715ea3..60cce0d 100644
--- a/app/src/main/res/values/attrs.xml
+++ b/app/src/main/res/values/attrs.xml
@@ -24,4 +24,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml
index 31c3c12..dafe89d 100644
--- a/app/src/main/res/values/dimens.xml
+++ b/app/src/main/res/values/dimens.xml
@@ -32,4 +32,8 @@
100dp
+
+ 40dp
+ 40dp
+
From 78009a2d5d78ef3c07a67f33885a7d344aa85c9d Mon Sep 17 00:00:00 2001
From: wxj <986798656@qq.com>
Date: Wed, 12 Jul 2017 09:41:11 +0800
Subject: [PATCH 5/6] update name
---
app/src/main/res/layout/activity_viewpager.xml | 4 ++--
...esh_footer_view.xml => layout_refresh_footer_hor_view.xml} | 0
...esh_header_view.xml => layout_refresh_header_hor_view.xml} | 2 +-
app/src/main/res/layout/recycler_view_head_foot.xml | 4 ++--
build.gradle | 2 +-
gradle/wrapper/gradle-wrapper.properties | 4 ++--
6 files changed, 8 insertions(+), 8 deletions(-)
rename app/src/main/res/layout/{layout_refresh_footer_view.xml => layout_refresh_footer_hor_view.xml} (100%)
rename app/src/main/res/layout/{layout_refresh_header_view.xml => layout_refresh_header_hor_view.xml} (97%)
diff --git a/app/src/main/res/layout/activity_viewpager.xml b/app/src/main/res/layout/activity_viewpager.xml
index 2db9bce..192a47f 100644
--- a/app/src/main/res/layout/activity_viewpager.xml
+++ b/app/src/main/res/layout/activity_viewpager.xml
@@ -9,7 +9,7 @@
+ layout="@layout/layout_refresh_header_hor_view"/>
+ layout="@layout/layout_refresh_footer_hor_view"/>
\ No newline at end of file
diff --git a/app/src/main/res/layout/layout_refresh_footer_view.xml b/app/src/main/res/layout/layout_refresh_footer_hor_view.xml
similarity index 100%
rename from app/src/main/res/layout/layout_refresh_footer_view.xml
rename to app/src/main/res/layout/layout_refresh_footer_hor_view.xml
diff --git a/app/src/main/res/layout/layout_refresh_header_view.xml b/app/src/main/res/layout/layout_refresh_header_hor_view.xml
similarity index 97%
rename from app/src/main/res/layout/layout_refresh_header_view.xml
rename to app/src/main/res/layout/layout_refresh_header_hor_view.xml
index 74a50c9..a3828c6 100644
--- a/app/src/main/res/layout/layout_refresh_header_view.xml
+++ b/app/src/main/res/layout/layout_refresh_header_hor_view.xml
@@ -25,7 +25,7 @@
android:layout_width="28dp"
android:layout_height="28dp"
android:layout_gravity="center"
- android:rotation="90"
+ android:rotation="270"
android:src="@mipmap/refresh_head_arrow"/>
+ layout="@layout/layout_refresh_header_hor_view"/>
+ layout="@layout/layout_refresh_footer_hor_view"/>
\ No newline at end of file
diff --git a/build.gradle b/build.gradle
index 74b2ab0..1ea4bd0 100644
--- a/build.gradle
+++ b/build.gradle
@@ -5,7 +5,7 @@ buildscript {
jcenter()
}
dependencies {
- classpath 'com.android.tools.build:gradle:2.2.3'
+ classpath 'com.android.tools.build:gradle:2.3.0'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 3e1c843..22afba8 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
-#Wed Jan 04 00:31:07 CST 2017
+#Fri Mar 10 17:10:42 CST 2017
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip
From 494d4f2c3660f1e80fbf56b207e90b1aa33aa1b4 Mon Sep 17 00:00:00 2001
From: wxj <986798656@qq.com>
Date: Wed, 12 Jul 2017 09:42:51 +0800
Subject: [PATCH 6/6] remove apk
---
.gitignore | 42 ------------------------------------------
art/demo.apk | Bin 2518580 -> 0 bytes
2 files changed, 42 deletions(-)
delete mode 100644 .gitignore
delete mode 100644 art/demo.apk
diff --git a/.gitignore b/.gitignore
deleted file mode 100644
index ae946ad..0000000
--- a/.gitignore
+++ /dev/null
@@ -1,42 +0,0 @@
-### Android template
-# Built application files
-*.apk
-*.ap_
-
-# Files for the Dalvik VM
-*.dex
-
-# Java class files
-*.class
-
-# Generated files
-bin/
-gen/
-
-# Gradle files
-.gradle/
-build/
-
-# Local configuration file (sdk path, etc)
-local.properties
-
-# Proguard folder generated by Eclipse
-proguard/
-
-# Log Files
-*.log
-
-# Android Studio Navigation editor temp files
-.navigation/
-
-/.gitignore
-.gradle
-/local.properties
-/.idea/workspace.xml
-/.idea/libraries
-.DS_Store
-/build
-/captures
-/.idea
-*.iml
-.idea
diff --git a/art/demo.apk b/art/demo.apk
deleted file mode 100644
index 10c29fad18f5a43996cd8b96ff2376c151c727fc..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 2518580
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