View的基本概念了解

View方面的东西,乍一眼看去,我真是满脸懵比,雾里看花般难受。

View是所有控件的基类,是一种界面层控件的抽象,代表着一个控件。连ViewGroup都是其子类。而ViewGroup可以直译为控件组,可以包含多个View。
一个View既可以表示一个控件,也可以是多个控件组成的一组控件。

一,View的基础知识

1.1 View的位置参数

一个矩形有四个点,而决定View的位置同样有四个点,分别是left,top,right,bottom。
其中left表示View的左上角距离左边的距离,top表示View的左上角距离上边的距离。
right表示View的右下角距离左边的距离,bottom表示View的右下角距离上边的距离。
当然,这些坐标相对于它的父容器而言的。

View的布局如下:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

        <asule.myview.ManualView
            android:background="#123456"
            android:layout_centerInParent="true"
            android:layout_width="150dp"
            android:layout_height="150dp" />

</RelativeLayout>

下面父容器指的是RelativeLayout
这里写图片描述

那么很容易的可以获得View的宽高
    int width=right-left;
    int height=bootom-top;

而得到这个四个位置参数的值,也很简单。
    通过View的getLeft,getRight,getTop,getBottom方法。

在android3.0以后,View中又提供了几个位置参数:x,y,transactionX,transactionY四个参数。
x,y表示的是当前View在父容器中左上角的位置,而transactionX和transactionY表示的是View的左上角相对于父容器的偏移量。
所以android3.0以前,左上角的位置就是(left,top),而现在有了偏移量就变为(left+transactionX,top+transactionY)。

x=left+transactionX;
y=top+transactionY;

而默认情况下,transactionX和transactionY都为0。如下:

System.out.println("transactionX:" + getTranslationX());
System.out.println("transactionY:" + getTranslationY());

System.out: transactionX:0.0
System.out: transactionY:0.0

1.2 MotionEvent和TouchSlop

MotionEvent处理一些手指在屏幕上产生的事件,如
MotionEvent.ACTION_DOWN,手指按下。
MotionEvent.ACTION_MOVE,手指在屏幕上滑动。
MotionEvent.ACTION_UP,手指松开。

MotionEvent可以监听我们手指产生事件的同时,还可以得到点击事件位置的x和y的坐标。

int x= (int) event.getX();
int y= (int) event.getY();
int rawX=(int)event.getRawX();
int rawY= (int) event.getRawY();

getx/getY和getRawX/getRawY有很大的不同。
getx/getY得到的是点击事件的位置相对于这个View左上角的x,y坐标。
getRawX/getRawY得到的是点击事件的位置相对于手机屏幕左上角的x,y坐标。手机屏幕的x,y为(0,0)

TouchSlop是系统能够识别的可以被滑动的最小距离,如果你滑动的记录小于这个值,那么将不会进行滑动。会认为你滑动距离太短。
它的作用就是,在处理滑动的时候,可以进行对滑动距离的过滤。

得到系统认为的最小滑动距离:
int mTouchSlop=ViewConfiguration.get(getContext()).getScaledTouchSlop();

1.3 VelocityTracker,GestureDetector,Scroller

VelocityTracker的例子:

public boolean onTouchEvent(MotionEvent event) {
        if (velocityTracker==null){
            //创建VelocityTracker对象
            velocityTracker = VelocityTracker.obtain();
        }
        //在onTouchEvent方法中追踪当前MotionEvent事件的速度
        velocityTracker.addMovement(event);

        //units的单位是毫秒,表示在多少毫秒内来计算的速度。假如你在50毫秒内完成了滑动,那么将计算不出来速度。
        velocityTracker.computeCurrentVelocity(100);
        /*
            computeCurrentVelocity另一个重载方法computeCurrentVelocity(int units, float maxVelocity)
            maxVelocity表示最大速率,如果速度大于了maxVelocity,显示的速度为maxVelocity。如果小于的话,那么就显示正常的速度。
            velocityTracker.computeCurrentVelocity(1000, (float)20);
        */
        xVelocity = (int) velocityTracker.getXVelocity();
        yVelocity = (int) velocityTracker.getYVelocity();

        /*
            ACTION_DOWN和ACTION_UP的事件速度一般都为0,真正需要考虑的是ACTION_MOVE时的速度。
            怎么计算速度?
                速度=(终点位置-起点位置)/规定的毫秒值

            本例中规定的毫秒值的是100毫秒。
            从左往右滑动,
                    滑动的方向和x的正方向相同,那么x方向上的速度就为正值
                        如果向下倾斜,与y的正方向相同,那么y方向的速度为正值。
            (android中的正方向是水平向右和垂直向下。)
        */
        switch (event.getAction()){
            case MotionEvent.ACTION_DOWN:
                System.out.println("ACTION_DOWN");
                System.out.println("xVelocity:" + xVelocity);
                System.out.println("yVelocity:" + yVelocity);
                break;
            case MotionEvent.ACTION_MOVE:
                System.out.println("ACTION_MOVE");
                System.out.println("xVelocity:" + xVelocity);
                System.out.println("yVelocity:" + yVelocity);
                break;
            case MotionEvent.ACTION_UP:
                System.out.println("ACTION_UP");
                System.out.println("xVelocity:" + xVelocity);
                System.out.println("yVelocity:" + yVelocity);
                break;
        }
        return true;
    }
//当不需要使用VelocityTracker时重置并回收内存
velocityTracker.clear();
velocityTracker.recycle();

