根据源码仿写HorizontalScrollView添加左右阴影效果

上一篇篇把滚动效果补充进去了,这篇把左右阴影效果

package com.myf.scrollerscalerulerview;

import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.widget.EdgeEffect;
import android.widget.OverScroller;
/**
 * 添加左右阴影效果
 * 仿写>添加了滚动功能>添加左右阴影效果
 * @author ZH-SW-Mengyf
 */
@SuppressLint("NewApi") 
public class HScrollerLayout extends ViewGroup {
    /**
     * 判定为拖动的最小移动像素数
     */
    private int mTouchSlop;
    
	/**
	 * 界面可滚动的左边界
	 */
	private int mLeftBorder;

	/**
	 * 界面可滚动的右边界
	 */
	private int mRightBorder;
	
	private int mEventX;
	
	private int mOverscrollDistance;
	
	private int mLastMotionX;
	
	private boolean mIsBeingDragged= false;
	
	private int mScrollX;
	
	 private OverScroller mScroller;
	 private EdgeEffect mEdgeGlowLeft;
	 private EdgeEffect mEdgeGlowRight;

	private VelocityTracker mVelocityTracker;

	private int mMinimumVelocity;

	private int mMaximumVelocity;

	private int mOverflingDistance;

	public HScrollerLayout(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
		init(context);
		
	}

	public HScrollerLayout(Context context, AttributeSet attrs) {
		super(context, attrs);
		init(context);
	}

	public HScrollerLayout(Context context) {
		super(context);
		init(context);
	}
	
	private void init(Context context) {
		mScroller = new OverScroller(getContext());
	    // 第一步,创建Scroller的实例
        
        final ViewConfiguration configuration = ViewConfiguration.get(context);
        mTouchSlop = configuration.getScaledTouchSlop();
        mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();
        mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
        mOverscrollDistance = configuration.getScaledOverscrollDistance();
        mOverflingDistance = configuration.getScaledOverflingDistance();
        setOverScrollMode(OVER_SCROLL_ALWAYS);
        //这个可以保证draw(Canvas canvas)被调用
        setWillNotDraw(false);
	}

