Scroll滑动Activity

根据我参考的所有资料  都是需要先了解 ScrollBy   ScrollTo  mScrollX mScrollY的这些概念了。

看别人讲解就和看API没什么区别 ,很多东西不是很理解,然后我就做了个例子。


imageView.scrollTo(0,0);
        btnScrollTo_1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                imageView.scrollTo(100,20);
                Log.i(TAG,"mScrollX:"+imageView.getScrollX()+"   mScrollY:"+imageView.getScrollY());
            }
        });
        btnScrollTo_2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                imageView.scrollTo(20,100);
                Log.i(TAG, "mScrollX:" + imageView.getScrollX() + "   mScrollY:" + imageView.getScrollY());
            }
        });
        btnLeft.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                imageView.scrollBy(10,0);
                Log.i(TAG, "mScrollX:" + imageView.getScrollX() + "   mScrollY:" + imageView.getScrollY());
            }
        });
        btnRight.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                imageView.scrollBy(-10, 0);
                Log.i(TAG, "mScrollX:" + imageView.getScrollX() + "   mScrollY:" + imageView.getScrollY());
            }
        });

        btnDown.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                imageView.scrollBy(0, -10);
                Log.i(TAG, "mScrollX:" + imageView.getScrollX() + "   mScrollY:" + imageView.getScrollY());
            }
        });
        btnUp.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                imageView.scrollBy(0, 10);
                Log.i(TAG, "mScrollX:" + imageView.getScrollX() + "   mScrollY:" + imageView.getScrollY());
            }
        });

下边的Log分别是  btnUp   btnDown   btnLeft   btnrRight    btnScrollTo_1   btnScrollTo_2  按下时的日志:




记住这张图 记住张图  记住这张图


scrollBy

简单来说  ScrollBy 就是使View 或者控件进行移动的时候调用,两个参数 x,y   ,比如你想从家往学校走,这些两个参数就是你设定的你走路的步伐长度,比如你把参数设置为(1m,0m);的话,那么就是你去学校的步长为每步一米。  每调用一次,那么你的腿就往前迈一步。y就是换一个方向啦。当然这些值也可以为负,比如你到学校后每调用一次(-1m,0)的话你就是从学校往家的路程中迈进一步,这里是以像素为单位的。叫做偏移量。

/**
 * Move the scrolled position of your view. This will cause a call to
 * {@link #onScrollChanged(int, int, int, int)} and the view will be
 * invalidated.
 * @param x the amount of pixels to scroll by horizontally
 * @param y the amount of pixels to scroll by vertically
 */
public void scrollBy(int x, int y) {
    scrollTo(mScrollX + x, mScrollY + y);
}

scrollTo

这个方法的调用就用一个词形容就是,一步到位,填充坐标后就一步迈向这个点。在你的View上设置滚动位置时调用。

/**
 * Set the scrolled position of your view. This will cause a call to
 * {@link #onScrollChanged(int, int, int, int)} and the view will be
 * invalidated.
 * @param x the x position to scroll to
 * @param y the y position to scroll to
 */
public void scrollTo(int x, int y) {
    if (mScrollX != x || mScrollY != y) {
        int oldX = mScrollX;
        int oldY = mScrollY;
        mScrollX = x;
        mScrollY = y;
        invalidateParentCaches();
        onScrollChanged(mScrollX, mScrollY, oldX, oldY);
        if (!awakenScrollBars()) {
            postInvalidateOnAnimation();
        }
    }
}

getScrollX & getScrollY

这两个方法就是获取 你的步伐长度的,在scrollBy方法中是指定步伐长度,而这两个方法就是获得在X轴和Y轴上的步伐长度 在这里叫做 偏移量

/**
 * Return the scrolled left position of this view. This is the left edge of
 * the displayed part of your view. You do not need to draw any pixels
 * farther left, since those are outside of the frame of your view on
 * screen.
 *
 * @return The left edge of the displayed part of your view, in pixels.
 */
public final int getScrollX() {
    return mScrollX;
}


相对位置:

通过API可以看出一个问题 ,它进行移动或者其他获取scrollX时,都是以左上角这个位置进行计算的,或者是进行相对位移的。比如一个ImageView控件进行滚动,他是按照这个控件进行移动的,还有View都是按照这个规则定点计算获取的。并不是屏幕的这个View。




说完基础说正事:

看看目录结构:


代码还是相当麻烦的。只要是贴一下SlidingMenu这个文件。我自己仿写的也是一头雾水。大部分理解了。还不算全懂,

