自定义view总结(一)

1.getRawX、getRawY与getX、getY的区别

在编写android的自定义控件,或者判断用户手势操作时,往往需要使用MotionEvent中的getRawX()、getRawY()与getX()、getY()取得触摸点在X轴与Y轴上的距离,这四个方法都返回一个float类型的参数,单位为像素(Pixel)。getRawX()、getRawY()返回的是触摸点相对于屏幕的位置,而getX()、getY()返回的则是触摸点相对于View的位置。

以下两张图直观的表现了这几个方法的区别,在屏幕中央放置了一个Button,并为它注册了OnTouchListener,图中绿圆点为触摸点位置。

    

 

2.View中的getScrollX、getScrollY

getScrollX()与getScrollY()的值由调用View的scrollTo(int x, int y)或者scrollBy(int x, int y)产生,其中scrollTo是将View中的内容移动到指定的坐标x、y处,此x、y是相对于

View的左上角,而不是屏幕的左上角。scrollBy(int x, int y)则是改变View中的相对位置,参数x、y为距离上一次的相对位置。

文字解释总是不好理解的,那么我们就直接上图吧,直观一些。

               (图1)                                                         (图2)                                                                (图3)

1.图1中,屏幕中心放置了一个button,而button的内容被放置在了它的左上角。

2.调用button的scrollTo(-100, -100)方法,结果如图2所示,button内的内容被移至相对button左上角(-100,-100)的位置

3.对图2的button调用scrollBy(-100,-100)方法,结果如图3所示,button内的内容被移至相对于图2的(-100,-100)位置

这时的getScrollX()与getScrollY()的值为:

06-15 15:44:56.072  20471-20471/com.test.yangy.studiotest V/ScrollActivity﹕ btn scroll X=-200
06-15 15:44:56.072  20471-20471/com.test.yangy.studiotest V/ScrollActivity﹕ btn scroll Y=-200

值得注意的是,当View中的内容向右移动时,getScrollX()的值为负数,同理,向scrollTo与scrollBy的x中传入负数,view中的内容向右移动,反之向左。

当View中的内容向下移动时,getScrollY()的值为负数,同理,向scrollTo与scrollBy的y中传入负数,view中的内容向下移动,反之向上。

前言:关于控件的高度有两种获取方式,一个是获得控件的实际大小(getMeasuredHeight),就是包含显示部分和已显示的部分;

其中的ScrollView表示滑动的距离,通过getScrollY()方法获得。

scrollTo方法应该是表示 相对view初始位置 横向移动了x,纵向移动了y的距离,而view的初始位置是不变的,

所以不管调用多少次scrollTo()方法,view移动的都是同一个位置。至于ScrollBy方法,
其实内部就一句代码:

其实最终也是调用 scrollTo 方法。
 只是每次是相对于 当前View 的位置 移动 x和y的距离。
至于,为什么调用 scrollBy(100,100) ,view不是按照我们的直觉向右下角移动,而是向左上角移动?
这是因为view移动时所使用的参考对象不同,view移动的参考对象是手机屏幕。
确切的说,本身View所在的画布是固定,不动的。
而我们手机屏幕面积有限,只能看到画布的一小片区域,这时执行 scrollBy(100,100),
是手机屏幕相对于画布向右下角移动了(100,100)的距离,但对于用户来说,是view控件向左上角移动了位置。

public boolean dispatchTouchEvent(MotionEvent ev)
 如果return false,
    事件分发分为两种情况:如果当前 View 获取的事件直接来自 Activity,则会将事件返回给Activity的onTouchEvent进行消费;

  如果当前 View 获取的事件来自外层父控件,则会将事件返回给父View的onTouchEvent进行消费。


如果return super.dispatchTouchEvent(ev),
    事件分发分为两种情况:如果当前View是ViewGroup,则事件会分发给onInterceptTouchEvent方法进行处理;

 
    如果当前View是普通View,则事件直接交给onTouchEvent方法进行处理;

为什么看第三方源码时,有时候写getAction,有时候写getActionMasked呢?从上面的分析中可以看出来个所以然:

getAction表示触摸动作的原始32位信息,当没有多点触控时,高8位即为0,这个时候getAction == getActionMasked。
所以当我们确定我们自定义的View不会使用到多点触控时,就可以直接使用getAction来表示具体的触摸动作。
但如果需要使用多点触控,或者是不确定后面会不会使用多点触控,则使用getActionMasked则是最保险的操作,所以我们经常看到下面这两种写法:


getGlobalVisibleRect方法的作用是获取视图在屏幕坐标中的可视区域

另外需要说的是,getGlobalVisibleRect还可以接受第二个Point类型的参数:

      targetView.getGlobalVisibleRect(Rect r, Point gobalOffset)

调用完毕后,globalOffset的值就是targetView原点偏离屏幕坐标原点的距离。

 

1. 讲解这两个方法之前, 首先我们先看下一个图片, 只是一个简单的Activity, 一个Linearlayout(背景为红色) 里面放了一个正方形(背景为蓝色)

并且边距已经标出.

2.  接下来我们看这两个方法获得的值(背景为蓝色 View 的矩形框)

//Kotlin code

var globalVisibleRect = Rect()

testVisiblty.getGlobalVisibleRect(globalVisibleRect)

var localVisibleRect = Rect()

testVisiblty.getLocalVisibleRect(localVisibleRect);