	   @Override
	    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
	        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
	        int childCount = getChildCount();
	        for (int i = 0; i < childCount; i++) {
	            View childView = getChildAt(i);
	            // 为ScrollerLayout中的每一个子控件测量大小
	            measureChild(childView, widthMeasureSpec, heightMeasureSpec);
	        }
	    }
	
	@Override
	protected void onLayout(boolean changed, int l, int t, int r, int b) {
		if (changed) {
			int childCount = getChildCount();
			for (int i = 0; i < childCount; i++) {
				View childView = getChildAt(i);
				// 为ScrollerLayout中的每一个子控件在水平方向上进行布局
				childView.layout(i * childView.getMeasuredWidth(), 0, (i + 1)* childView.getMeasuredWidth(),childView.getMeasuredHeight());
			}
			
			mLeftBorder = getChildAt(0).getLeft();
			
			mRightBorder = getChildAt(getChildCount() - 1).getRight();

			final int scrollRange = Math.max(0, getWidth() - (mRightBorder - mLeftBorder));

			mScrollX = scrollRange;
			
			if (mScrollX > scrollRange) {
				mScrollX = scrollRange;
			} 
			else if (mScrollX < 0) {
				mScrollX = 0;
			}
			scrollTo(mScrollX, 0);
		}
	}
	
	@Override
	public boolean onInterceptTouchEvent(MotionEvent ev) {
		mEventX = (int) ev.getX();
		switch (ev.getAction()) {
		case MotionEvent.ACTION_DOWN:
			initOrResetVelocityTracker();
			mVelocityTracker.addMovement(ev);
			break;
		
		case MotionEvent.ACTION_MOVE:
			int xDiff = (int) Math.abs(mLastMotionX - mEventX);
			// 当手指拖动值大于TouchSlop值时,认为应该进行滚动,拦截子控件的事件
			if (xDiff > mTouchSlop) {
				mIsBeingDragged = true;
				mLastMotionX = mEventX; 
				initVelocityTrackerIfNotExists();
				mVelocityTracker.addMovement(ev);
				return true;
			}
			break;
			
		   case MotionEvent.ACTION_CANCEL:
           case MotionEvent.ACTION_UP:
        	   mIsBeingDragged = false;
        	   break;
		}
		return super.onInterceptTouchEvent(ev);
	}

	
	@SuppressLint("NewApi") @Override
	public boolean onTouchEvent(MotionEvent event) {
		 initVelocityTrackerIfNotExists();
		 mVelocityTracker.addMovement(event);
		switch (event.getAction()) {
		case MotionEvent.ACTION_DOWN:
			mLastMotionX = (int) event.getX();
			break;

		case MotionEvent.ACTION_MOVE:
			int x = (int) event.getX();
			int deltaX = (int) (mLastMotionX - x);
			if (!mIsBeingDragged && Math.abs(deltaX) > mTouchSlop) {
				mIsBeingDragged = true;
				if (deltaX > 0) {
					deltaX -= mTouchSlop;
				} else {
					deltaX += mTouchSlop;
				}
			}
			if (mIsBeingDragged) {
				mLastMotionX = x;
				final int oldX = mScrollX;
				final int range = getScrollRange();
				boolean isBeyondScrollRange = overScrollBy(deltaX, 0, oldX, 0, range, 0, mOverscrollDistance, 0, true);
				if (isBeyondScrollRange) {
					mVelocityTracker.clear();
				}
				//这部分代码是进行两边阴影处理的
			    final int overscrollMode = getOverScrollMode();
				final boolean canOverscroll = overscrollMode == OVER_SCROLL_ALWAYS ||(overscrollMode == OVER_SCROLL_IF_CONTENT_SCROLLS && range > 0);
				 if (canOverscroll) {
				      final int pulledToX = oldX + deltaX;
				    
                      if (pulledToX < 0) {
                          mEdgeGlowLeft.onPull((float) deltaX / getWidth());
                          if (!mEdgeGlowRight.isFinished()) {
                              mEdgeGlowRight.onRelease();
                          }
                      } else if (pulledToX > range) {
                          mEdgeGlowRight.onPull((float) deltaX / getWidth());
                          if (!mEdgeGlowLeft.isFinished()) {
                              mEdgeGlowLeft.onRelease();
                          }
                      }
                      if (mEdgeGlowLeft != null && (!mEdgeGlowLeft.isFinished() || !mEdgeGlowRight.isFinished())) {
                          postInvalidateOnAnimation();
                      }
				 }
			}
			break;

		case MotionEvent.ACTION_UP:
			if (mIsBeingDragged) {
				 final VelocityTracker velocityTracker = mVelocityTracker;
                 velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
                 int initialVelocity = (int) velocityTracker.getXVelocity();

                 if (getChildCount() > 0) {
                     if ((Math.abs(initialVelocity) > mMinimumVelocity)) {
                         fling(-initialVelocity);
                     } else {
                         if (mScroller.springBack(mScrollX, 0, 0, getScrollRange(), 0, 0)) {
                             postInvalidateOnAnimation();
                         }
                     }
                 }
				
				mIsBeingDragged = false;
				
				recycleVelocityTracker();
				
				   if (mEdgeGlowLeft != null) {
                       mEdgeGlowLeft.onRelease();
                       mEdgeGlowRight.onRelease();
                   }
			}
			break;
		}

		return true;
	}
	
	/**
	 * 可以滚动的最大范围
	 * @return
	 */
    private int getScrollRange() {
        int scrollRange = (mRightBorder - mLeftBorder) - getWidth();
        return scrollRange;
    }
	
	@Override
	protected void onOverScrolled(int scrollX, int scrollY, boolean clampedX,boolean clampedY) {
		mScrollX = scrollX;
		 super.scrollTo(scrollX, scrollY);
	}
	

    private void initOrResetVelocityTracker() {
        if (mVelocityTracker == null) {
            mVelocityTracker = VelocityTracker.obtain();
        } else {
            mVelocityTracker.clear();
        }
    }

    private void initVelocityTrackerIfNotExists() {
        if (mVelocityTracker == null) {
            mVelocityTracker = VelocityTracker.obtain();
        }
    }

    private void recycleVelocityTracker() {
        if (mVelocityTracker != null) {
            mVelocityTracker.recycle();
            mVelocityTracker = null;
        }
    }
    
    public void fling(int velocityX) {
        if (getChildCount() > 0) {
            int width = getWidth();

            mScroller.fling(mScrollX, 0, velocityX, 0, 0,  getScrollRange(), 0, 0, width/2, 0);

            postInvalidateOnAnimation();
        }
    }

    @Override
    public void computeScroll() {
        if (mScroller.computeScrollOffset()) {
            int oldX = mScrollX;
            int oldY = 0;
            int x = mScroller.getCurrX();
            int y = mScroller.getCurrY();

            if (oldX != x || oldY != y) {
            	final int range = getScrollRange();
                overScrollBy(x - oldX, y - oldY, oldX, oldY, range, 0, mOverflingDistance, 0, false);
                
                //这部分代码是话两边阴影的
                final int overscrollMode = getOverScrollMode();
                final boolean canOverscroll = overscrollMode == OVER_SCROLL_ALWAYS || (overscrollMode == OVER_SCROLL_IF_CONTENT_SCROLLS && range > 0);
                if (canOverscroll) {
                    if (x < 0 && oldX >= 0) {
                        mEdgeGlowLeft.onAbsorb((int) mScroller.getCurrVelocity());
                    } else if (x > range && oldX <= range) {
                        mEdgeGlowRight.onAbsorb((int) mScroller.getCurrVelocity());
                    }
                }
            }

            if (!awakenScrollBars()) {
                postInvalidateOnAnimation();
            }
        }
    }
    
    @Override
    public void setOverScrollMode(int overScrollMode) {
        if (overScrollMode != OVER_SCROLL_NEVER) {
            if (mEdgeGlowLeft == null) {
                Context context = getContext();
                mEdgeGlowLeft = new EdgeEffect(context);
                mEdgeGlowRight = new EdgeEffect(context);
            }
        } else {
            mEdgeGlowLeft = null;
            mEdgeGlowRight = null;
        }
    	super.setOverScrollMode(overScrollMode);
    }
    
    //这里有个坑,为什么自定义ViewGroup draw方法不会被调用。
    //https://blog.csdn.net/hb8676086/article/details/52059993
    @Override
    public void draw(Canvas canvas) {
        super.draw(canvas);
        if (mEdgeGlowLeft != null) {
            final int scrollX = mScrollX;
          
            if (!mEdgeGlowLeft.isFinished()) {
                final int restoreCount = canvas.save();
                final int height = getHeight() ;
                canvas.rotate(270);
                canvas.translate(-height , Math.min(0, scrollX));
                mEdgeGlowLeft.setSize(height, getWidth());
                if (mEdgeGlowLeft.draw(canvas)) {
                    postInvalidateOnAnimation();
                }
                canvas.restoreToCount(restoreCount);
            }
            if (!mEdgeGlowRight.isFinished()) {
                final int restoreCount = canvas.save();
                final int width = getWidth();
                final int height = getHeight();
                canvas.rotate(90);
                canvas.translate(0,-(Math.max(getScrollRange(), scrollX) + width));
                mEdgeGlowRight.setSize(height, width);
                if (mEdgeGlowRight.draw(canvas)) {
                    postInvalidateOnAnimation();
                }
                canvas.restoreToCount(restoreCount);
            }
        }
    }
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值