69 | * This layout should be made the parent of the view that will be refreshed as a 70 | * result of the gesture and can only support one direct child. This view will 71 | * also be made the target of the gesture and will be forced to match both the 72 | * width and the height supplied in this layout. The GZoomSwifrefresh does not 73 | * provide accessibility events; instead, a menu item must be provided to allow 74 | * refresh of the content wherever this gesture is used. 75 | *
76 | */ 77 | public class GZoomSwifrefresh extends ViewGroup implements NestedScrollingParent, 78 | NestedScrollingChild { 79 | // Maps to ProgressBar.Large style 80 | public static final int LARGE = MaterialProgressDrawable.LARGE; 81 | // Maps to ProgressBar default style 82 | public static final int DEFAULT = MaterialProgressDrawable.DEFAULT; 83 | 84 | @VisibleForTesting 85 | static final int CIRCLE_DIAMETER = 40; 86 | @VisibleForTesting 87 | static final int CIRCLE_DIAMETER_LARGE = 56; 88 | 89 | private static final String LOG_TAG = GZoomSwifrefresh.class.getSimpleName(); 90 | 91 | private static final int MAX_ALPHA = 255; 92 | private static final int STARTING_PROGRESS_ALPHA = (int) (.3f * MAX_ALPHA); 93 | 94 | private static final float DECELERATE_INTERPOLATION_FACTOR = 2f; 95 | private static final int INVALID_POINTER = -1; 96 | private static final float DRAG_RATE = .5f; 97 | 98 | // Max amount of circle that can be filled by progress during swipe gesture, 99 | // where 1.0 is a full circle 100 | private static final float MAX_PROGRESS_ANGLE = .8f; 101 | 102 | private static final int SCALE_DOWN_DURATION = 150; 103 | 104 | private static final int ALPHA_ANIMATION_DURATION = 300; 105 | 106 | private static final int ANIMATE_TO_TRIGGER_DURATION = 200; 107 | 108 | private static final int ANIMATE_TO_START_DURATION = 200; 109 | 110 | // Default background for the progress spinner 111 | private static final int CIRCLE_BG_LIGHT = 0xFFFAFAFA; 112 | // Default offset in dips from the top of the view to where the progress spinner should stop 113 | private static final int DEFAULT_CIRCLE_TARGET = 64; 114 | 115 | /**底部view上拉最大的大小*/ 116 | private final int mBottomSpinnerOffsetEnd; 117 | 118 | private View mTarget; // the target of the gesture 119 | OnRefreshListener mListener; 120 | 121 | OnBottomRefreshListener mListenerBottom; 122 | 123 | boolean mRefreshing = false; 124 | 125 | //boolean mRefreshingBottom = false; 126 | 127 | boolean mRefreshingBottom = false; 128 | 129 | ScrollView s; 130 | 131 | private int mTouchSlop; 132 | /**初始化也是64dp,作为一个界定点,最终的刷新动画需要停在这个点上,超过这个点会减缓滑动速度*/ 133 | private float mTotalDragDistance = -1; 134 | 135 | // If nested scrolling is enabled, the total amount that needed to be 136 | // consumed by this as the nested scrolling parent is used in place of the 137 | // overscroll determined by MOVE events in the onTouch handler 138 | /**顶部没有被消费的*/ 139 | private float mTotalUnconsumed; 140 | /**底部滑动没有被消费的*/ 141 | private float mTotalUnconsumedBottom; 142 | private final NestedScrollingParentHelper mNestedScrollingParentHelper; 143 | private final NestedScrollingChildHelper mNestedScrollingChildHelper; 144 | private final int[] mParentScrollConsumed = new int[2]; 145 | private final int[] mParentOffsetInWindow = new int[2]; 146 | private boolean mNestedScrollInProgress; 147 | 148 | private int mMediumAnimationDuration; 149 | int mCurrentTargetOffsetTop; 150 | /**目前底部circle的底部*/ 151 | int mCurrentTargetOffsetBottom; 152 | 153 | private float mInitialMotionY; 154 | private float mInitialDownY; 155 | private boolean mIsBeingDragged; 156 | private int mActivePointerId = INVALID_POINTER; 157 | // Whether this item is scaled up rather than clipped 158 | /**如果在z轴它的上面没有东西,那么为true*/ 159 | boolean mScale; 160 | 161 | // Target is returning to its start offset because it was cancelled or a 162 | // refresh was triggered. 163 | private boolean mReturningToStart; 164 | /**是否正在返回底部*/ 165 | private boolean mReturningToEnd ; 166 | private final DecelerateInterpolator mDecelerateInterpolator; 167 | private static final int[] LAYOUT_ATTRS = new int[] { 168 | android.R.attr.enabled 169 | }; 170 | 171 | CircleImageView mCircleView; 172 | 173 | 174 | CircleImageView mCircleViewBottom; 175 | private int mCircleViewIndex = -1; 176 | /**bottomCircle在视图中的顺序*/ 177 | private int mCircleViewBottomIndex=-1; 178 | 179 | protected int mFrom; 180 | 181 | float mStartingScale; 182 | /**原始顶部*/ 183 | protected int mOriginalOffsetTop; 184 | /**原始底部*/ 185 | protected int mOriginalOffsetBottom; 186 | /**默认64dp*/ 187 | int mSpinnerOffsetEnd; 188 | /**底部spinner可以上拉到最大高度*/ 189 | int mSpinnerBottomOffsetEnd; 190 | MaterialProgressDrawable mProgress; 191 | /**底部Circle的drawable*/ 192 | MaterialProgressDrawable mProgressBottom; 193 | 194 | private Animation mScaleAnimation; 195 | 196 | private Animation mScaleDownAnimation; 197 | /**顶部circleView的透明度启动动画*/ 198 | private Animation mAlphaStartAnimation; 199 | /**底部 bottom view的透明度启动动画*/ 200 | private Animation mAlphaStartAnimationBottom; 201 | /**顶部circleView的透明度最大动画*/ 202 | private Animation mAlphaMaxAnimation; 203 | /**底部 bottom view的透明度最大动画*/ 204 | private Animation mAlphaMaxAnimationBottom; 205 | 206 | private Animation mScaleDownToStartAnimation; 207 | /**top circle标志位*/ 208 | boolean mNotify; 209 | 210 | /**bottom circle view 标志位*/ 211 | boolean mNotifyBottom; 212 | 213 | /**circle的初始上限*/ 214 | private int mCircleDiameter; 215 | 216 | // Whether the client has set a custom starting position; 217 | boolean mUsingCustomStart; 218 | 219 | private OnChildScrollUpCallback mChildScrollUpCallback; 220 | 221 | 222 | Handler hd =new Handler(){ 223 | @Override 224 | public void handleMessage(Message msg) { 225 | Log.d("fish","getMessageName="+msg.what); 226 | reset(); 227 | super.handleMessage(msg); 228 | } 229 | }; 230 | 231 | //add 232 | private boolean mBottomIsScrolling; 233 | /**第一次测量标志位*/ 234 | private boolean firstMeasure ; 235 | 236 | /**刷新动画监听,在动画结束的时候判断,如果还在刷新就继续显示circle,*/ 237 | private AnimationListener mRefreshListenerBottom = new AnimationListener() { 238 | @Override 239 | public void onAnimationStart(Animation animation) { 240 | } 241 | 242 | @Override 243 | public void onAnimationRepeat(Animation animation) { 244 | } 245 | 246 | @Override 247 | public void onAnimationEnd(Animation animation) { 248 | if (mRefreshingBottom) { 249 | // Make sure the progress view is fully visible 250 | mProgressBottom.setAlpha(MAX_ALPHA); 251 | mProgressBottom.start(); 252 | if (mNotifyBottom) { 253 | if (mListenerBottom != null) { 254 | mListenerBottom.onBottomRefresh(); 255 | Log.e("fish","mListenerBottom!= null"); 256 | }else{ 257 | Log.e("fish","mListenerBottom == null"); 258 | } 259 | } 260 | mCurrentTargetOffsetBottom = mCircleViewBottom.getBottom(); 261 | } else { 262 | reset(); 263 | } 264 | } 265 | }; 266 | 267 | /**刷新动画监听,在动画结束的时候判断,如果还在刷新就继续显示circle,*/ 268 | private AnimationListener mRefreshListener = new AnimationListener() { 269 | @Override 270 | public void onAnimationStart(Animation animation) { 271 | } 272 | 273 | @Override 274 | public void onAnimationRepeat(Animation animation) { 275 | } 276 | 277 | @Override 278 | public void onAnimationEnd(Animation animation) { 279 | if (mRefreshing) { 280 | mProgress.setAlpha(MAX_ALPHA); 281 | mProgress.start(); 282 | if (mNotify) { 283 | if (mListener != null) { 284 | mListener.onRefresh(); 285 | } 286 | } 287 | mCurrentTargetOffsetTop = mCircleView.getTop(); 288 | } else { 289 | reset(); 290 | } 291 | } 292 | }; 293 | 294 | 295 | 296 | /**重设状态,将circle隐藏起来*/ 297 | void reset() { 298 | mCircleView.clearAnimation(); 299 | 300 | mCircleViewBottom.clearAnimation(); 301 | 302 | mProgress.stop(); 303 | mCircleView.setVisibility(View.GONE); 304 | mProgressBottom.stop(); 305 | mCircleViewBottom.setVisibility(View.GONE); 306 | 307 | setColorViewAlpha(MAX_ALPHA); 308 | if (mScale) { 309 | setAnimationProgress(0 /* animation complete and view is hidden */); 310 | } else { 311 | setTargetOffsetTopAndBottom(mOriginalOffsetTop - mCurrentTargetOffsetTop,//往回推 312 | true /* requires update */); 313 | setTargetOffsetTopAndBottomForBottom(mOriginalOffsetBottom - mCurrentTargetOffsetBottom,true); 314 | } 315 | mCurrentTargetOffsetTop = mCircleView.getTop(); 316 | mCurrentTargetOffsetBottom = mCircleViewBottom.getBottom(); 317 | } 318 | 319 | @Override 320 | public void setEnabled(boolean enabled) { 321 | super.setEnabled(enabled); 322 | if (!enabled) { 323 | reset(); 324 | } 325 | } 326 | 327 | @Override 328 | protected void onDetachedFromWindow() { 329 | super.onDetachedFromWindow(); 330 | reset(); 331 | } 332 | /**设置透明度,包括circleView和透明度*/ 333 | private void setColorViewAlpha(int targetAlpha) { 334 | mCircleView.getBackground().setAlpha(targetAlpha); 335 | mProgress.setAlpha(targetAlpha); 336 | 337 | 338 | mCircleViewBottom.getBackground().setAlpha(targetAlpha); 339 | mProgressBottom.setAlpha(targetAlpha); 340 | } 341 | 342 | /** 343 | * The refresh indicator starting and resting position is always positioned 344 | * near the top of the refreshing content. This position is a consistent 345 | * location, but can be adjusted in either direction based on whether or not 346 | * there is a toolbar or actionbar present. 347 | *
348 | * Note: Calling this will reset the position of the refresh indicator to
349 | * start
.
350 | *
这个方法就是自己设置circle的起始结束位置
361 | */ 362 | public void setProgressViewOffset(boolean scale, int start, int end) { 363 | mScale = scale; 364 | mOriginalOffsetTop = start; 365 | mSpinnerOffsetEnd = end; 366 | mUsingCustomStart = true; 367 | reset(); 368 | mRefreshing = false; 369 | } 370 | 371 | /** 372 | * @return The offset in pixels from the top of this view at which the progress spinner should 373 | * appear. 374 | */ 375 | public int getProgressViewStartOffset() { 376 | return mOriginalOffsetTop; 377 | } 378 | 379 | /** 380 | * @return The offset in pixels from the top of this view at which the progress spinner should 381 | * come to rest after a successful swipe gesture. 382 | */ 383 | public int getProgressViewEndOffset() { 384 | return mSpinnerOffsetEnd; 385 | } 386 | 387 | /** 388 | * The refresh indicator resting position is always positioned near the top 389 | * of the refreshing content. This position is a consistent location, but 390 | * can be adjusted in either direction based on whether or not there is a 391 | * toolbar or actionbar present. 392 | * 393 | * @param scale Set to true if there is no view at a higher z-order than where the progress 394 | * spinner is set to appear. Setting it to true will cause indicator to be scaled 395 | * up rather than clipped. 396 | * @param end The offset in pixels from the top of this view at which the 397 | * progress spinner should come to rest after a successful swipe 398 | * gesture. 399 | */ 400 | public void setProgressViewEndTarget(boolean scale, int end) { 401 | mSpinnerOffsetEnd = end; 402 | mScale = scale; 403 | mCircleView.invalidate(); 404 | mCircleViewBottom.invalidate(); 405 | } 406 | 407 | /** 408 | * One of DEFAULT, or LARGE. 409 | * 设置progressBar的size大小,他是固定的,只有大和一般两种大小 410 | */ 411 | public void setSize(int size) { 412 | if (size != MaterialProgressDrawable.LARGE && size != MaterialProgressDrawable.DEFAULT) { 413 | return; 414 | } 415 | final DisplayMetrics metrics = getResources().getDisplayMetrics(); 416 | if (size == MaterialProgressDrawable.LARGE) { 417 | mCircleDiameter = (int) (CIRCLE_DIAMETER_LARGE * metrics.density); 418 | } else { 419 | mCircleDiameter = (int) (CIRCLE_DIAMETER * metrics.density); 420 | } 421 | // force the bounds of the progress circle inside the circle view to 422 | // update by setting it to null before updating its size and then 423 | // re-setting it 424 | mCircleView.setImageDrawable(null); 425 | mCircleViewBottom.setImageDrawable(null); 426 | mProgress.updateSizes(size); 427 | mCircleView.setImageDrawable(mProgress); 428 | mProgressBottom.updateSizes(size); 429 | // mProgressBottom.showArrow(true); 430 | // mProgressBottom.setRotation(0.5f); 431 | mCircleViewBottom.setImageDrawable(mProgressBottom); 432 | 433 | } 434 | 435 | /** 436 | * Simple constructor to use when creating a GZoomSwifrefresh from code. 437 | * 438 | * @param context 439 | */ 440 | public GZoomSwifrefresh(Context context) { 441 | this(context, null); 442 | } 443 | 444 | /** 445 | * Constructor that is called when inflating GZoomSwifrefresh from XML. 446 | * 447 | * @param context 448 | * @param attrs 449 | */ 450 | public GZoomSwifrefresh(Context context, AttributeSet attrs) { 451 | super(context, attrs); 452 | 453 | mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); 454 | 455 | mMediumAnimationDuration = getResources().getInteger( 456 | android.R.integer.config_mediumAnimTime); 457 | /**因为要绘制小圈圈,所以是需要绘制的*/ 458 | setWillNotDraw(false); 459 | mDecelerateInterpolator = new DecelerateInterpolator(DECELERATE_INTERPOLATION_FACTOR); 460 | 461 | final DisplayMetrics metrics = getResources().getDisplayMetrics(); 462 | mCircleDiameter = (int) (CIRCLE_DIAMETER * metrics.density); 463 | 464 | createProgressView(); 465 | //按照顺序绘制 466 | ViewCompat.setChildrenDrawingOrderEnabled(this, true); 467 | // the absolute offset has to take into account that the circle starts at an offset 468 | mSpinnerOffsetEnd = (int) (DEFAULT_CIRCLE_TARGET * metrics.density); 469 | //这是错的,只是先初始化 470 | mBottomSpinnerOffsetEnd = mSpinnerOffsetEnd; 471 | 472 | mTotalDragDistance = mSpinnerOffsetEnd; 473 | mNestedScrollingParentHelper = new NestedScrollingParentHelper(this); 474 | 475 | mNestedScrollingChildHelper = new NestedScrollingChildHelper(this); 476 | setNestedScrollingEnabled(true); 477 | 478 | mOriginalOffsetTop = mCurrentTargetOffsetTop = -mCircleDiameter; 479 | moveToStart(1.0f); 480 | //这个也是错的,只是先初始化一下 481 | mOriginalOffsetBottom = mCircleDiameter; 482 | mCurrentTargetOffsetBottom = mCircleDiameter; 483 | 484 | 485 | final TypedArray a = context.obtainStyledAttributes(attrs, LAYOUT_ATTRS); 486 | setEnabled(a.getBoolean(0, true)); 487 | a.recycle(); 488 | 489 | firstMeasure = true; 490 | WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE); 491 | int windowHeight = wm.getDefaultDisplay().getHeight(); 492 | Log.e("fish","init+-+windowHeight=="+windowHeight); 493 | mOriginalOffsetBottom = windowHeight - mCircleDiameter/2; 494 | Log.e("fish","init+-+mOriginalOffsetBottom=="+mOriginalOffsetBottom); 495 | 496 | mCurrentTargetOffsetBottom = mOriginalOffsetBottom; 497 | mSpinnerBottomOffsetEnd = mOriginalOffsetBottom - mSpinnerOffsetEnd; 498 | 499 | 500 | } 501 | @Override 502 | protected int getChildDrawingOrder(int childCount, int i) { 503 | if (mCircleViewIndex < 0) { 504 | return i; 505 | } else if (i == childCount - 1) { 506 | // Draw the selected child last 507 | return mCircleViewIndex; 508 | } else if (i >= mCircleViewIndex) { 509 | // Move the children after the selected child earlier one 510 | return i + 1; 511 | } else { 512 | // Keep the children before the selected child the same 513 | return i; 514 | } 515 | //return super.getChildDrawingOrder(childCount,i); 516 | } 517 | /**创造ProgressView,给他们设置ImageDrawable*/ 518 | private void createProgressView() { 519 | mCircleView = new CircleImageView(getContext(), CIRCLE_BG_LIGHT); 520 | 521 | mCircleViewBottom = new CircleImageView(getContext(),CIRCLE_BG_LIGHT); 522 | 523 | mProgress = new MaterialProgressDrawable(getContext(), this); 524 | mProgress.setBackgroundColor(CIRCLE_BG_LIGHT); 525 | mCircleView.setImageDrawable(mProgress); 526 | mCircleView.setVisibility(View.GONE); 527 | addView(mCircleView); 528 | 529 | mProgressBottom = new MaterialProgressDrawable(getContext(),this); 530 | mProgressBottom.setBackgroundColor(CIRCLE_BG_LIGHT); 531 | mCircleViewBottom.setImageDrawable(mProgressBottom); 532 | // mProgressBottom.showArrow(true); 533 | // mProgressBottom.setRotation(0.5f); 534 | // mProgressBottom.start(); 535 | mCircleViewBottom.setVisibility(View.GONE); 536 | addView(mCircleViewBottom); 537 | //addView(mCircleView,2); 538 | } 539 | 540 | /** 541 | * Set the listener to be notified when a refresh is triggered via the swipe 542 | * gesture. 543 | */ 544 | public void setOnRefreshListener(OnRefreshListener listener) { 545 | mListener = listener; 546 | } 547 | 548 | 549 | public void setOnBottomRefreshListenrer(OnBottomRefreshListener listener) 550 | { 551 | mListenerBottom = listener; 552 | } 553 | 554 | 555 | /** 556 | * Pre API 11, alpha is used to make the progress circle appear instead of scale. 557 | * 这里是判断机型版本有没有大于安卓3.0 558 | * 小于返回true 559 | */ 560 | private boolean isAlphaUsedForScale() { 561 | return android.os.Build.VERSION.SDK_INT < 11;//11 是Android3.0 562 | } 563 | 564 | /** 565 | * Notify the widget that refresh state has changed. Do not call this when 566 | * refresh is triggered by a swipe gesture. 567 | * 这个方法是暴露给外部手动停止滑动的 568 | * @param refreshing Whether or not the view should show refresh progress. 569 | */ 570 | public void setRefreshing(boolean refreshing) { 571 | if (refreshing && mRefreshing != refreshing) { 572 | // scale and show 573 | mRefreshing = refreshing; 574 | int endTarget = 0; 575 | if (!mUsingCustomStart) { 576 | endTarget = mSpinnerOffsetEnd + mOriginalOffsetTop; 577 | } else { 578 | endTarget = mSpinnerOffsetEnd; 579 | } 580 | setTargetOffsetTopAndBottom(endTarget - mCurrentTargetOffsetTop, 581 | true /* requires update */); 582 | mNotify = false; 583 | mNotifyBottom = false; 584 | startScaleUpAnimation(mRefreshListener); 585 | } else { 586 | setRefreshing(refreshing, false /* notify */); 587 | } 588 | } 589 | 590 | public void setBottomRefreshing(boolean refreshing) { 591 | if (refreshing && mRefreshingBottom != refreshing) { 592 | // scale and show 593 | mRefreshingBottom = refreshing; 594 | int endTarget = 0; 595 | if (!mUsingCustomStart) { 596 | endTarget = (int) (mOriginalOffsetBottom - mSpinnerOffsetEnd*1.5); 597 | } else { 598 | endTarget = mSpinnerOffsetEnd; 599 | } 600 | setTargetOffsetTopAndBottomForBottom(endTarget - mCurrentTargetOffsetBottom, 601 | true /* requires update */); 602 | mNotifyBottom = false; 603 | // mNotifyBottom = false; 604 | startScaleUpAnimationBottom(mRefreshListenerBottom); 605 | } else { 606 | setRefreshingBottom(refreshing, false /* notify */); 607 | } 608 | } 609 | 610 | 611 | /**开始顶部 CircleView的动画*/ 612 | private void startScaleUpAnimation(AnimationListener listener) { 613 | mCircleView.setVisibility(View.VISIBLE); 614 | if (android.os.Build.VERSION.SDK_INT >= 11) { 615 | // Pre API 11, alpha is used in place of scale up to show the 616 | // progress circle appearing. 617 | // Don't adjust the alpha during appearance otherwise. 618 | mProgress.setAlpha(MAX_ALPHA); 619 | } 620 | mScaleAnimation = new Animation() { 621 | @Override 622 | public void applyTransformation(float interpolatedTime, Transformation t) { 623 | setAnimationProgress(interpolatedTime);//interpolatedTime 从小到大,0.0-1 624 | } 625 | }; 626 | mScaleAnimation.setDuration(mMediumAnimationDuration); 627 | if (listener != null) { 628 | mCircleView.setAnimationListener(listener); 629 | } 630 | mCircleView.clearAnimation(); 631 | mCircleView.startAnimation(mScaleAnimation); 632 | } 633 | 634 | /**开始底部 CircleView的动画*/ 635 | private void startScaleUpAnimationBottom(AnimationListener listener) { 636 | mCircleViewBottom.setVisibility(View.VISIBLE); 637 | if (android.os.Build.VERSION.SDK_INT >= 11) { 638 | // Pre API 11, alpha is used in place of scale up to show the 639 | // progress circle appearing. 640 | // Don't adjust the alpha during appearance otherwise. 641 | mProgressBottom.setAlpha(MAX_ALPHA); 642 | } 643 | mScaleAnimation = new Animation() { 644 | @Override 645 | public void applyTransformation(float interpolatedTime, Transformation t) { 646 | setAnimationProgressBottom(interpolatedTime);//interpolatedTime 从小到大,0.0-1 647 | } 648 | }; 649 | mScaleAnimation.setDuration(mMediumAnimationDuration); 650 | if (listener != null) { 651 | mCircleViewBottom.setAnimationListener(listener); 652 | } 653 | mCircleViewBottom.clearAnimation(); 654 | mCircleViewBottom.startAnimation(mScaleAnimation); 655 | } 656 | 657 | 658 | /** 659 | * Pre API 11, this does an alpha animation. 660 | * @param progress 661 | * 设置CircleView的进程 662 | */ 663 | void setAnimationProgress(float progress) { 664 | if (isAlphaUsedForScale()) { 665 | setColorViewAlpha((int) (progress * MAX_ALPHA)); 666 | Log.e(LOG_TAG,"setAnimationProgress,setColorViewAlpha"); 667 | } else { 668 | //不出所料,我走的是这个方法 669 | ViewCompat.setScaleX(mCircleView, progress); 670 | ViewCompat.setScaleY(mCircleView, progress); 671 | Log.e(LOG_TAG,"setAnimationProgress,ViewCompat.setScale,progress=="+progress); 672 | } 673 | } 674 | 675 | 676 | void setAnimationProgressBottom(float progress) { 677 | if (isAlphaUsedForScale()) { 678 | setColorViewAlpha((int) (progress * MAX_ALPHA)); 679 | Log.e(LOG_TAG,"setAnimationProgress,setColorViewAlpha"); 680 | } else { 681 | //不出所料,我走的是这个方法 682 | ViewCompat.setScaleX(mCircleViewBottom, progress); 683 | ViewCompat.setScaleY(mCircleViewBottom, progress); 684 | Log.e(LOG_TAG,"setAnimationProgressBottom,ViewCompat.setScale,progress=="+progress); 685 | } 686 | } 687 | 688 | /**设置refreshing的状态,如果不相应就相应的改变,应该是为了设置刷不刷新*/ 689 | private void setRefreshing(boolean refreshing, final boolean notify) { 690 | if (mRefreshing != refreshing) { 691 | mNotify = notify; 692 | ensureTarget(); 693 | mRefreshing = refreshing; 694 | if (mRefreshing) {//如果标志位正确,就启动刷新动画,将circle一步步移到中间那个位置 695 | animateOffsetToCorrectPosition(mCurrentTargetOffsetTop, mRefreshListener); 696 | } else { 697 | startScaleDownAnimation(mRefreshListener); 698 | } 699 | } 700 | } 701 | 702 | 703 | 704 | /**设置refreshing的状态,如果不相应就相应的改变,应该是为了设置刷不刷新*/ 705 | private void setRefreshingBottom(boolean refreshing, final boolean notify) { 706 | if (mRefreshingBottom != refreshing) { 707 | mNotifyBottom = notify; 708 | ensureTarget(); 709 | mRefreshingBottom = refreshing; 710 | if (mRefreshingBottom) {//如果标志位正确,就启动刷新动画,将circle一步步移到中间那个位置 711 | animateOffsetToCorrectPositionBottom(mCurrentTargetOffsetBottom, mRefreshListenerBottom);// 712 | //add 713 | //reset(); 714 | //不行,加上去之后转都不转 715 | } else { 716 | startScaleDownAnimationBottom(mRefreshListenerBottom); 717 | } 718 | } 719 | } 720 | 721 | 722 | 723 | 724 | /**开始mScaleDownAnimation动画,还有对于circle进程的改变 725 | * 就是在你手指拖动的过程中改变 726 | * 727 | *说白了就是对circleBottom进行倒着转动画
728 | * */ 729 | void startScaleDownAnimation(AnimationListener listener) { 730 | mScaleDownAnimation = new Animation() { 731 | @Override 732 | public void applyTransformation(float interpolatedTime, Transformation t) { 733 | setAnimationProgress(1 - interpolatedTime);//倒着转? 734 | } 735 | }; 736 | mScaleDownAnimation.setDuration(SCALE_DOWN_DURATION); 737 | mCircleView.setAnimationListener(listener); 738 | mCircleView.clearAnimation(); 739 | mCircleView.startAnimation(mScaleDownAnimation); 740 | } 741 | 742 | /**开始mScaleDownAnimation动画,还有对于circle进程的改变 743 | * 就是在你手指拖动的过程中改变 744 | * 745 | * bottomn 746 | * */ 747 | void startScaleDownAnimationBottom(AnimationListener listener) { 748 | mScaleDownAnimation = new Animation() { 749 | @Override 750 | public void applyTransformation(float interpolatedTime, Transformation t) { 751 | setAnimationProgressBottom(1 - interpolatedTime);//倒着转? 752 | } 753 | }; 754 | mScaleDownAnimation.setDuration(SCALE_DOWN_DURATION); 755 | mCircleViewBottom.setAnimationListener(listener); 756 | mCircleViewBottom.clearAnimation(); 757 | mCircleViewBottom.startAnimation(mScaleDownAnimation); 758 | } 759 | 760 | /**开始透明度的变化,最终76 761 | *for top CircleView
762 | * */ 763 | private void startProgressAlphaStartAnimation() { 764 | mAlphaStartAnimation = startAlphaAnimation(mProgress.getAlpha(), STARTING_PROGRESS_ALPHA); 765 | } 766 | 767 | /**开始透明度的变化,最终76 768 | *for bottom CircleView
769 | * */ 770 | private void startProgressAlphaStartAnimationBottom() { 771 | mAlphaStartAnimationBottom = startAlphaAnimationBottom(mProgress.getAlpha(), STARTING_PROGRESS_ALPHA); 772 | } 773 | 774 | 775 | /**开启到最大的透明度,255 776 | *for top CircleView
*/ 777 | private void startProgressAlphaMaxAnimation() { 778 | mAlphaMaxAnimation = startAlphaAnimation(mProgress.getAlpha(), MAX_ALPHA); 779 | } 780 | 781 | /**开启到最大的透明度,255 782 | *for top CircleView
*/ 783 | private void startProgressAlphaMaxAnimationBottom() { 784 | mAlphaMaxAnimationBottom = startAlphaAnimationBottom(mProgressBottom.getAlpha(), MAX_ALPHA); 785 | } 786 | 787 | 788 | /**透明度动画。两个参数分别是开始透明度和结束,这样可能效果不会太突兀? 789 | *for top circle view
790 | * */ 791 | private Animation startAlphaAnimation(final int startingAlpha, final int endingAlpha) { 792 | // Pre API 11, alpha is used in place of scale. Don't also use it to 793 | // show the trigger point. 794 | if (mScale && isAlphaUsedForScale()) { 795 | return null; 796 | } 797 | Animation alpha = new Animation() { 798 | @Override 799 | public void applyTransformation(float interpolatedTime, Transformation t) { 800 | mProgress.setAlpha( 801 | (int) (startingAlpha + ((endingAlpha - startingAlpha) * interpolatedTime))); 802 | } 803 | }; 804 | alpha.setDuration(ALPHA_ANIMATION_DURATION); 805 | // Clear out the previous animation listeners. 806 | mCircleView.setAnimationListener(null); 807 | mCircleView.clearAnimation(); 808 | mCircleView.startAnimation(alpha); 809 | return alpha; 810 | } 811 | 812 | 813 | /**透明度动画。两个参数分别是开始透明度和结束,这样可能效果不会太突兀? 814 | *for top circle view
815 | * */ 816 | private Animation startAlphaAnimationBottom(final int startingAlpha, final int endingAlpha) { 817 | // Pre API 11, alpha is used in place of scale. Don't also use it to 818 | // show the trigger point. 819 | if (mScale && isAlphaUsedForScale()) { 820 | return null; 821 | } 822 | Animation alpha = new Animation() { 823 | @Override 824 | public void applyTransformation(float interpolatedTime, Transformation t) { 825 | mProgress.setAlpha( 826 | (int) (startingAlpha + ((endingAlpha - startingAlpha) * interpolatedTime))); 827 | } 828 | }; 829 | alpha.setDuration(ALPHA_ANIMATION_DURATION); 830 | // Clear out the previous animation listeners. 831 | mCircleViewBottom.setAnimationListener(null); 832 | mCircleViewBottom.clearAnimation(); 833 | mCircleViewBottom.startAnimation(alpha); 834 | return alpha; 835 | } 836 | 837 | 838 | 839 | /** 840 | * @deprecated Use {@link #setProgressBackgroundColorSchemeResource(int)} 841 | */ 842 | @Deprecated 843 | public void setProgressBackgroundColor(int colorRes) { 844 | setProgressBackgroundColorSchemeResource(colorRes); 845 | } 846 | 847 | /** 848 | * Set the background color of the progress spinner disc. 849 | * 850 | * @param colorRes Resource id of the color. 851 | */ 852 | public void setProgressBackgroundColorSchemeResource(@ColorRes int colorRes) { 853 | setProgressBackgroundColorSchemeColor(ContextCompat.getColor(getContext(), colorRes)); 854 | } 855 | 856 | /** 857 | * Set the background color of the progress spinner disc. 858 | * 859 | * @param color 860 | */ 861 | public void setProgressBackgroundColorSchemeColor(@ColorInt int color) { 862 | mCircleView.setBackgroundColor(color); 863 | mProgress.setBackgroundColor(color); 864 | } 865 | 866 | /** 867 | * @deprecated Use {@link #setColorSchemeResources(int...)} 868 | */ 869 | @Deprecated 870 | public void setColorScheme(@ColorInt int... colors) { 871 | setColorSchemeResources(colors); 872 | } 873 | 874 | /** 875 | * Set the color resources used in the progress animation from color resources. 876 | * The first color will also be the color of the bar that grows in response 877 | * to a user swipe gesture. 878 | * 879 | * @param colorResIds 880 | */ 881 | public void setColorSchemeResources( int... colorResIds) {//这里被我删了注解 882 | final Context context = getContext(); 883 | int[] colorRes = new int[colorResIds.length]; 884 | for (int i = 0; i < colorResIds.length; i++) { 885 | colorRes[i] = ContextCompat.getColor(context, colorResIds[i]); 886 | } 887 | setColorSchemeColors(colorRes); 888 | } 889 | 890 | /** 891 | * Set the colors used in the progress animation. The first 892 | * color will also be the color of the bar that grows in response to a user 893 | * swipe gesture. 894 | * 895 | * @param colors 896 | */ 897 | public void setColorSchemeColors(@ColorInt int... colors) { 898 | ensureTarget(); 899 | mProgress.setColorSchemeColors(colors); 900 | } 901 | /**设置底部circle加载颜色*/ 902 | public void setBottomColorSchemeColors(int ...colors) 903 | { 904 | ensureTarget(); 905 | mProgressBottom.setColorSchemeColors(colors); 906 | } 907 | 908 | 909 | /** 910 | * @return Whether the SwipeRefreshWidget is actively showing refresh 911 | * progress. 912 | */ 913 | public boolean isRefreshing() { 914 | return mRefreshing; 915 | } 916 | 917 | /**这个就是要找到滑动的目标,也就是mTarget 918 | * ,他就是找到在子view中接着上面刷新圆环的第一个,也就是我们放在格局中的第一个 919 | * */ 920 | private void ensureTarget() { 921 | // Don't bother getting the parent height if the parent hasn't been laid 922 | // out yet. 923 | if (mTarget == null) {//手势目标 924 | for (int i = 0; i < getChildCount(); i++) { 925 | View child = getChildAt(i); 926 | if (!child.equals(mCircleView)&&!child.equals(mCircleViewBottom)) { 927 | mTarget = child; 928 | Log.e(LOG_TAG,"ensureTarget,mTarget=="+mTarget.toString()); 929 | break; 930 | } 931 | } 932 | } 933 | } 934 | 935 | /** 936 | * Set the distance to trigger a sync in dips 937 | *设置拖动的极限距离 938 | * @param distance 939 | */ 940 | public void setDistanceToTriggerSync(int distance) { 941 | mTotalDragDistance = distance; 942 | } 943 | 944 | @Override 945 | protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 946 | final int width = getMeasuredWidth(); 947 | final int height = getMeasuredHeight(); 948 | if (getChildCount() == 0) { 949 | return; 950 | } 951 | if (mTarget == null) { 952 | ensureTarget(); 953 | } 954 | if (mTarget == null) { 955 | return; 956 | } 957 | final View child = mTarget; 958 | final int childLeft = getPaddingLeft(); 959 | final int childTop = getPaddingTop(); 960 | final int childWidth = width - getPaddingLeft() - getPaddingRight(); 961 | final int childHeight = height - getPaddingTop() - getPaddingBottom(); 962 | child.layout(childLeft, childTop, childLeft + childWidth, childTop + childHeight); 963 | Log.e(LOG_TAG,"onLayout,childTop=="+childTop+",childHeight"+childHeight); 964 | //草,就是因为这个,才会滚不回去,在重绘的过程中会进行新一次的onLayout 965 | /* if(firstMeasure) {//其实已经没有必要摆在这里了 966 | // mOriginalOffsetBottom = height + mCircleDiameter; 967 | //mOriginalOffsetBottom = 1250; 968 | mCurrentTargetOffsetBottom = mOriginalOffsetBottom; 969 | mSpinnerBottomOffsetEnd = mOriginalOffsetBottom - mSpinnerOffsetEnd; 970 | firstMeasure = false; 971 | Log.e("fish", "firstMeasure,mCurrentTargetOffsetBottom==" + mCurrentTargetOffsetBottom + ",mSpinnerBottomOffsetEnd==" + mSpinnerBottomOffsetEnd); 972 | Log.e("fish", "firstMeasure,mOriginalOffsetBottom==" + mOriginalOffsetBottom); 973 | }*/ 974 | 975 | int circleWidth = mCircleView.getMeasuredWidth(); 976 | int circleHeight = mCircleView.getMeasuredHeight(); 977 | mCircleView.layout((width / 2 - circleWidth / 2), mCurrentTargetOffsetTop, 978 | (width / 2 + circleWidth / 2), mCurrentTargetOffsetTop + circleHeight); 979 | // mCircleView.layout((width / 2 - circleWidth / 2), childHeight/2, 980 | // (width / 2 + circleWidth / 2), childHeight/2 + circleHeight); 981 | 982 | // mCurrentTargetOffsetBottom = mOriginalOffsetBottom; 983 | int circleBottomWidth = mCircleViewBottom.getMeasuredWidth(); 984 | int circleBottomHeight = mCircleViewBottom.getMeasuredHeight(); 985 | mCircleViewBottom.setVisibility(View.VISIBLE); 986 | mCircleViewBottom.layout((width / 2 - circleBottomWidth / 2), mCurrentTargetOffsetBottom - circleBottomHeight, 987 | (width / 2 + circleBottomWidth / 2), mCurrentTargetOffsetBottom); 988 | // mProgressBottom.start(); 989 | } 990 | 991 | @Override 992 | public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 993 | super.onMeasure(widthMeasureSpec, heightMeasureSpec); 994 | if (mTarget == null) { 995 | ensureTarget(); 996 | } 997 | if (mTarget == null) { 998 | return; 999 | } 1000 | mTarget.measure(MeasureSpec.makeMeasureSpec( 1001 | getMeasuredWidth() - getPaddingLeft() - getPaddingRight(), 1002 | MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec( 1003 | getMeasuredHeight() - getPaddingTop() - getPaddingBottom(), MeasureSpec.EXACTLY)); 1004 | mCircleView.measure(MeasureSpec.makeMeasureSpec(mCircleDiameter, MeasureSpec.EXACTLY), 1005 | MeasureSpec.makeMeasureSpec(mCircleDiameter, MeasureSpec.EXACTLY)); 1006 | mCircleViewIndex = -1; 1007 | // Get the index of the circleview. 1008 | for (int index = 0; index < getChildCount(); index++) { 1009 | if (getChildAt(index) == mCircleView) { 1010 | mCircleViewIndex = index; 1011 | Log.e(LOG_TAG,"mCircleViewIndex=="+mCircleViewIndex); 1012 | break; 1013 | } 1014 | } 1015 | 1016 | mCircleViewBottom.measure(MeasureSpec.makeMeasureSpec(mCircleDiameter, MeasureSpec.EXACTLY), 1017 | MeasureSpec.makeMeasureSpec(mCircleDiameter, MeasureSpec.EXACTLY)); 1018 | mCircleViewBottomIndex = -1; 1019 | // Get the index of the circleview. 1020 | for (int index = 0; index < getChildCount(); index++) { 1021 | if (getChildAt(index) == mCircleViewBottom) { 1022 | mCircleViewBottomIndex = index; 1023 | Log.e(LOG_TAG,"mCircleViewBottomIndex=="+mCircleViewBottomIndex); 1024 | break; 1025 | } 1026 | } 1027 | 1028 | 1029 | } 1030 | 1031 | /** 1032 | * Get the diameter of the progress circle that is displayed as part of the 1033 | * swipe to refresh layout. 1034 | * 1035 | * @return Diameter in pixels of the progress circle view. 1036 | *获取圆环直径
1037 | */ 1038 | public int getProgressCircleDiameter() { 1039 | return mCircleDiameter; 1040 | } 1041 | 1042 | /** 1043 | * @return Whether it is possible for the child view of this layout to 1044 | * scroll up. Override this if the child view is a custom view. 1045 | */ 1046 | public boolean canChildScrollUp() { 1047 | if (mChildScrollUpCallback != null) { 1048 | Log.e("fish","canChildScrollUp;mChildScrollUpCallback != null"); 1049 | return mChildScrollUpCallback.canChildScrollUp(this, mTarget); 1050 | 1051 | } 1052 | if (android.os.Build.VERSION.SDK_INT < 14) { 1053 | Log.e("fish","canChildScrollUp;Build.VERSION.SDK_INT < 14"); 1054 | if (mTarget instanceof AbsListView) { 1055 | Log.e("fish","canChildScrollUp;mTarget instanceof AbsListView"); 1056 | final AbsListView absListView = (AbsListView) mTarget; 1057 | return absListView.getChildCount() > 0 1058 | && (absListView.getFirstVisiblePosition() > 0 || absListView.getChildAt(0) 1059 | .getTop() < absListView.getPaddingTop()); 1060 | } else { 1061 | Log.e("fish","canChildScrollUp;mTarget !instanceof AbsListView"); 1062 | return ViewCompat.canScrollVertically(mTarget, -1) || mTarget.getScrollY() > 0; 1063 | } 1064 | } else { 1065 | // Log.e("fish","canChildScrollUp;android.os.Build.VERSION.SDK_INT > 14"); 1066 | //这个可以用,判断是否可以向下拉 1067 | // boolean re = ViewCompat.canScrollVertically(mTarget,1); 1068 | //Log.e("fish","canChildScrollDown?"+re); 1069 | return ViewCompat.canScrollVertically(mTarget, -1); 1070 | } 1071 | } 1072 | 1073 | 1074 | //自己写的方法 1075 | /**判断是否而已向下拉*/ 1076 | public boolean canChildScrollDown() 1077 | { 1078 | return ViewCompat.canScrollVertically(mTarget, 1); 1079 | } 1080 | 1081 | 1082 | /** 1083 | * Set a callback to override {@link GZoomSwifrefresh#canChildScrollUp()} method. Non-null 1084 | * callback will return the value provided by the callback and ignore all internal logic. 1085 | * @param callback Callback that should be called when canChildScrollUp() is called. 1086 | */ 1087 | public void setOnChildScrollUpCallback(@Nullable OnChildScrollUpCallback callback) { 1088 | mChildScrollUpCallback = callback; 1089 | } 1090 | 1091 | @Override 1092 | public boolean onInterceptTouchEvent(MotionEvent ev) { 1093 | //先确定滑动的目标对象 1094 | ensureTarget(); 1095 | 1096 | final int action = MotionEventCompat.getActionMasked(ev); 1097 | int pointerIndex; 1098 | //点击事件开始,将标志位清空 1099 | if (mReturningToStart && action == MotionEvent.ACTION_DOWN) { 1100 | mReturningToStart = false; 1101 | } 1102 | /**isEnabled 可用 1103 | * 1104 | * canChildScrollUp 子view还可以向上拉 1105 | * */ 1106 | if (!isEnabled() || mReturningToStart || canChildScrollUp() 1107 | || mRefreshing || mNestedScrollInProgress) { 1108 | // Fail fast if we're not in a state where a swipe is possible 1109 | return false; 1110 | } 1111 | 1112 | switch (action) { 1113 | case MotionEvent.ACTION_DOWN: 1114 | Log.e("fish","onInterceptTouchEvent,ACTION_DOWN"); 1115 | setTargetOffsetTopAndBottom(mOriginalOffsetTop - mCircleView.getTop(), true); 1116 | mActivePointerId = ev.getPointerId(0); 1117 | mIsBeingDragged = false; 1118 | 1119 | pointerIndex = ev.findPointerIndex(mActivePointerId); 1120 | if (pointerIndex < 0) { 1121 | return false; 1122 | } 1123 | mInitialDownY = ev.getY(pointerIndex); 1124 | break; 1125 | 1126 | case MotionEvent.ACTION_MOVE://这个move基本不触发 1127 | Log.e("fish","onInterceptTouchEvent,ACTION_MOVE"); 1128 | if (mActivePointerId == INVALID_POINTER) { 1129 | Log.e(LOG_TAG, "Got ACTION_MOVE event but don't have an active pointer id."); 1130 | return false; 1131 | } 1132 | 1133 | pointerIndex = ev.findPointerIndex(mActivePointerId); 1134 | if (pointerIndex < 0) { 1135 | return false; 1136 | } 1137 | final float y = ev.getY(pointerIndex); 1138 | startDragging(y); 1139 | break; 1140 | 1141 | case MotionEventCompat.ACTION_POINTER_UP: 1142 | Log.e("fish","onInterceptTouchEvent,MotionEventCompat.ACTION_POINTER_UP"); 1143 | onSecondaryPointerUp(ev); 1144 | break; 1145 | 1146 | case MotionEvent.ACTION_UP: 1147 | Log.e("fish","onInterceptTouchEvent,motionEvent.ACTION_UP"); 1148 | case MotionEvent.ACTION_CANCEL: 1149 | Log.e("fish","onInterceptTouchEvent,MotionEvent.ACTION_CANCEL"); 1150 | mIsBeingDragged = false; 1151 | mActivePointerId = INVALID_POINTER; 1152 | break; 1153 | } 1154 | 1155 | return mIsBeingDragged; 1156 | } 1157 | 1158 | @Override 1159 | public void requestDisallowInterceptTouchEvent(boolean b) { 1160 | // if this is a List < L or another view that doesn't support nested 1161 | // scrolling, ignore this request so that the vertical scroll event 1162 | // isn't stolen 1163 | if ((android.os.Build.VERSION.SDK_INT < 21 && mTarget instanceof AbsListView) 1164 | || (mTarget != null && !ViewCompat.isNestedScrollingEnabled(mTarget))) { 1165 | // Nope. 1166 | } else { 1167 | super.requestDisallowInterceptTouchEvent(b); 1168 | } 1169 | } 1170 | 1171 | // NestedScrollingParent 1172 | 1173 | @Override 1174 | public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) { 1175 | //target 发起滑动的字view,可以不是当前view的直接子view 1176 | //child 包含target的直接子view 1177 | //返回true表示要与target配套滑动,为true则下面的accepted也会被调用 1178 | //mReturningToStart是为了配合onTouchEvent的,这里我们不扩展 1179 | return isEnabled() && !mReturningToStart && !mRefreshing && !mRefreshingBottom //没有在刷新和返回途中 1180 | && (nestedScrollAxes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0;//竖直方向 1181 | } 1182 | 1183 | @Override 1184 | public void onNestedScrollAccepted(View child, View target, int axes) { 1185 | Log.e(LOG_TAG,"onNestedScrollAccepted,axes="+axes); 1186 | // Reset the counter of how much leftover scroll needs to be consumed. 1187 | mNestedScrollingParentHelper.onNestedScrollAccepted(child, target, axes); 1188 | // Dispatch up to the nested parent 1189 | startNestedScroll(axes & ViewCompat.SCROLL_AXIS_VERTICAL);//调用自己child接口的方法,实现向上传递 1190 | mTotalUnconsumed = 0; 1191 | // 1192 | mTotalUnconsumedBottom = 0; 1193 | mNestedScrollInProgress = true; 1194 | 1195 | } 1196 | 1197 | /** 1198 | * 在child开始滑动之前会通知parent的这个方法,看看能不能滑动dx dy这么多 1199 | *这里是回调,parent可以先于child进行滑动 1200 | * */ 1201 | @Override 1202 | public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) { 1203 | // If we are in the middle of consuming, a scroll, then we want to move the spinner back up 1204 | // before allowing the list to scroll 1205 | Log.e(LOG_TAG,"onNestedPreScroll,dy="+dy+",consume[1]=="+consumed[1]); 1206 | if (dy > 0 && mTotalUnconsumed > 0 && !mRefreshingBottom) {//向下拖dy小于0,所以这是为了处理拖circle到一半然后又缩回去的情况 1207 | if (dy > mTotalUnconsumed) {//拖动的很多,大于未消费的 1208 | consumed[1] = dy - (int) mTotalUnconsumed; 1209 | mTotalUnconsumed = 0; 1210 | } else {//拖动一点,我们全部用给自己 1211 | mTotalUnconsumed -= dy; 1212 | consumed[1] = dy; 1213 | } 1214 | moveSpinner(mTotalUnconsumed);//move 到这个位置 1215 | } 1216 | 1217 | //处理底部的,圆圈已经出来了之后它又向下拖 1218 | if(dy<0 && mTotalUnconsumedBottom > 0 && !mRefreshing) 1219 | { 1220 | Log.e("fish","dy<0 && mTotalUnconsumedBottom > 0+++dy=="+dy+",mTotalUnconsumedBottom=="+mTotalUnconsumedBottom); 1221 | if(-dy>mTotalUnconsumedBottom)//如果拖动的很多,就先给圆圈,然后还给子控件 1222 | { 1223 | consumed[1]= -(int) mTotalUnconsumedBottom; 1224 | mTotalUnconsumedBottom = 0; 1225 | mBottomIsScrolling = false; 1226 | }else{//否则,先给父控件 1227 | mTotalUnconsumedBottom +=dy; 1228 | consumed[1]=dy;//原来传回去的是正数,结果越滑越快。。。改成负数之后就对了,开心 1229 | } 1230 | moveBottomSpinner(mTotalUnconsumedBottom); 1231 | } 1232 | 1233 | 1234 | // If a client layout is using a custom start position自定义开始位置 for the circle 1235 | // view, they mean to hide it again before scrolling the child view 1236 | // If we get back to mTotalUnconsumed == 0 and there is more to go, hide 1237 | // the circle so it isn't exposed if its blocking content is moved 1238 | if (mUsingCustomStart && dy > 0 && mTotalUnconsumed == 0 1239 | && Math.abs(dy - consumed[1]) > 0) { 1240 | mCircleView.setVisibility(View.GONE); 1241 | } 1242 | 1243 | // Now let our nested parent consume the leftovers 1244 | /**计算它的父层的消耗,加上去*/ 1245 | final int[] parentConsumed = mParentScrollConsumed; 1246 | if (dispatchNestedPreScroll(dx - consumed[0], dy - consumed[1], parentConsumed, null)) {//父控件处理了才会返回true 1247 | consumed[0] += parentConsumed[0]; 1248 | consumed[1] += parentConsumed[1]; 1249 | } 1250 | } 1251 | 1252 | @Override 1253 | public int getNestedScrollAxes() { 1254 | return mNestedScrollingParentHelper.getNestedScrollAxes(); 1255 | } 1256 | 1257 | @Override 1258 | public void onStopNestedScroll(View target) { 1259 | mNestedScrollingParentHelper.onStopNestedScroll(target); 1260 | mNestedScrollInProgress = false; 1261 | Log.e(LOG_TAG,"onStopNestedScroll,mTotalUnconsumed="+mTotalUnconsumed); 1262 | // Finish the spinner for nested scrolling if we ever consumed any 1263 | // unconsumed nested scroll 1264 | if (mTotalUnconsumed > 0 && !mRefreshingBottom) { 1265 | finishSpinner(mTotalUnconsumed); 1266 | mTotalUnconsumed = 0; 1267 | } 1268 | if(mTotalUnconsumedBottom > 0 && !mRefreshing) 1269 | { 1270 | Log.e("fish","onStopNestedScroll,mTotalUnconsumedBottom > 0"); 1271 | finishSpinnerBottom(mTotalUnconsumedBottom); 1272 | mTotalUnconsumedBottom = 0; 1273 | mBottomIsScrolling = false; 1274 | } 1275 | // Dispatch up our nested parent 1276 | stopNestedScroll(); 1277 | } 1278 | /** 1279 | * 这里是后于child的滑动 1280 | * */ 1281 | @Override 1282 | public void onNestedScroll(final View target, final int dxConsumed, final int dyConsumed, 1283 | final int dxUnconsumed, final int dyUnconsumed) { 1284 | // Dispatch up to the nested parent first 1285 | dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, 1286 | mParentOffsetInWindow); 1287 | Log.e(LOG_TAG,"onNestedScroll,dyUnconsumed="+dyUnconsumed+"mParentOffsetInWindow[1]"+mParentOffsetInWindow[1]); 1288 | // This is a bit of a hack. Nested scrolling works from the bottom up, and as we are 1289 | // sometimes between two nested scrolling views, we need a way to be able to know when any 1290 | // nested scrolling parent has stopped handling events. We do that by using the 1291 | // 'offset in window 'functionality to see if we have been moved from the event. 1292 | // This is a decent indication of whether we should take over the event stream or not. 1293 | final int dy = dyUnconsumed + mParentOffsetInWindow[1]; 1294 | if (dy < 0 && !canChildScrollUp() && !mRefreshingBottom) {//向下拉 1295 | mTotalUnconsumed += Math.abs(dy); 1296 | moveSpinner(mTotalUnconsumed); 1297 | } else if(dy > 0 && !canChildScrollDown() && !mRefreshing) //向上拉 1298 | { 1299 | mTotalUnconsumedBottom +=dy; 1300 | moveBottomSpinner(mTotalUnconsumedBottom); 1301 | mBottomIsScrolling = true; 1302 | } 1303 | } 1304 | 1305 | // NestedScrollingChild 1306 | 1307 | /** 1308 | * 子,设置嵌套滑动是否可用 1309 | * */ 1310 | @Override 1311 | public void setNestedScrollingEnabled(boolean enabled) { 1312 | mNestedScrollingChildHelper.setNestedScrollingEnabled(enabled); 1313 | } 1314 | 1315 | /**嵌套滑动是否可用*/ 1316 | @Override 1317 | public boolean isNestedScrollingEnabled() { 1318 | return mNestedScrollingChildHelper.isNestedScrollingEnabled(); 1319 | } 1320 | 1321 | /** 1322 | * 开始嵌套滑动(子) 1323 | * axes表示方向,在ViewCompat.SCROLL_AXIS_HORIZONTAL横向 1324 | * 还有纵向 1325 | * 1326 | * 在这里面调用了mNestedScrollingChildHelper的startNestedScroll 1327 | * */ 1328 | @Override 1329 | public boolean startNestedScroll(int axes) { 1330 | return mNestedScrollingChildHelper.startNestedScroll(axes); 1331 | } 1332 | 1333 | /** 1334 | * 子,停止嵌套滑动 1335 | * */ 1336 | @Override 1337 | public void stopNestedScroll() { 1338 | mNestedScrollingChildHelper.stopNestedScroll(); 1339 | } 1340 | /** 1341 | * 是否有父view支持嵌套滑动 1342 | * */ 1343 | @Override 1344 | public boolean hasNestedScrollingParent() { 1345 | return mNestedScrollingChildHelper.hasNestedScrollingParent(); 1346 | } 1347 | /**在处理滑动之后调用 1348 | * @param dxConsumed x轴上 被消费的距离 1349 | * @param dyConsumed y轴上 被消费的距离 1350 | * @param dxUnconsumed x轴上 未被消费的距离 1351 | * @param dyUnconsumed y轴上 未被消费的距离 1352 | * @param offsetInWindow view 的移动距离 1353 | * */ 1354 | @Override 1355 | public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed, 1356 | int dyUnconsumed, int[] offsetInWindow) { 1357 | //事先拦截 1358 | 1359 | return mNestedScrollingChildHelper.dispatchNestedScroll(dxConsumed, dyConsumed, 1360 | dxUnconsumed, dyUnconsumed, offsetInWindow); 1361 | } 1362 | /** 1363 | * 一般在滑动之前调用, 在ontouch 中计算出滑动距离, 然后 调用改 方法, 就给支持的嵌套的父View 处理滑动事件 1364 | * @param dx x 轴上滑动的距离, 相对于上一次事件, 不是相对于 down事件的 那个距离 1365 | * @param dy y 轴上滑动的距离 1366 | * @param consumed 一个数组, 可以传 一个空的 数组, 表示 x 方向 或 y 方向的事件 是否有被消费 1367 | * @param offsetInWindow 支持嵌套滑动到额父View 消费 滑动事件后 导致 本 View 的移动距离 1368 | * @return 支持的嵌套的父View 是否处理了 滑动事件 1369 | */ 1370 | @Override 1371 | public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow) { 1372 | Log.e("fish","父:dispatchNestedPreScroll,dy="+dy); 1373 | //先拦截 1374 | if(mBottomIsScrolling && mTotalUnconsumedBottom>0 &&dy<0)//设置成功!但是子view还在动 1375 | { 1376 | Log.e("fish","父:dispatchNestedPreScroll,mTotalUnconsumedBottom="+mTotalUnconsumedBottom+",dy=="+dy); 1377 | if(-dy>=mTotalUnconsumedBottom)//向下拖动很大 1378 | { 1379 | moveBottomSpinner(mTotalUnconsumedBottom); 1380 | }else { 1381 | moveBottomSpinner(-dy); 1382 | mTotalUnconsumedBottom -= dy; 1383 | dy = 0; 1384 | } 1385 | } 1386 | return mNestedScrollingChildHelper.dispatchNestedPreScroll( 1387 | dx, dy, consumed, offsetInWindow); 1388 | } 1389 | 1390 | @Override 1391 | public boolean onNestedPreFling(View target, float velocityX, 1392 | float velocityY) { 1393 | return dispatchNestedPreFling(velocityX, velocityY); 1394 | } 1395 | 1396 | @Override 1397 | public boolean onNestedFling(View target, float velocityX, float velocityY, 1398 | boolean consumed) { 1399 | return dispatchNestedFling(velocityX, velocityY, consumed); 1400 | } 1401 | 1402 | @Override 1403 | public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed) { 1404 | return mNestedScrollingChildHelper.dispatchNestedFling(velocityX, velocityY, consumed); 1405 | } 1406 | 1407 | @Override 1408 | public boolean dispatchNestedPreFling(float velocityX, float velocityY) { 1409 | return mNestedScrollingChildHelper.dispatchNestedPreFling(velocityX, velocityY); 1410 | } 1411 | 1412 | private boolean isAnimationRunning(Animation animation) { 1413 | return animation != null && animation.hasStarted() && !animation.hasEnded(); 1414 | } 1415 | 1416 | 1417 | 1418 | 1419 | /** 1420 | * 进行实际circle的滑动绘制,包括颜色等设置 1421 | * 1422 | * @param overscrollTop 滑动的绝对值 1423 | * */ 1424 | private void moveBottomSpinner(float overscrollTop) { 1425 | mProgressBottom.showArrow(true); 1426 | float originalDragPercent = overscrollTop / mTotalDragDistance; 1427 | float dragPercent = Math.min(1f, Math.abs(originalDragPercent)); 1428 | float adjustedPercent = (float) Math.max(dragPercent - .4, 0) * 5 / 3;//这个不理解 1429 | float extraOS = Math.abs(overscrollTop) - mTotalDragDistance;//这样不就是负的吗//就是负的//可以是正的 1430 | float slingshotDist = mUsingCustomStart ? mSpinnerOffsetEnd - mOriginalOffsetTop 1431 | : mSpinnerOffsetEnd;///mSpinnerOffsetEnd 默认是拉到最底的可能位置 ,和mTotalDragDistance一开始初始化是相同的 1432 | float tensionSlingshotPercent = Math.max(0, Math.min(extraOS, slingshotDist * 2) 1433 | / slingshotDist); 1434 | float tensionPercent = (float) ((tensionSlingshotPercent / 4) - Math.pow( 1435 | (tensionSlingshotPercent / 4), 2)) * 2f;//tensionSlingshotPercent = x ,x/4-(x/4)^2,再*2,最多0.5 1436 | float extraMove = (slingshotDist) * tensionPercent * 2;//这个是模拟后来的拖动,最多slingshotDist 1437 | 1438 | int targetY = mOriginalOffsetBottom - (int) ((slingshotDist * dragPercent) + extraMove); 1439 | 1440 | // where 1.0f is a full circle 1441 | if (mCircleViewBottom.getVisibility() != View.VISIBLE) { 1442 | mCircleViewBottom.setVisibility(View.VISIBLE); 1443 | } 1444 | if (!mScale) { 1445 | ViewCompat.setScaleX(mCircleViewBottom, 1f); 1446 | ViewCompat.setScaleY(mCircleViewBottom, 1f); 1447 | Log.e("param","mScale==false,in moveBottomSpinner"); 1448 | } 1449 | 1450 | if (mScale) { 1451 | setAnimationProgress(Math.min(1f, overscrollTop / mTotalDragDistance)); 1452 | } 1453 | if (overscrollTop < mTotalDragDistance) { 1454 | if (mProgressBottom.getAlpha() > STARTING_PROGRESS_ALPHA 1455 | && !isAnimationRunning(mAlphaStartAnimationBottom)) { 1456 | // Animate the alpha 1457 | startProgressAlphaStartAnimationBottom(); 1458 | } 1459 | } else { 1460 | if (mProgressBottom.getAlpha() < MAX_ALPHA && !isAnimationRunning(mAlphaMaxAnimationBottom)) { 1461 | // Animate the alpha 1462 | startProgressAlphaMaxAnimationBottom(); 1463 | } 1464 | } 1465 | float strokeStart = adjustedPercent * .8f; 1466 | mProgressBottom.setStartEndTrim(0f, Math.min(MAX_PROGRESS_ANGLE, strokeStart));//Trim 修剪 1467 | mProgressBottom.setArrowScale(Math.min(1f, adjustedPercent)); 1468 | 1469 | float rotation = (-0.25f + .4f * adjustedPercent + tensionPercent * 2) * .5f; 1470 | mProgressBottom.setProgressRotation(rotation); 1471 | setTargetOffsetTopAndBottomForBottom(targetY-mCurrentTargetOffsetBottom, true /* requires update */); 1472 | 1473 | 1474 | } 1475 | 1476 | 1477 | 1478 | /**进行实际circle的滑动绘制,包括颜色等设置*/ 1479 | private void moveSpinner(float overscrollTop) { 1480 | Log.e("moveSpinner","+++++++++++++++++++++++moveSpinner--"+overscrollTop); 1481 | //overscrollTop = -overscrollTop; 1482 | mProgress.showArrow(true); 1483 | float originalDragPercent = overscrollTop / mTotalDragDistance; 1484 | Log.e("moveSpinner","moveSpinner--originalDragPercent=="+originalDragPercent); 1485 | float dragPercent = Math.min(1f, Math.abs(originalDragPercent)); 1486 | Log.e("moveSpinner","moveSpinner--dragPercent=="+dragPercent); 1487 | float adjustedPercent = (float) Math.max(dragPercent - .4, 0) * 5 / 3;//这个不理解 1488 | Log.e("moveSpinner","moveSpinner--adjustedPercent=="+adjustedPercent); 1489 | float extraOS = Math.abs(overscrollTop) - mTotalDragDistance;//这样不就是负的吗//就是负的//可以是正的 1490 | Log.e("moveSpinner","moveSpinner--extraOS=="+extraOS); 1491 | float slingshotDist = mUsingCustomStart ? mSpinnerOffsetEnd - mOriginalOffsetTop 1492 | : mSpinnerOffsetEnd;///mSpinnerOffsetEnd 默认是拉到最底的可能位置 ,和mTotalDragDistance一开始初始化是相同的 1493 | Log.e("moveSpinner","moveSpinner--slingshotDist=="+slingshotDist+",mSpinnerOffsetEnd=="+mSpinnerOffsetEnd); 1494 | float tensionSlingshotPercent = Math.max(0, Math.min(extraOS, slingshotDist * 2) 1495 | / slingshotDist); 1496 | Log.e("moveSpinner","moveSpinner--tensionSlingshotPercent=="+tensionSlingshotPercent); 1497 | float tensionPercent = (float) ((tensionSlingshotPercent / 4) - Math.pow( 1498 | (tensionSlingshotPercent / 4), 2)) * 2f;//tensionSlingshotPercent = x ,x/4-(x/4)^2,再*2 最大1/2 1499 | Log.e("moveSpinner","moveSpinner--tensionPercent=="+tensionPercent); 1500 | float extraMove = (slingshotDist) * tensionPercent * 2;//我理解了,这个是模拟后来的拖动,如果 1501 | Log.e("moveSpinner","moveSpinner--extraMove=="+extraMove); 1502 | int targetY = mOriginalOffsetTop + (int) ((slingshotDist * dragPercent) + extraMove);//最大可以达到 +2 slingshotDist 1503 | Log.e("moveSpinner","moveSpinner--targetY=="+targetY); 1504 | 1505 | 1506 | // where 1.0f is a full circle 1507 | if (mCircleView.getVisibility() != View.VISIBLE) { 1508 | mCircleView.setVisibility(View.VISIBLE); 1509 | } 1510 | if (!mScale) { 1511 | ViewCompat.setScaleX(mCircleView, 1f); 1512 | ViewCompat.setScaleY(mCircleView, 1f); 1513 | } 1514 | 1515 | if (mScale) { 1516 | setAnimationProgress(Math.min(1f, overscrollTop / mTotalDragDistance)); 1517 | } 1518 | if (overscrollTop < mTotalDragDistance) { 1519 | if (mProgress.getAlpha() > STARTING_PROGRESS_ALPHA 1520 | && !isAnimationRunning(mAlphaStartAnimation)) { 1521 | // Animate the alpha 1522 | startProgressAlphaStartAnimation(); 1523 | } 1524 | } else { 1525 | if (mProgress.getAlpha() < MAX_ALPHA && !isAnimationRunning(mAlphaMaxAnimation)) { 1526 | // Animate the alpha 1527 | startProgressAlphaMaxAnimation(); 1528 | } 1529 | } 1530 | float strokeStart = adjustedPercent * .8f; 1531 | mProgress.setStartEndTrim(0f, Math.min(MAX_PROGRESS_ANGLE, strokeStart)); 1532 | mProgress.setArrowScale(Math.min(1f, adjustedPercent)); 1533 | 1534 | float rotation = (-0.25f + .4f * adjustedPercent + tensionPercent * 2) * .5f; 1535 | mProgress.setProgressRotation(rotation); 1536 | setTargetOffsetTopAndBottom(targetY - mCurrentTargetOffsetTop, true /* requires update */); 1537 | Log.e("moveSpinner","+++++++++++++++++++++++moveSpinner--move=="+(targetY - mCurrentTargetOffsetTop)); 1538 | // setTargetOffsetTopAndBottom(10, true /* requires update */); 1539 | // Log.e(LOG_TAG,"mCurrentTargetOffsetTop=="+mCurrentTargetOffsetTop+",targetY=="+targetY); 1540 | } 1541 | 1542 | private void finishSpinner(float overscrollTop) { 1543 | Log.e("fish","finishSpinner--"+overscrollTop); 1544 | if (overscrollTop > mTotalDragDistance) {//大于这个核心点就升上去 1545 | setRefreshing(true, true /* notify */); 1546 | Log.e("fish","finishSpinner-->>>overscrollTop > mTotalDragDistance"); 1547 | } else { 1548 | // cancel refresh 1549 | mRefreshing = false; 1550 | mProgress.setStartEndTrim(0f, 0f); 1551 | AnimationListener listener = null; 1552 | if (!mScale) { 1553 | listener = new AnimationListener() { 1554 | 1555 | @Override 1556 | public void onAnimationStart(Animation animation) { 1557 | } 1558 | 1559 | @Override 1560 | public void onAnimationEnd(Animation animation) { 1561 | if (!mScale) { 1562 | startScaleDownAnimation(null); 1563 | } 1564 | } 1565 | 1566 | @Override 1567 | public void onAnimationRepeat(Animation animation) { 1568 | } 1569 | 1570 | }; 1571 | } 1572 | animateOffsetToStartPosition(mCurrentTargetOffsetTop, listener); 1573 | mProgress.showArrow(false); 1574 | } 1575 | } 1576 | 1577 | /**结束下半部的spinner*/ 1578 | private void finishSpinnerBottom(float overscrollTop) {//在小于mTotalDragDistance的时候处理的不好,会在中间停止 1579 | Log.e("fish","finishSpinnerBottom--"+overscrollTop); 1580 | if (overscrollTop > mTotalDragDistance) { 1581 | setRefreshingBottom(true, true); 1582 | Log.e("fish","finishSpinnerBottom-->>>overscrollTop > mTotalDragDistance"); 1583 | } else { 1584 | // cancel refresh 1585 | mRefreshingBottom = false; 1586 | mProgressBottom.setStartEndTrim(0f, 0f); 1587 | AnimationListener listener = null; 1588 | if (!mScale) { 1589 | listener = new AnimationListener() { 1590 | 1591 | @Override 1592 | public void onAnimationStart(Animation animation) { 1593 | } 1594 | 1595 | @Override 1596 | public void onAnimationEnd(Animation animation) { 1597 | if (!mScale) { 1598 | // startScaleDownAnimation(null); 1599 | startScaleDownAnimationBottom(null);//倒着转 1600 | } 1601 | } 1602 | 1603 | @Override 1604 | public void onAnimationRepeat(Animation animation) { 1605 | } 1606 | 1607 | }; 1608 | } 1609 | // animateOffsetToCorrectPositionBottom(mCurrentTargetOffsetBottom,listener); 1610 | animateOffsetToStartPositionBottom(mCurrentTargetOffsetBottom,listener); 1611 | mProgressBottom.showArrow(false); 1612 | } 1613 | } 1614 | 1615 | 1616 | 1617 | 1618 | @Override 1619 | public boolean onTouchEvent(MotionEvent ev) { 1620 | final int action = MotionEventCompat.getActionMasked(ev); 1621 | int pointerIndex = -1; 1622 | 1623 | if (mReturningToStart && action == MotionEvent.ACTION_DOWN) { 1624 | mReturningToStart = false; 1625 | } 1626 | Log.e("fish","-onTouchEvent-;ACTION=="+ev.getAction()); 1627 | if (!isEnabled() || mReturningToStart || canChildScrollUp() 1628 | || mRefreshing || mNestedScrollInProgress) { 1629 | // Fail fast if we're not in a state where a swipe is possible 1630 | return false; 1631 | } 1632 | 1633 | switch (action) { 1634 | case MotionEvent.ACTION_DOWN: 1635 | Log.e("fish","-onTouchEvent-;ACTION_DOWN"); 1636 | mActivePointerId = ev.getPointerId(0); 1637 | mIsBeingDragged = false; 1638 | break; 1639 | 1640 | case MotionEvent.ACTION_MOVE: { 1641 | Log.e("fish","-onTouchEvent-;ACTION_MOVE"); 1642 | pointerIndex = ev.findPointerIndex(mActivePointerId); 1643 | if (pointerIndex < 0) { 1644 | Log.e(LOG_TAG, "Got ACTION_MOVE event but have an invalid active pointer id."); 1645 | return false; 1646 | } 1647 | 1648 | final float y = ev.getY(pointerIndex); 1649 | startDragging(y); 1650 | 1651 | if (mIsBeingDragged) { 1652 | final float overscrollTop = (y - mInitialMotionY) * DRAG_RATE; 1653 | if (overscrollTop > 0 && !mRefreshingBottom) { 1654 | moveSpinner(overscrollTop*2); 1655 | } else { 1656 | return false; 1657 | } 1658 | } 1659 | break; 1660 | } 1661 | case MotionEventCompat.ACTION_POINTER_DOWN: { 1662 | Log.e("fish","-onTouchEvent-;MotionEventCompat.ACTION_POINTER_DOWN"); 1663 | pointerIndex = MotionEventCompat.getActionIndex(ev); 1664 | if (pointerIndex < 0) { 1665 | Log.e(LOG_TAG, 1666 | "Got ACTION_POINTER_DOWN event but have an invalid action index."); 1667 | return false; 1668 | } 1669 | mActivePointerId = ev.getPointerId(pointerIndex); 1670 | break; 1671 | } 1672 | 1673 | case MotionEventCompat.ACTION_POINTER_UP: 1674 | Log.e("fish","-onTouchEvent-;MotionEventCompat.ACTION_POINTER_UP"); 1675 | onSecondaryPointerUp(ev); 1676 | break; 1677 | 1678 | case MotionEvent.ACTION_UP: { 1679 | Log.e("fish","-onTouchEvent-;MotionEvent.ACTION_UP"); 1680 | pointerIndex = ev.findPointerIndex(mActivePointerId); 1681 | if (pointerIndex < 0) { 1682 | Log.e(LOG_TAG, "Got ACTION_UP event but don't have an active pointer id."); 1683 | return false; 1684 | } 1685 | 1686 | if (mIsBeingDragged && !mRefreshingBottom) { 1687 | final float y = ev.getY(pointerIndex); 1688 | final float overscrollTop = (y - mInitialMotionY) * DRAG_RATE; 1689 | mIsBeingDragged = false; 1690 | finishSpinner(overscrollTop); 1691 | } 1692 | mActivePointerId = INVALID_POINTER; 1693 | return false; 1694 | } 1695 | case MotionEvent.ACTION_CANCEL: 1696 | return false; 1697 | } 1698 | 1699 | return true; 1700 | } 1701 | 1702 | private void startDragging(float y) { 1703 | final float yDiff = y - mInitialDownY; 1704 | if (yDiff > mTouchSlop && !mIsBeingDragged) { 1705 | mInitialMotionY = mInitialDownY + mTouchSlop; 1706 | mIsBeingDragged = true; 1707 | mProgress.setAlpha(STARTING_PROGRESS_ALPHA); 1708 | } 1709 | } 1710 | /**启动关于mCircleView的动画mAnimateToCorrectPosition,相应的更改mFrom的值为 1711 | * param:from 1712 | * */ 1713 | private void animateOffsetToCorrectPosition(int from, AnimationListener listener) { 1714 | mFrom = from; 1715 | mAnimateToCorrectPosition.reset(); 1716 | mAnimateToCorrectPosition.setDuration(ANIMATE_TO_TRIGGER_DURATION); 1717 | mAnimateToCorrectPosition.setInterpolator(mDecelerateInterpolator); 1718 | if (listener != null) { 1719 | mCircleView.setAnimationListener(listener); 1720 | } 1721 | mCircleView.clearAnimation(); 1722 | mCircleView.startAnimation(mAnimateToCorrectPosition); 1723 | } 1724 | 1725 | 1726 | /**启动关于mCircleView的动画mAnimateToCorrectPosition,相应的更改mFrom的值为 1727 | * param:from 1728 | * */ 1729 | private void animateOffsetToCorrectPositionBottom(int from, AnimationListener listener) { 1730 | mFrom = from; 1731 | mAnimateToCorrectPositionBottom.reset(); 1732 | mAnimateToCorrectPositionBottom.setDuration(ANIMATE_TO_TRIGGER_DURATION); 1733 | mAnimateToCorrectPositionBottom.setInterpolator(mDecelerateInterpolator); 1734 | if (listener != null) { 1735 | mCircleViewBottom.setAnimationListener(listener); 1736 | } 1737 | mCircleViewBottom.clearAnimation(); 1738 | mCircleViewBottom.startAnimation(mAnimateToCorrectPositionBottom); 1739 | } 1740 | 1741 | 1742 | 1743 | 1744 | private void animateOffsetToStartPosition(int from, AnimationListener listener) { 1745 | if (mScale) { 1746 | // Scale the item back down 1747 | startScaleDownReturnToStartAnimation(from, listener); 1748 | } else { 1749 | mFrom = from; 1750 | mAnimateToStartPosition.reset(); 1751 | mAnimateToStartPosition.setDuration(ANIMATE_TO_START_DURATION); 1752 | mAnimateToStartPosition.setInterpolator(mDecelerateInterpolator); 1753 | if (listener != null) { 1754 | mCircleView.setAnimationListener(listener); 1755 | } 1756 | mCircleView.clearAnimation(); 1757 | mCircleView.startAnimation(mAnimateToStartPosition); 1758 | } 1759 | } 1760 | 1761 | 1762 | private void animateOffsetToStartPositionBottom(int from, AnimationListener listener) { 1763 | if (mScale) { 1764 | // Scale the item back down 1765 | startScaleDownReturnToStartAnimation(from, listener); 1766 | } else { 1767 | mFrom = from; 1768 | mAnimateToStartPositionBottom.reset(); 1769 | mAnimateToStartPositionBottom.setDuration(ANIMATE_TO_START_DURATION); 1770 | mAnimateToStartPositionBottom.setInterpolator(mDecelerateInterpolator); 1771 | if (listener != null) { 1772 | mCircleViewBottom.setAnimationListener(listener); 1773 | } 1774 | mCircleViewBottom.clearAnimation(); 1775 | mCircleViewBottom.startAnimation(mAnimateToStartPositionBottom); 1776 | } 1777 | } 1778 | 1779 | 1780 | /**刷新的动画,将circle从mFrom动画移动到mSpinnerOffsetEnd 1781 | * 1782 | * */ 1783 | private final Animation mAnimateToCorrectPosition = new Animation() { 1784 | @Override 1785 | public void applyTransformation(float interpolatedTime, Transformation t) { 1786 | int targetTop = 0; 1787 | int endTarget = 0; 1788 | if (!mUsingCustomStart) { 1789 | endTarget = mSpinnerOffsetEnd - Math.abs(mOriginalOffsetTop); 1790 | } else { 1791 | endTarget = mSpinnerOffsetEnd; 1792 | } 1793 | targetTop = (mFrom + (int) ((endTarget - mFrom) * interpolatedTime));//一开始我这里有疑惑,但是后来才想到interpolatedTime是0.x的。也就相当于百分比 1794 | int offset = targetTop - mCircleView.getTop(); 1795 | setTargetOffsetTopAndBottom(offset, false /* requires update */); 1796 | mProgress.setArrowScale(1 - interpolatedTime); 1797 | } 1798 | }; 1799 | 1800 | 1801 | 1802 | /**刷新的动画,将circle从mFrom移动到 1803 | *中间然后滑动 1804 | * Bottom 1805 | * */ 1806 | private final Animation mAnimateToCorrectPositionBottom = new Animation() { 1807 | @Override 1808 | public void applyTransformation(float interpolatedTime, Transformation t) { 1809 | int targetTop = 0; 1810 | int endTarget = 0; 1811 | if (!mUsingCustomStart) {//false 1812 | endTarget = (int) (mOriginalOffsetBottom - mSpinnerOffsetEnd*1.5);//*2 - mCircleDiameter;//mOriginalOffsetTop初始是负直径 1813 | } else { 1814 | endTarget = mSpinnerOffsetEnd; 1815 | } 1816 | targetTop = (mFrom + (int) ((endTarget - mFrom) * interpolatedTime));//一开始我这里有疑惑,但是后来才想到interpolatedTime是0.x的。也就相当于百分比 1817 | int offset = targetTop - mCircleViewBottom.getBottom(); 1818 | // Lo 1819 | // setTargetOffsetTopAndBottom(offset, false /* requires update */); 1820 | setTargetOffsetTopAndBottomForBottom(offset,false); 1821 | mProgress.setArrowScale(1 - interpolatedTime); 1822 | } 1823 | }; 1824 | 1825 | /**回到顶部 1826 | * @param interpolatedTime 动画时间 1827 | * @return 这里是根据当前位置 mFrom 回到 mOriginalOffsetTop 1828 | * 1829 | * */ 1830 | void moveToStart(float interpolatedTime) { 1831 | int targetTop = 0; 1832 | targetTop = (mFrom + (int) ((mOriginalOffsetTop - mFrom) * interpolatedTime)); 1833 | int offset = targetTop - mCircleView.getTop(); 1834 | setTargetOffsetTopAndBottom(offset, false /* requires update */); 1835 | } 1836 | 1837 | /**回到顶部 1838 | * @param interpolatedTime 动画时间 1839 | * @return 这里是根据当前位置 mFrom 回到 mOriginalOffsetTop 1840 | * 1841 | * */ 1842 | void moveToEnd(float interpolatedTime) { 1843 | int targetTop = 0; 1844 | targetTop = (mFrom + (int) ((mOriginalOffsetBottom - mFrom) * interpolatedTime)); 1845 | int offset = targetTop - mCircleViewBottom.getBottom(); 1846 | setTargetOffsetTopAndBottomForBottom(offset, false /* requires update */); 1847 | } 1848 | 1849 | 1850 | private final Animation mAnimateToStartPosition = new Animation() { 1851 | @Override 1852 | public void applyTransformation(float interpolatedTime, Transformation t) { 1853 | moveToStart(interpolatedTime); 1854 | } 1855 | }; 1856 | 1857 | private final Animation mAnimateToStartPositionBottom = new Animation() { 1858 | @Override 1859 | public void applyTransformation(float interpolatedTime, Transformation t) { 1860 | moveToEnd(interpolatedTime); 1861 | } 1862 | }; 1863 | 1864 | 1865 | private void startScaleDownReturnToStartAnimation(int from, 1866 | AnimationListener listener) { 1867 | mFrom = from; 1868 | if (isAlphaUsedForScale()) { 1869 | mStartingScale = mProgress.getAlpha(); 1870 | } else { 1871 | mStartingScale = ViewCompat.getScaleX(mCircleView); 1872 | } 1873 | mScaleDownToStartAnimation = new Animation() { 1874 | @Override 1875 | public void applyTransformation(float interpolatedTime, Transformation t) { 1876 | float targetScale = (mStartingScale + (-mStartingScale * interpolatedTime)); 1877 | setAnimationProgress(targetScale); 1878 | moveToStart(interpolatedTime); 1879 | } 1880 | }; 1881 | mScaleDownToStartAnimation.setDuration(SCALE_DOWN_DURATION); 1882 | if (listener != null) { 1883 | mCircleView.setAnimationListener(listener); 1884 | } 1885 | mCircleView.clearAnimation(); 1886 | mCircleView.startAnimation(mScaleDownToStartAnimation); 1887 | } 1888 | 1889 | 1890 | /** 1891 | * 首先,提升circle的视图,然后将ciecle向上推offset个距离 1892 | *其中用到的是ViewCompat.offsetTopAndBottom(View,offset);正数向下 负数向上 1893 | *当requiresUpdate为true时,会进行整个的重绘,如果sdk小于11 1894 | *注意,每次移动的时候都会更新mCircleViewBottom
1895 | * */ 1896 | void setTargetOffsetTopAndBottomForBottom(int offset, boolean requiresUpdate) { 1897 | mCircleViewBottom.bringToFront();//改变circle在俯视图中的位置即z坐标,将他提到最上面来 1898 | //通过offset直接改变坐标,正数向下负数向上 1899 | ViewCompat.offsetTopAndBottom(mCircleViewBottom, offset); 1900 | mCurrentTargetOffsetBottom = mCircleViewBottom.getBottom(); 1901 | mCircleViewBottom.invalidate(); 1902 | if (requiresUpdate && android.os.Build.VERSION.SDK_INT < 11) { 1903 | invalidate(); 1904 | } 1905 | } 1906 | 1907 | 1908 | 1909 | /** 1910 | * 首先,提升circle的视图,然后将ciecle向下推offset个距离 1911 | *其中用到的是ViewCompat.offsetTopAndBottom(View,offset);正数向下负数向上 1912 | *当requiresUpdate为true时,会进行整个的重绘,如果sdk小于11 1913 | * */ 1914 | void setTargetOffsetTopAndBottom(int offset, boolean requiresUpdate) { 1915 | Log.e(LOG_TAG,"setTargetOffsetTopAndBottom,offset=="+offset); 1916 | mCircleView.bringToFront();//改变circle在俯视图中的位置即z坐标,将他提到最上面来 1917 | //通过offset直接改变坐标,正数向下负数向上 1918 | ViewCompat.offsetTopAndBottom(mCircleView, offset); 1919 | mCurrentTargetOffsetTop = mCircleView.getTop(); 1920 | mCircleView.invalidate(); 1921 | if (requiresUpdate && android.os.Build.VERSION.SDK_INT < 11) { 1922 | invalidate(); 1923 | } 1924 | } 1925 | 1926 | private void onSecondaryPointerUp(MotionEvent ev) { 1927 | final int pointerIndex = MotionEventCompat.getActionIndex(ev); 1928 | final int pointerId = ev.getPointerId(pointerIndex); 1929 | if (pointerId == mActivePointerId) { 1930 | // This was our active pointer going up. Choose a new 1931 | // active pointer and adjust accordingly. 1932 | final int newPointerIndex = pointerIndex == 0 ? 1 : 0; 1933 | mActivePointerId = ev.getPointerId(newPointerIndex); 1934 | } 1935 | } 1936 | 1937 | /** 1938 | * Classes that wish to be notified when the swipe gesture correctly 1939 | * triggers a refresh should implement this interface. 1940 | */ 1941 | public interface OnRefreshListener { 1942 | /** 1943 | * Called when a swipe gesture triggers a refresh. 1944 | */ 1945 | void onRefresh(); 1946 | } 1947 | 1948 | /**自建的下部刷新接口*/ 1949 | public interface OnBottomRefreshListener{ 1950 | void onBottomRefresh(); 1951 | } 1952 | 1953 | /** 1954 | * Classes that wish to override {@link GZoomSwifrefresh#canChildScrollUp()} method 1955 | * behavior should implement this interface. 1956 | */ 1957 | public interface OnChildScrollUpCallback { 1958 | /** 1959 | * Callback that will be called when {@link GZoomSwifrefresh#canChildScrollUp()} method 1960 | * is called to allow the implementer to override its behavior. 1961 | * 1962 | * @param parent GZoomSwifrefresh that this callback is overriding. 1963 | * @param child The child view of GZoomSwifrefresh. 1964 | * 1965 | * @return Whether it is possible for the child view of parent layout to scroll up. 1966 | */ 1967 | boolean canChildScrollUp(GZoomSwifrefresh parent, @Nullable View child); 1968 | } 1969 | } 1970 | -------------------------------------------------------------------------------- /widgetpro/src/main/java/gzoomswiperefresh/MainActivity.java: -------------------------------------------------------------------------------- 1 | package gzoomswiperefresh; 2 | 3 | import android.os.Bundle; 4 | import android.support.annotation.NonNull; 5 | import android.support.design.widget.NavigationView; 6 | import android.support.design.widget.TabLayout; 7 | import android.support.v4.app.Fragment; 8 | import android.support.v4.view.ViewPager; 9 | import android.support.v4.widget.DrawerLayout; 10 | import android.support.v7.app.ActionBarDrawerToggle; 11 | import android.support.v7.app.AppCompatActivity; 12 | import android.support.v7.widget.Toolbar; 13 | import android.view.MenuItem; 14 | 15 | import com.example.widgetpro.R; 16 | 17 | import java.util.ArrayList; 18 | import java.util.List; 19 | 20 | import butterknife.Bind; 21 | import butterknife.ButterKnife; 22 | 23 | public class MainActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener{ 24 | 25 | @Bind(R.id.main_toolbar) 26 | Toolbar toolbar; 27 | 28 | @Bind(R.id.main_drawerlayout) 29 | DrawerLayout drawerLayout; 30 | 31 | @Bind(R.id.main_navigation) 32 | NavigationView navigationView; 33 | 34 | @Bind(R.id.main_tablayout) 35 | TabLayout tabLayout; 36 | 37 | @Bind(R.id.main_viewpager) 38 | ViewPager viewpager; 39 | 40 | Listnull
.
585 | */
586 | public void setColors(@NonNull int[] colors) {
587 | mColors = colors;
588 | // if colors are reset, make sure to reset the color index as well
589 | setColorIndex(0);
590 | }
591 |
592 | /**
593 | * Set the absolute color of the progress spinner. This is should only
594 | * be used when animating between current and next color when the
595 | * spinner is rotating.
596 | *
597 | * @param color int describing the color.
598 | */
599 | public void setColor(int color) {
600 | mCurrentColor = color;
601 | }
602 |
603 | /**
604 | * @param index Index into the color array of the color to display in
605 | * the progress spinner.
606 | */
607 | public void setColorIndex(int index) {
608 | mColorIndex = index;
609 | mCurrentColor = mColors[mColorIndex];
610 | }
611 |
612 | /**
613 | * @return int describing the next color the progress spinner should use when drawing.
614 | */
615 | public int getNextColor() {
616 | return mColors[getNextColorIndex()];
617 | }
618 |
619 | private int getNextColorIndex() {
620 | return (mColorIndex + 1) % (mColors.length);
621 | }
622 |
623 | /**
624 | * Proceed to the next available ring color. This will automatically
625 | * wrap back to the beginning of colors.
626 | */
627 | public void goToNextColor() {
628 | setColorIndex(getNextColorIndex());
629 | }
630 |
631 | public void setColorFilter(ColorFilter filter) {
632 | mPaint.setColorFilter(filter);
633 | invalidateSelf();
634 | }
635 |
636 | /**
637 | * @param alpha Set the alpha of the progress spinner and associated arrowhead.
638 | */
639 | public void setAlpha(int alpha) {
640 | mAlpha = alpha;
641 | }
642 |
643 | /**
644 | * @return Current alpha of the progress spinner and arrowhead.
645 | */
646 | public int getAlpha() {
647 | return mAlpha;
648 | }
649 |
650 | /**
651 | * @param strokeWidth Set the stroke width of the progress spinner in pixels.
652 | */
653 | public void setStrokeWidth(float strokeWidth) {
654 | mStrokeWidth = strokeWidth;
655 | mPaint.setStrokeWidth(strokeWidth);
656 | invalidateSelf();
657 | }
658 |
659 | @SuppressWarnings("unused")
660 | public float getStrokeWidth() {
661 | return mStrokeWidth;
662 | }
663 |
664 | @SuppressWarnings("unused")
665 | public void setStartTrim(float startTrim) {
666 | mStartTrim = startTrim;
667 | invalidateSelf();
668 | }
669 |
670 | @SuppressWarnings("unused")
671 | public float getStartTrim() {
672 | return mStartTrim;
673 | }
674 |
675 | public float getStartingStartTrim() {
676 | return mStartingStartTrim;
677 | }
678 |
679 | public float getStartingEndTrim() {
680 | return mStartingEndTrim;
681 | }
682 |
683 | public int getStartingColor() {
684 | return mColors[mColorIndex];
685 | }
686 |
687 | @SuppressWarnings("unused")
688 | public void setEndTrim(float endTrim) {
689 | mEndTrim = endTrim;
690 | invalidateSelf();
691 | }
692 |
693 | @SuppressWarnings("unused")
694 | public float getEndTrim() {
695 | return mEndTrim;
696 | }
697 |
698 | @SuppressWarnings("unused")
699 | public void setRotation(float rotation) {
700 | mRotation = rotation;
701 | invalidateSelf();
702 | }
703 |
704 | @SuppressWarnings("unused")
705 | public float getRotation() {
706 | return mRotation;
707 | }
708 |
709 | public void setInsets(int width, int height) {
710 | final float minEdge = (float) Math.min(width, height);
711 | float insets;
712 | if (mRingCenterRadius <= 0 || minEdge < 0) {
713 | insets = (float) Math.ceil(mStrokeWidth / 2.0f);
714 | } else {
715 | insets = (float) (minEdge / 2.0f - mRingCenterRadius);
716 | }
717 | mStrokeInset = insets;
718 | }
719 |
720 | @SuppressWarnings("unused")
721 | public float getInsets() {
722 | return mStrokeInset;
723 | }
724 |
725 | /**
726 | * @param centerRadius Inner radius in px of the circle the progress
727 | * spinner arc traces.
728 | */
729 | public void setCenterRadius(double centerRadius) {
730 | mRingCenterRadius = centerRadius;
731 | }
732 |
733 | public double getCenterRadius() {
734 | return mRingCenterRadius;
735 | }
736 |
737 | /**
738 | * @param show Set to true to show the arrow head on the progress spinner.
739 | */
740 | public void setShowArrow(boolean show) {
741 | if (mShowArrow != show) {
742 | mShowArrow = show;
743 | invalidateSelf();
744 | }
745 | }
746 |
747 | /**
748 | * @param scale Set the scale of the arrowhead for the spinner.
749 | */
750 | public void setArrowScale(float scale) {
751 | if (scale != mArrowScale) {
752 | mArrowScale = scale;
753 | invalidateSelf();
754 | }
755 | }
756 |
757 | /**
758 | * @return The amount the progress spinner is currently rotated, between [0..1].
759 | */
760 | public float getStartingRotation() {
761 | return mStartingRotation;
762 | }
763 |
764 | /**
765 | * If the start / end trim are offset to begin with, store them so that
766 | * animation starts from that offset.
767 | */
768 | public void storeOriginals() {
769 | mStartingStartTrim = mStartTrim;
770 | mStartingEndTrim = mEndTrim;
771 | mStartingRotation = mRotation;
772 | }
773 |
774 | /**
775 | * Reset the progress spinner to default rotation, start and end angles.
776 | */
777 | public void resetOriginals() {
778 | mStartingStartTrim = 0;
779 | mStartingEndTrim = 0;
780 | mStartingRotation = 0;
781 | setStartTrim(0);
782 | setEndTrim(0);
783 | setRotation(0);
784 | }
785 |
786 | private void invalidateSelf() {
787 | mCallback.invalidateDrawable(null);
788 | }
789 | }
790 | }
791 |
--------------------------------------------------------------------------------
/widgetpro/src/main/java/gzoomswiperefresh/TabFragmentAdapter.java:
--------------------------------------------------------------------------------
1 | package gzoomswiperefresh;
2 |
3 | import android.support.v4.app.Fragment;
4 | import android.support.v4.app.FragmentManager;
5 | import android.support.v4.app.FragmentStatePagerAdapter;
6 |
7 | import java.util.List;
8 |
9 | /**
10 | *
11 | * Created by Dimon on 2016/3/23.
12 | * 因为是抽象类所以要继承
13 | */
14 | public class TabFragmentAdapter extends FragmentStatePagerAdapter {
15 |
16 | private List