上一篇篇把滚动效果补充进去了,这篇把左右阴影效果
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);
}
}
}
}