import java.util.List;
import android.content.Context;
import android.graphics.Rect;
import android.os.Handler;
import android.util.AttributeSet;
import android.view.FocusFinder;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.ViewParent;
import android.view.animation.AnimationUtils;
import android.view.animation.TranslateAnimation;
import android.widget.FrameLayout;
import android.widget.Scroller;
public class CustomScrollView extends FrameLayout{
View mView;
View mView2;
static final int ANIMATED_SCROLL_GAP = 250;
static final float MAX_SCROLL_FACTOR = 0.5f;
private long mLastScroll;
private final Rect mTempRect = new Rect();
private Scroller mScroller;
private boolean mScrollViewMovedFocus;
private float mLastMotionY;
private float mLastMotionX;
private boolean mIsLayoutDirty = true;
private View mChildToScrollTo = null;
private boolean mIsBeingDragged = false;
private VelocityTracker mVelocityTracker;
private boolean mFillViewport;
private boolean mSmoothScrollingEnabled = true;
private int mTouchSlop;
private int mMinimumVelocity;
private int mMaximumVelocity;
private int mActivePointerId = INVALID_POINTER;
private static final int INVALID_POINTER = -1;
private boolean mFlingEnabled = true;
public CustomScrollView(Context context) {
this(context, null);
}
public CustomScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
initScrollView();
}
@Override
protected float getTopFadingEdgeStrength() {
if (getChildCount() == 0) {
return 0.0f;
}
final int length = getVerticalFadingEdgeLength();
if (getScrollY() < length) {
return getScrollY() / (float)length;
}
return 1.0f;
}
@Override
protected float getLeftFadingEdgeStrength() {
if (getChildCount() == 0) {
return 0.0f;
}
final int length = getHorizontalFadingEdgeLength();
if (getScrollX() < length) {
return getScrollX() / (float)length;
}
return 1.0f;
}
@Override
protected float getRightFadingEdgeStrength() {
if (getChildCount() == 0) {
return 0.0f;
}
final int length = getHorizontalFadingEdgeLength();
final int rightEdge = getWidth() - getPaddingRight();
final int span = getChildAt(0).getRight() - getScrollX() - rightEdge;
if (span < length) {
return span / (float)length;
}
return 1.0f;
}
@Override
protected float getBottomFadingEdgeStrength() {
if (getChildCount() == 0) {
return 0.0f;
}
final int length = getVerticalFadingEdgeLength();
final int bottomEdge = getHeight() - getPaddingBottom();
final int span = getChildAt(0).getBottom() - getScrollY() - bottomEdge;
if (span < length) {
return span / (float)length;
}
return 1.0f;
}
/**
* @return The maximum amount this scroll view will scroll in response to
* an arrow event.
*/
public int getMaxScrollAmountV() {
return (int)(MAX_SCROLL_FACTOR * (getBottom() - getTop()));
}
public int getMaxScrollAmountH() {
return (int)(MAX_SCROLL_FACTOR * (getRight() - getLeft()));
}
private void initScrollView() {
mScroller = new Scroller(getContext());
setFocusable(true);
setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);
setWillNotDraw(false);
final ViewConfiguration configuration = ViewConfiguration.get(getContext());
mTouchSlop = configuration.getScaledTouchSlop();//触发事件距离
mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();//最低速度
mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();//最高速度
}
//只能包含一个子视图
@Override
public void addView(View child) {
if (getChildCount() > 0) {
throw new IllegalStateException("ScrollView can host only one direct child");
}
super.addView(child);
}
@Override
public void addView(View child, int index) {
if (getChildCount() > 0) {
throw new IllegalStateException("ScrollView can host only one direct child");
}
super.addView(child, index);
}
@Override
public void addView(View child, ViewGroup.LayoutParams params) {
if (getChildCount() > 0) {
throw new IllegalStateException("ScrollView can host only one direct child");
}
super.addView(child, params);
}
@Override
public void addView(View child, int index, ViewGroup.LayoutParams params) {
if (getChildCount() > 0) {
throw new IllegalStateException("ScrollView can host only one direct child");
}
super.addView(child, index, params);
}
//垂直滚动
private boolean canScrollV() {
View child = getChildAt(0);
if (child != null) {
int childHeight = child.getHeight();
return getHeight() < childHeight + getPaddingTop() + getPaddingBottom();
}
return false;
}
//水平滚动
private boolean canScrollH() {
View child = getChildAt(0);
if (child != null) {
int childWidth = child.getWidth();
return getWidth() < childWidth + getPaddingLeft() + getPaddingRight();
}
return false;
}
/**
* 表明内容是否达到填充视窗
*
* @return True if the content fills the viewport, false otherwise.
*/
public boolean isFillViewport() {
return mFillViewport;
}
/**
* Indicates this ScrollView whether it should stretch its content height to fill
* the viewport or not.
*
* @param fillViewport True to stretch the content's height to the viewport's
* boundaries, false otherwise.
*/
public void setFillViewport(boolean fillViewport) {
if (fillViewport != mFillViewport) {
mFillViewport = fillViewport;
requestLayout();
}
}
//设置箭头推动过滤
/**
* @return Whether arrow scrolling will animate its transition.
*
*/
public boolean isSmoothScrollingEnabled() {
return mSmoothScrollingEnabled;
}
/**
* Set whether arrow scrolling will animate its transition.
* @param smoothScrollingEnabled whether arrow scrolling will animate its transition
*/
public void setSmoothScrollingEnabled(boolean smoothScrollingEnabled) {
mSmoothScrollingEnabled = smoothScrollingEnabled;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
if (!mFillViewport) {
return;
}
final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
if (heightMode == MeasureSpec.UNSPECIFIED && widthMode == MeasureSpec.UNSPECIFIED) {
return;
}
if (getChildCount() > 0) {
final View child = getChildAt(0);
int height = getMeasuredHeight();
int width = getMeasuredWidth();
if (child.getMeasuredHeight() < height || child.getMeasuredWidth() < width) {
width -= getPaddingLeft();
width -= getPaddingRight();
int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY);
height -= getPaddingTop();
height -= getPaddingBottom();
int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(height,
MeasureSpec.EXACTLY);
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
}
}
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
// Let the focused view and/or our descendants get the key first
return super.dispatchKeyEvent(event) || executeKeyEvent(event);
}
/**
* You can call this function yourself to have the scroll view perform
* scrolling from a key event, just as if the event had been dispatched to
* it by the view hierarchy.
*
* @param event The key event to execute.
* @return Return true if the event was handled, else false.
*/
public boolean executeKeyEvent(KeyEvent event) {
mTempRect.setEmpty();
boolean handled = false;
if (event.getAction() == KeyEvent.ACTION_DOWN) {
switch (event.getKeyCode()) {
case KeyEvent.KEYCODE_DPAD_LEFT:
if (canScrollH()) {
if (!event.isAltPressed()) {
handled = arrowScrollH(View.FOCUS_LEFT);
} else {
handled = fullScrollH(View.FOCUS_LEFT);
}
}
break;
case KeyEvent.KEYCODE_DPAD_RIGHT:
if (canScrollH()) {
if (!event.isAltPressed()) {
handled = arrowScrollH(View.FOCUS_RIGHT);
} else {
handled = fullScrollH(View.FOCUS_RIGHT);
}
}
break;
case KeyEvent.KEYCODE_DPAD_UP:
if (canScrollV()) {
if (!event.isAltPressed()) {
handled = arrowScrollV(View.FOCUS_UP);
} else {
handled = fullScrollV(View.FOCUS_UP);
}
}
break;
case KeyEvent.KEYCODE_DPAD_DOWN:
if (canScrollV()) {
if (!event.isAltPressed()) {
handled = arrowScrollV(View.FOCUS_DOWN);
} else {
handled = fullScrollV(View.FOCUS_DOWN);
}
}
break;
}
}
return handled;
}
private boolean inChild(int x, int y) {
if (getChildCount() > 0) {
final int scrollX = getScrollX();
final int scrollY = getScrollY();
final View child = getChildAt(0);
return !(y < child.getTop() - scrollY || y >= child.getBottom() - scrollY
|| x < child.getLeft() - scrollX || x >= child.getRight() - scrollX);
}
return false;
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
/*
* This method JUST determines whether we want to intercept the motion.
* If we return true, onMotionEvent will be called and we do the actual
* scrolling there.
*/
/*
* Shortcut the most