android CustomScrollView上下左右同时滑动

这个博客介绍了如何创建一个自定义的ScrollView,使其能够支持上下左右同时滑动。通过覆盖滚动边缘的渐变效果、处理箭头事件以及实现平滑滚动,该CustomScrollView可以处理多方向的滚动操作。
摘要由CSDN通过智能技术生成

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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值