GestureDetector:

    private MyGestureDetectorListener listener;
    private GestureDetector detector;

    private void init() {
        //创建GestureDetector对象并实现OnGestureListener接口
        listener = new MyGestureDetectorListener();
        detector = new GestureDetector(listener);
    }

    /*
        轻轻触碰屏幕并抬起
                onDown---->onSingleTapUp

        长按屏幕后抬起
                onDown---->onShowPress---->onLongPress

        手指在屏幕上滑动,最后松开
                onDown---->onScroll(很多次调用)---->onFling
    */

    class MyGestureDetectorListener implements GestureDetector.OnGestureListener{
        //手指轻轻触碰屏幕触发一瞬间调用
        @Override
        public boolean onDown(MotionEvent e) {
            System.out.println("onDown");
            return true;
        }

        //手指轻轻触碰屏幕,但并没有松开或拖动
        @Override
        public void onShowPress(MotionEvent e) {
            System.out.println("onShowPress");
        }

        //手指轻轻触碰屏幕,松开。表示单击行为
        @Override
        public boolean onSingleTapUp(MotionEvent e) {
            System.out.println("onSingleTapUp");
            return true;
        }

        //手指在按下并滑动
        @Override
        public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
            System.out.println("onScroll");
            return true;
        }

        //手指长按
        @Override
        public void onLongPress(MotionEvent e) {
            System.out.println("onLongPress");
        }

        //手指按下屏幕快速滑动后松开
        @Override
        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
            System.out.println("onFling");
            System.out.println("velocityX"+velocityX);
            System.out.println("velocityY"+velocityY);
            return true;
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        //接管View的onTouchEvent方法
        return detector.onTouchEvent(event);
    }

Scroller:

弹性滑动对象,scroolTo和scroolBy两种方法的滑动都是瞬间完成的,没有过渡的滑动效果在。
而Scroller可以完成这个过渡效果。
它本身无法让View弹性滑动,它需要和View的computeScroll方法配合才能完成这个功能。
如何使用Scroller?会在下面介绍scroolTo和scroolBy时通过一个侧滑的demo来使用Scroller。

二,View的滑动

2.1 使用scrollTo和scrollBy
首先看源码:

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);
}

可以看出scrollBy实际上也是调用了scrollTo的方法。
并且在mScrollX ,mScrollY的基础上进行了移动。
scrollBy是绝对移动,它移动时会在上一个的位置上继续移动,而scrollTo却不是,它是直接移动到那个位置。


那么mScrollX和mScrollY是什么?
public final int getScrollX() {
       return mScrollX;
}

public final int getScrollY() {
       return mScrollY;
}
在view中可以通过getScrollX和getScrollY来获得。


mScrollX的值等于View的上边缘在水平方向上的值减去View内容的上边缘在水平方向上的值。
同理mScrollY的值等于View的下边缘在竖直方向上的值减去View内容的下边缘在竖直方向上的值。
单位是像素。
什么是View边缘,什么又是View内容的边缘?
View边缘指的是View的位置,由四个顶点组成,而View内容边缘指的是View中内容的边缘。
而scrollTo和scrollBy实现滑动,只能将View的内容进行移动。

一个概念的了解:
android的View视图是没有边界的,也就是说,我们在手机屏幕上看到的,是因为屏幕的限制,我们只能看到这么大。
既然这是前提,下面举例子来看。
public class MainActivity extends AppCompatActivity {

    private ManualView manual;//自定义的TextView

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        manual = (ManualView) findViewById(R.id.manual);
        int scrollX = manual.getScrollX();
        int scrollY = manual.getScrollY();
        System.out.println("默认情况下"+"ScrollX:"+scrollX + " --- ScrollY:" + scrollY);

        findViewById(R.id.btn_scrollby).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                manual.scrollBy(-20, 0);
                int scrollX = manual.getScrollX();
                int scrollY = manual.getScrollY();
                System.out.println("scrollby移动后" + "ScrollX:" + scrollX + " --- ScrollY:" + scrollY);
            }
        });

        findViewById(R.id.btn_scrollto).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                manual.scrollTo(100,0);
                int scrollX = manual.getScrollX();
                int scrollY = manual.getScrollY();
                System.out.println("scrollto移动后" + "ScrollX:" + scrollX + " --- ScrollY:" + scrollY);
            }
        });
    }
}

运行后:

这里写图片描述

当scrollBy(-20, 0)后,就成为了下面这个样子:

这里写图片描述

前面提到的说每一个View的视图是没有边界的,而只是因为视图的边界超过了它的父容器(可以认为是被隐藏或覆盖),所以只会显示我们眼中看到的布局视图。

这个View没有边界的视图应该是下面这样子:
这里写图片描述

当我们scrollBy(-20,0)时,内容向右移动,滚动时不是以(0,0)作为参照,是调用scrollBy以及scrollTo方法的View自己作为参照。
为什么是向右,要知道View的布局并没有移动,移动的只是View的内容。
View布局的左上减去View内容的左上,此时此刻就是-20。
这里写图片描述
按照上面所说的View边缘和View内容边缘的值的差,可以得出此时的mScrollX为-20。

有时候在想scrollTo和scrollBy移动的只是View的内容,那么有的控件没有内容,又是怎么实现这个方法的呢?

2.2 使用动画完成滑动

使用平移动画或者属性动画。动画以后再深究。

2.3 改变布局参数

通过设置控件的LayoutParams,改变Margin值来达到View的移动。
貌似这种方法也想的出来啊。

RelativeLayout.LayoutParams params= 
            (RelativeLayout.LayoutParams) manual.getLayoutParams();
params.leftMargin+=100;
manual.setLayoutParams(params);
//或
manual.requestLayout();

2.4 各种滑动方法的对比

scrollTo和scrollBy方法可以很方便的实现滑动功能,但缺点是只能滑动View的内容,不可以滑动View本身。

使用动画的话,要注意属性动画只能使用到Android3.0以上,如果要向下兼容,还需要使用第三方的库。

改变布局参数,操作有些复杂,适用于有交互的View。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值