Log.d("MainActivity", "test globalVisibleRect::" + globalVisibleRect)

Log.d("MainActivity", "test localVisibleRect::" + localVisibleRect)

3.  查看log输出

02-26 07:00:31.361 9716 9716 D MainActivity: test globalVisibleRect::Rect(150, 150 - 750, 750)

可以看出这个坐标的获得是想对于屏幕左上角的坐标. (矩形左上角距离屏幕顶端的距离是150[left], 150[top]), 右下角距离屏幕的距离

是Right = [Left+600px] = 750, 同理Bottom 是一样的

02-26 07:00:31.362  9716  9716 D MainActivity: test localVisibleRect::Rect(0, 0 - 600, 600)

可以看出这个坐标的获得是想对于自身的坐标.(矩形左上角距离自己的距离为0,0) 同理右下角距离左上角的距离为(600,600)

4.  从上面来看, 没有什么啊, 就是一个普通的坐标,一个相对于屏幕左上角, 一个是想对于自己的左上角. OK .现在我们改变蓝色矩形框的位置, 看如下图。把蓝色矩形框位置向左边偏移450px(150dp)

 

查看log输出:

02-26 10:15:38.114 12203 12203 D MainActivity: test globalVisibleRect::Rect(150, 150 - 300, 750)

这个很好理解了. 矩形左上角距离屏幕顶端的距离是(150, 150) 右下角距离屏幕顶端的距离是(300, 750)

02-26 10:15:38.114 12203 12203 D MainActivity: test localVisibleRect::Rect(450, 0 - 600, 600)

但是这个坐标怎么理解???

首先我们要明白这个蓝色矩形是一个600x600的矩形, 而我们向左移动了450px, 那么现在可以看到的是150x600, 这个时候可见区域的顶点

坐标相对于原图形的600x600来说是距离原图形的左上角距离是450px, 同理600也是.

总结:

getGlobalVisibleRect() 是view可见区域相对与屏幕来说的坐标位置.

getLocalVisibleRect()是view可见区域想对于自己坐标的位置.

一定要记清楚是可见区域.

 

这几个方法的意思很简单,都是对比父容器有多少距离的

这四个方法是对应的,一个是获取上边距,一个是下边距,但是它们的对应点是一样的。

什么是对应点?

任何时候测试距离都要有2个点,就想是一条直线的2个点,直线的一端,到直线的另一端,就是这直线的距离吧。

getTop和getButtom

它们的对应点为父容器的上面 
 

 

 

getRight和getLeft

则是按父容器的左边为对应点
 

Android系统手机屏幕的左上角为坐标系,同时y轴方向与笛卡尔坐标系的y轴方向想反。提供了

getLeft(), 
getTop(), 
getBottom(), 
getRight()

这些API来获取控件在Parent中的相对位置。同时也提供了

getLocalVisibleRect()
getGlobalVisibleRect()
getLocationOnScreen()
getLocationInWindow()

这些API来获取控件在屏幕中的绝对位置。详情可参考:android应用程序中获取view的位置

如果要将View中的内容滚动到相应到指定位置,可以使用这些API

scrollTo()
scrollBy()

如果要改变整个View在屏幕中的位置,可以使用下列API:

offsetLeftAndRight(int offset) // 用于左右移动
offsetTopAndBottom(int offset) // 用于上下移动

下面简要总结一下scrollTo(),scrollBy(),getScrollX(), getScrollY()这些方法

scrollTo(int x, int y) 是将View中内容滑动到相应的位置,参考的坐标系原点为parent View的左上角。

调用scrollTo(100, 0)表示将View中的内容移动到x = 100, y = 0的位置,如下图所示。注意,图中黄色矩形区域表示的是一个parent View,绿色虚线矩形为parent view中的内容。一般情况下两者的大小一致,本文为了显示方便,将虚线框画小了一点。图中的黄色区域的位置始终不变,发生位置变化的是显示的内容。

同理,scrollTo(0, 100)的效果如下图所示:

scrollTo(100, 100)的效果图如下:

若函数中参数为负值,则子View的移动方向将相反。

 

关于scrollTo()方法中参数值为正,却向左移动,参数值为负,却向右移动(这地方确实很怪)的一些理解:

scrollTo()方法本身滚动的是View的内容,View本身位置不变。可以将该View想象成一个带滚动条的窗体,当滚动条向右移动时,原本窗体显示的内容向左移动;当滚动条向下移动时,原本窗体显示的内容向上移动。这也就解释了为什么scrollTo()方法中参数大小0,View向左移动,参数小于0,View向右移动。

 

scrollBy(int x, int y)其实是对scrollTo的包装,移动的是相当位置。 scrollTo(int x, int y)的源码和scrollBy(int x, int y)源码如下所示.

复制代码

 /**
     * 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<pre name="code" class="java">    /**
     * 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();
            }
        }
    }

复制代码

public void scrollBy(int x, int y) { scrollTo(mScrollX + x, mScrollY + y); }

可见,mScrollX和mScrollY是View类中专门用于记录滑动位置的变量。这两个函数最终调用onScrollChanged()函数,感兴趣者可以参考他们的源代码。

理解了scrollTo(int x, int y)和scrollBy(int x, int y)的用法,就不难理解getScrollX() 和getScrollY()。这两个函数的源码如下所示:

 

复制代码

 /**
     * 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;
    

复制代码

复制代码

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

复制代码

类: android-android.view.View

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值