package com.example.hejingzhou.sliddemo;

import android.app.Activity;
import android.content.Context;
import android.graphics.Point;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Display;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.RelativeLayout;
import android.widget.Scroller;

/**
 * Created by Hejingzhou on 2016/3/5.
 */
public class SlidingMenu extends RelativeLayout {

    private String TAG = getClass().getSimpleName();

    private View mSlidingView;//中View
    private View mMenuView;//左View
    private View mDetailView;//右View

    private RelativeLayout bgShade;
    private int screenWidth, screenHeight;//屏幕宽高
    private Context mContext;
    private Scroller mScroller;
    private VelocityTracker mVelocityTracker;//跟踪速度
    private int mTouchSlop;//触摸最短距离
    private float mLastMotionX, mLastMotionY;
    private static final int VELOCITY = 50;
    private boolean mIsBeingDragged = true;//拖动是存在的
    private boolean tCanSlideLeft = true, tCanSlideRight = false;//左划true右划false
    private boolean hasClickLeft = false, hasClickRight = false;

    public SlidingMenu(Context context) {
        super(context);
        Log.i("SlidingMenu", "参数个数  " + 1);
        Init(context);
    }

    public SlidingMenu(Context context, AttributeSet attrs) {
        super(context, attrs);
        Log.i("SlidingMenu", "参数个数  " + 2);
        Init(context);
    }

    public SlidingMenu(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        Log.i("SlidingMenu", "参数个数  " + 3);
        Init(context);
    }

    private void Init(Context context) {
        mContext = context;
        bgShade = new RelativeLayout(context);//在这个继承的类中实例化创建一个相对布局
        mScroller = new Scroller(getContext());//实例化滑动类
        mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
        /**
         * 主要是构建一个相对布局宽高是手机的宽高像素
         */
        WindowManager windowManager = ((Activity) context).getWindow().getWindowManager();// 创建一个WindowsManager对象
        Display display = windowManager.getDefaultDisplay();//实例化一个显示屏操作的类
        Point point = new Point();
        display.getSize(point);
        screenWidth = point.x;
        Log.i(TAG, "像素宽" + screenWidth);
        screenHeight = point.y;
        Log.i(TAG, "像素高" + screenHeight);

        LayoutParams bgParams = new LayoutParams(screenWidth, screenHeight);/*****************************/
        bgParams.addRule(RelativeLayout.CENTER_IN_PARENT);//设置位于父布局的中间
        bgShade.setLayoutParams(bgParams);
        Log.i("SlidingMenu","初始化完成");
    }



 /*   public void addViews(View left, View center, View right) {
        setLeftView(left);
        setRightView(right);
        setCenterView(center);
    }*/

    /**
     * 设置左MenuView的宽高参数  宽是WRAP_CONTENT  高是MATCH_ARENT
     *
     * @param view
     */
    public void setLeftView(View view) {
        RelativeLayout.LayoutParams behindParams = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.MATCH_PARENT);
        addView(view, behindParams);
        mMenuView = view;
    }

    /**
     * 设置右MenuView的宽高参数  宽是WRAP_CONTENT  高是MATCH_ARENT
     *
     * @param right
     */
    public void setRightView(View right) {
        RelativeLayout.LayoutParams behindParams = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.MATCH_PARENT);
        behindParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
        addView(right, behindParams);
        mDetailView = right;
    }

    /**
     * 设置中间主View的宽高参数  宽是MATCH_ARENT  高是MATCH_ARENT
     *
     * @param center
     */
    public void setCenterView(View center) {
        RelativeLayout.LayoutParams aboveParams = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.MATCH_PARENT);
        RelativeLayout.LayoutParams bgParams = new RelativeLayout.LayoutParams(screenWidth, screenHeight);
        bgParams.addRule(RelativeLayout.CENTER_IN_PARENT);
        View bgShadeContent = new View(mContext);
        Log.i(TAG,"程序获取资源没出错_前");
        //bgShadeContent.setBackgroundResource(R.drawable.shade_bg);
        Log.i(TAG, "程序获取资源没出错_后");

        bgShade.addView(bgShadeContent, bgParams);
        addView(bgShade, bgParams);
        addView(center, aboveParams);
        mSlidingView = center;
        mSlidingView.bringToFront();
    }

    /**
     * public void  scrollBy (int x, int y)
     * Added in API level 1
     * Move the scrolled position of your view.移动滚动视图的位置
     * This will cause a call to onScrollChanged(int, int, int, int) and the view will be invalidated.视图将失效。
     * Parameters
     * x int: the amount of pixels to scroll by horizontally水平滚动的像素的数量
     * y int: the amount of pixels to scroll by vertically
     */
    @Override
    public void scrollTo(int x, int y) {
        super.scrollTo(x, y);
        postInvalidate();//刷新View,导致失效发生在随后通过事件循环周期
    }

    /**
     * 计算卷轴的滑动  进行相应的左右移动
     */
    public void computeScroll() {
        if (!mScroller.isFinished()) {//如果滚动还没有完成
            if (mScroller.computeScrollOffset()) {//返回最新的滚动位置,返回true表示已经完成滚动
                int oldX = mSlidingView.getScrollX();
                int oldY = mSlidingView.getScrollY();//得到一个View到另一个View的偏移量
                int x = mScroller.getCurrX();
                int y = mScroller.getCurrY();//当前在XY轴上的抵消值
                if (oldX != x || oldY != y) { //手指触电进行了移动
                    if (mSlidingView != null) {
                        mSlidingView.scrollTo(x, y);//直接就把这个View移动到(X,Y)这个位置
                        if (x < 0) bgShade.scrollTo(x + 20, y);//如果当前布局在相对原点的右边,那么这个相对布局往左移动20单位
                        else bgShade.scrollTo(x - 20, y);//向右移动20单位
                    }
                }
                invalidate();//
            }
        }
    }

    private boolean canSlideLeft = true;
    private boolean canSlideRight = false;//左右滑动标志

    /**
     * 传进来的 ture OR flase
     *
     * @param left
     * @param right
     */
    public void setCanSliding(boolean left, boolean right) {
        canSlideLeft = left;
        canSlideRight = right;
    }

    /**
     * 触摸监听拦截
     *
     * @param event
     * @return
     */
    public boolean onInterceptTouchEvent(MotionEvent event) {
        final int action = event.getAction();//返回正在执行的操作
        final float x = event.getX();
        final float y = event.getY();//gatXY(),得到的是自身控件的左上角坐标
        switch (action) {
            case MotionEvent.ACTION_DOWN: //按下
                mLastMotionX = x;
                mLastMotionY = y;
                mIsBeingDragged = false;//拖动是否存在
                if (canSlideLeft) {
                    mMenuView.setVisibility(View.VISIBLE);//设置启动这个可见View
                    mDetailView.setVisibility(View.INVISIBLE);//这种View是无形的,但它仍然占用空间布局的目的。
                }
                if (canSlideRight) {
                    mMenuView.setVisibility(View.INVISIBLE);
                    mDetailView.setVisibility(View.VISIBLE);
                }
                break;
            case MotionEvent.ACTION_MOVE:
                final float dx = x - mLastMotionX;//dx = 移动后当前的控件坐标 - 移动前按下时的坐标
                final float xDiff = Math.abs(dx);//绝对值
                final float dy = y - mLastMotionY;
                final float yDiff = Math.abs(dy);
                /**
                 * 通过手滑动的焦点坐标和View的缩放比例进行判断
                 * 主要是证明是否进行了滑动 左划 Or 右划
                 */
                if (xDiff > mTouchSlop && xDiff > yDiff) {//如果差值大于最小距离并且x轴差值大于y轴差值
                    if (canSlideLeft) {
                        /**
                         *  float oldScrollX = mSlidingView.getScrollX(); 写错一步 写成getScaleX()
                         *  这一步导致在从中间的Activity滑向左菜单后,再次划回主Actyvity时
                         *  还需要划到主Activity 右View后才能带动左菜单
                         */
                        float oldScrollX = mSlidingView.getScrollX();//获取一个View的X(横向)缩放比例搜索,如果没有调用 setScaleX,那么返回1。
                        if (oldScrollX < 0) {
                            mIsBeingDragged = true;//证明是拖动的
                            mLastMotionX = x;
                        } else {
                            if (dx > 0) {
                                mIsBeingDragged = true;
                                mLastMotionX = x;
                            }
                        }
                    } else if (canSlideRight) {
                        float oldScrollX = mSlidingView.getScrollX();
                        if (oldScrollX > 0) {
                            mIsBeingDragged = true;
                            mLastMotionX = x;
                        } else {
                            if (dx < 0) {
                                mIsBeingDragged = true;
                                mLastMotionX = x;
                            }
                        }
                    }
                }
                break;
        }
        return mIsBeingDragged;
    }

    /**
     * 触摸键听 和 滑动的算法 的详细操作
     *
     * @param event
     * @return
     */
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (mVelocityTracker == null) {
            mVelocityTracker = VelocityTracker.obtain();//如果滑动速度为空,就返回滑动速度类,继续监听。
        }
        mVelocityTracker.addMovement(event);//将触摸事件添加到速度实例化的类中
        final int action = event.getAction();//返回当前的事件
        final float x = event.getX();
        final float y = event.getY();
        switch (action) {
            case MotionEvent.ACTION_DOWN:
                if (!mScroller.isFinished()) {//如果滑动还没有完成,那么就停止滑动
                    mScroller.abortAnimation();
                }
                mLastMotionX = x;
                mLastMotionY = y;
                /**
                 * 如果mSlidingView的滑动偏移量和负的菜单View宽 并且 最后焦点的X小于菜单的宽 返回false
                 */
                if (mSlidingView.getScrollX() == -getMenuViewWidth() && mLastMotionX < getMenuViewWidth()) {
                    return false;
                }
                if (mSlidingView.getScrollX() == getMenuViewWidth() && mLastMotionX > getMenuViewWidth()) {
                    /**
                     * 第二处错误 第一遍写的时候反悔了ture    ,但是改为flase后程序呈现效果并未发生变化
                     */
                    return false;
                }
                break;
            case MotionEvent.ACTION_MOVE:
                    if (mIsBeingDragged) {
                    final float deltaX = mLastMotionX - x;//按下时的焦点X坐标 - 现在的X坐标
                    mLastMotionX = x;
                    float oldScrollX = mSlidingView.getScrollX();//获取这个中间Activity的这个mScrollX值
                    float scrollX = oldScrollX + deltaX;
                    if (canSlideLeft) {
                        if (scrollX > 0) {
                            scrollX = 0;
                        }
                    }
                    if (canSlideRight) {
                        if (scrollX < 0)
                            scrollX = 0;
                    }
                    if (deltaX < 0 && oldScrollX < 0) {
                        final float leftBound = 0;
                        final float rightBound = -getMenuViewWidth();
                        if (scrollX > leftBound) {
                            scrollX = leftBound;
                        } else if (scrollX < rightBound) {
                            scrollX = rightBound;
                        }
                    } else if (deltaX > 0 && oldScrollX > 0) {
                        final float rightBound = getDetailViewWidth();
                        final float leftBound = 0;
                        if (scrollX < leftBound) {
                            scrollX = leftBound;
                        } else if (scrollX > rightBound) {
                            scrollX = rightBound;
                        }
                    }
                    if (mSlidingView != null) {
                        mSlidingView.scrollTo((int) scrollX, mSlidingView.getScrollY());//固定中间这个Activity的位置
                        if (scrollX < 0)
                            bgShade.scrollTo((int) scrollX + 20, mSlidingView.getScrollY());//左移20固定
                        else
                            bgShade.scrollTo((int) scrollX - 20, mSlidingView.getScrollY());//右移20固定
                    }

                }
                break;
            case MotionEvent.ACTION_CANCEL:
            case MotionEvent.ACTION_UP:
                if (mIsBeingDragged) {
                    final VelocityTracker velocityTracker = mVelocityTracker;
                    velocityTracker.computeCurrentVelocity(100);//最大速度
                    float xVelocity = velocityTracker.getXVelocity();//得到计算的速度
                    int oldScrollX = mSlidingView.getScrollX();//将mSlidingView的ScrollX
                    int dx = 0;
                    if (oldScrollX <= 0 && canSlideLeft) {
                        /**
                         * 第六处错误,多写一句错误的 导致在左菜单中向主View中划的过程中  不会慢慢滑动过渡过去
                         * 会跟着手的触点  进行平移
                         */
                       // if (xVelocity <= 0 && canSlideLeft) {
                            if (xVelocity > VELOCITY) {
                                dx = -getMenuViewWidth() - oldScrollX;
                            } else if (xVelocity < -VELOCITY) {
                                dx = -oldScrollX;
                                if (hasClickLeft) {
                                    /**
                                     * 第七处错误  没有写hasClickLeft = false; 点击按键时进行实时停止
                                     */
                                    hasClickLeft = false;
                                    setCanSliding(tCanSlideLeft, tCanSlideRight);
                                }
                            } else if (oldScrollX < -getMenuViewWidth() / 2) {
                                dx = -getMenuViewWidth() - oldScrollX;
                            } else if (oldScrollX >= -getMenuViewWidth() / 2) {
                                dx = -oldScrollX;
                                if (hasClickLeft) {
                                    hasClickLeft = false;
                                    setCanSliding(tCanSlideLeft, tCanSlideRight);
                                }
                         //   }
                        }
                        if (oldScrollX >= 0 && canSlideRight) {
                            if (xVelocity < -VELOCITY) {
                                dx = getDetailViewWidth() - oldScrollX;
                            } else if (xVelocity > VELOCITY) {
                                dx = -oldScrollX;
                                if (hasClickRight) {
                                    hasClickRight = false;
                                    setCanSliding(tCanSlideLeft, tCanSlideRight);
                                }
                            } else if (oldScrollX > getDetailViewWidth() / 2) {
                                dx = getDetailViewWidth() - oldScrollX;
                            } else if (oldScrollX <= getDetailViewWidth() / 2) {
                                dx = -oldScrollX;
                                if (hasClickRight) {
                                    hasClickRight = false;
                                    setCanSliding(tCanSlideLeft, tCanSlideRight);
                                }
                            }
                        }
                        smoothScrollTo(dx);
                    }
                    /**
                     * 第三处错误 没有写 break  添加后没有什么变化
                     */
                    break;
                }
        }
        /**
         * 第四处错误 返回的super.onTouchEvent(event)
         */
        return true;
    }

    /**
     * 最左边的Menu菜单的View
     */
    public void showLeftView() {
        int menuWidth = mMenuView.getWidth();//这个菜单的View值
        int oldScrollX = mSlidingView.getScrollX();//SlidingView的mScrollX值
        if (oldScrollX == 0) {//如果SlidingView就没动
            mMenuView.setVisibility(View.VISIBLE);
            mDetailView.setVisibility(View.INVISIBLE);
            smoothScrollTo(-menuWidth);//开始平滑 在X轴上的 -menuWidth像素
            tCanSlideLeft = canSlideLeft;//左划置为ture
            tCanSlideRight = canSlideRight;//右划置为flase
            hasClickLeft = true;//左菜单Button按下
            setCanSliding(true, false);
        } else if (oldScrollX == -menuWidth) {//中间主视图SlidingView在右划视图的状态下
            smoothScrollTo(menuWidth);//向左移动menuWidth
            if (hasClickLeft) {//左Button置为false
                hasClickLeft = false;
                setCanSliding(tCanSlideLeft, tCanSlideRight);//设置左右划动状态标记
            }
        }
    }

    /**
     * 最右边的DetailView视图
     */
    public void showRightView() {
        int menuWidth = mDetailView.getWidth();
        int oldScrollx = mSlidingView.getScrollX();
        if (oldScrollx == 0) {
            mMenuView.setVisibility(View.INVISIBLE);
            mDetailView.setVisibility(View.VISIBLE);
            smoothScrollTo(menuWidth);
            tCanSlideLeft = canSlideLeft;
            tCanSlideRight = canSlideRight;
            hasClickRight = true;
            setCanSliding(false, true);
        } else if (oldScrollx == menuWidth) {
            smoothScrollTo(-menuWidth);
            if (hasClickRight) {
                hasClickRight = false;
                setCanSliding(tCanSlideLeft, tCanSlideRight);
            }
        }
    }

    /**
     * 平滑开始
     *
     * @param dx 平滑X轴的距离参数
     */
    private void smoothScrollTo(int dx) {
        /**
         * 第五处错误  写成 -500  这个值直接影响着 从主View 划回到左菜单的时间
         */
        int duration = 500;
        int oldScrollX = mSlidingView.getScrollX();
        /**
         * startX 水平滑动的偏移量
         * startY 垂直滑动的偏移量
         * dx dx水平滑动距离,+向左
         * dy dy竖直滑动距离,+向上
         *duration 水平滑动的时间
         */
        mScroller.startScroll(oldScrollX, mSlidingView.getScrollY(), dx
                , mSlidingView.getScrollY(), duration);
        invalidate();
    }

    /**
     * 获取最右边的DetailView的宽
     *
     * @return
     */
    private int getDetailViewWidth() {
        if (mDetailView == null) {
            return 0;
        }
        return mDetailView.getWidth();
    }

    /**
     * 获得菜单的宽
     * @return
     */
    private int getMenuViewWidth() {
        if (mMenuView == null) {
            return 0;
        }
        return mMenuView.getWidth();//返回你的View的宽
    }
}


很晕菜,

效果:                                        


源码   http://download.csdn.net/detail/csdnhejingzhou/9456648 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值