其他分享
首页 > 其他分享> > Android艺术开发探索——3.View基础

Android艺术开发探索——3.View基础

作者:互联网

3.1 什么是View

View是Android中所有控件的基类,是一种界面层的控件的一种抽象,本身可以是单个控件也可以是由多个控件组成的一组控件。

3.1.1 View的位置参数

在这里插入图片描述
这些坐标都是相对于view的父容器来说的,是一种相对坐标
后来增加了x,y,translationX,translationY,x,y是view左上角的坐标,translationX,translationY是view左上角相对于父容器的偏移量,默认值为0,
x = left + translationX
y = top + translationY
1.获取view左上角相对于父容器的坐标,当view没有发生平移时,getX() == getLeft(),getY() == getTop(),当发生平移时,top和left表示的是原始左上角的位置信息,其值并不会发生改变,此时发生改变的是x,y,translationX,translationY。

3.1.2 MotionEvent

1.手指接触屏幕后所产生的一系列事件

事件类型具体动作
ACTION_DOWN手指刚接触屏幕
ACTION_MOVE手指在屏幕上移动
ACTION_UP手指从屏幕上松开的一瞬间
ACTION_CANCEL结束事件(非人为原因)

2.通过MotionEvent对象我们可以得到点击事件的x和y坐标。
getX/getY 相对于当前View左上角的x和y坐标,getRawX/getRawY 相对于手机屏幕左上角的x和y坐标
在这里插入图片描述

3.1.3 TouchSlop

系统所能识别出的被认为是滑动的最小距离,是个常量,和设备有关,在不同设备上这个值可能是不同的,可通过ViewConfiguration.get(getContext()).getScaledTouchSlop()获得,可利用这个常量做一些过滤。

3.1.4 VelocityTracker

速度追踪,用于追踪手指在滑动过程中的速度,包括水平方向和竖直方向。

		
		//在View的onTouchEvent方法中追踪当前点击事件的速度
        VelocityTracker velocityTracker =   VelocityTracker.obtain();
        velocityTracker.addMovement(event);

		//获得当前的速度
        velocityTracker.computeCurrentVelocity(1000);
        int xVelocity = (int) velocityTracker.getXVelocity();
        int yVelocity = (int) velocityTracker.getYVelocity();
        
	   //当不需要使用时,调用clear方法来重置并回收内存
	   velocityTracker.clear();
       velocityTracker.recycle();
	   

1.获取速度之前先计算速度computeCurrentVelocity()
2.这里的速度是指一段时间内手指划过的像素数
3.和Android坐标轴相同方向结果为正,相反方向为负

3.1.5 GestureDetector

手势检测,用于辅助检测用户的单击,滑动,长按,双击等行为
GestureDetector内部的Listener接口:

使用:

3.1.6 Scroller

弹性滑动对象,用于实现view的弹性滑动。scrollTo/scrollBy进行滑动时,其过程是瞬间完成的,Scroller来实现有过渡效果的滑动,若要实现弹性滑动,需要和View的computeScroll方法配合使用。

3.2 View的滑动

实现滑动的三种方式:
1.通过View本身提供的scrollTo / scrollBy方法
2.通过动画给View施加平移效果
3.通过改变View的LayoutParams使View重新布局从而实现滑动

3.2.1 使用scrollTo / scrollBy

/**
     * 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();
            }
        }
    }

    /**
     * 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) { //可以看出scrollBy也是调用scrollBy实现的
        scrollTo(mScrollX + x, mScrollY + y);
    }

scrollBy:基于当前位置的相对滑动
scrollTo:基于所传递参数的绝对滑动
mScrollX:值总是等于View左边缘和View内容左边缘在水平方向的距离
mScrollY:值总是等于View上边缘和View内容上边缘在水平方向的距离
View边缘指View位置,由四个定点组成,View内容边缘指View中内容的边缘。
scrollTo,scrollBy只能改变View内容的位置而不能改变View在布局中的位置,不能将View本身进行移动。

3.2.2 使用动画

主要操作View的translationX和translationY属性。可以采用传统的View动画,也可以采用属性动画
View动画的代码

<?xml version="1.0" encoding="UTF-8" ?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:fillAfter="true"      //希望动画后的状态得以保留
    android:zAdjustment="normal">

    <translate
        android:duration="100" //持续时间
        android:fromXDelta='0'
        android:fromYDelta='0'
        android:interpolator="@android:anim/linear_interpolator"
        android:toXDelta="100"
        android:toYDelta="100"/>
</set>

属性动画

ObjectAnimator.ofFloat(tragetView,"translationX",0,100).setDuration(100).start()
           

上面的动画并不能真正改变View的位置,这个View的单击事件,新位置无法触发onClick事件,单击原位置可以。
Android3.0开始,使用属性动画可以解决这个问题。
针对上面问题,有以下解决方法:在新位置预先创建一个和Button一模一样的Button,当Button完成平移动画后,把目标Button隐藏,同时把预先创建的Button显示出来

3.2.3 改变布局参数——LayoutParams

把一个Button向右平移100px:
1.将这个Button的LayoutParams里的marginLeft 参数的值增加100px;
2.在Button的左边放置一个空View,这个空View的默认宽度为0,当需要向右移动Button时,只需重新设置空View的宽度,当空View的宽度增大时,Button就自动被挤向右边。

                MarginLayoutParams params = (MarginLayoutParams) button.getLayoutParams();
                params.width += 500;
                params.leftMargin += 500;
                button.requestLayout();
                //或者 button.setLayoutParams(params);

3.2.4 各种滑动方式的对比

在这里插入图片描述

3.3 弹性滑动

实现弹性滑动的方法有很多,共同思想将一次大的滑动分成若干次小滑动,并在一个时间段内完成。

3.3.1 Scroller

Scroller的源码


//1.创建Scroller实例
Scroller scroller = new Scroller(mContext);

    //缓慢滑动到指定位置

    private void smoothScrollTo(int destX,int destY){
        int scrollX = getScrollX();
        int deltaY = destY - scrollX;
        //1000ms内滑向destX,效果是慢慢滑动
//2.调用startScroll()方法来初始化滚动数据并刷新界面
        mScroller.startScroll(scrollX,0,deltaX,0,1000);
        invalidate();

    }
//3.重写computeScroll()方法,并在其内部完成平滑滚动的逻辑       
    @Override
    public void computeScroll(){
        if(mScroller.computeScrollOffset()){
            scrollTo(mScroller.getCurrX(),mScroller.getCurrY());
            postInvalidate();
        }
    }

invalidate()方法会导致View重绘,在View的draw方法中又会去调用computeScroll方法,computeScroll方法在View中是一个空实现,需要自己实现,正是因为这个computeScroll方法,View才能实现弹性滑动。总结:当View重绘后会在draw方法中调用computeScroll,computeScroll会去向Scroller获取当前的scrollX和scrollY,然后通过scrollTo方法实现滑动;接着调用postInvalidate方法进行第二次重绘,反复直到整个滑动结束。
computeScrollOffset()方法会根据时间流逝的百分比计算出scrollX和scrollY改变的百分比并计算当前的值,返回true表示滑动未结束。

概括Scroller工作原理:需要配合View的computeScroll方法才能完成弹性滑动,不断让View重绘,每一次重绘距滑动起始时间有一个时间间隔,通过这个时间间隔Scroller可以得出View当前的滑动位置,知道滑动位置就可以通过scrollTo方法完成View的滑动。View的每一次重绘都会导致View进行小幅度的滑动。多次的小幅度滑动就组成了弹性滑动。

3.3.2 通过动画

动画本身就是一种渐进过程,通过它来实现的滑动天然就具有弹性效果

ObjectAnimator.ofFloat(targetView,"translationX",0,100).setDuration(100).start();

100毫秒内向右移动100像素

3.3.3 使用延时策略

通过发送一系列延时消息从而达到一种渐进式效果。可以使用Handler或View的postDelayed方法,也可以使用线程的sleep方法。
postDelayed:延时发送一个消息,在消息中进行View的滑动,然后接连不断地发送这种延时消息
sleep:在while循环中不断滑动View和sleep

    private static final int MESSAGE_SCROLL_TO = 1;
    private static final int FRAME_COUNT = 30;
    private static final int DELAYED_TIME=33;

    private int mCount = 0;

    @SuppressLint("HandlerLeak")
    private Handler mHandler = new Handler(){
        @Override
        public void handleMessage(Message msg){
            switch (msg.what){
                case MESSAGE_SCROLL_TO:{
                    mCount++;
                    if(mCount <= FRAME_COUNT){
                        float fraction = mCount/(float) FRAME_COUNT;
                        int scrollX = (int) (fraction*100);
                        button.scrollTo(scrollX,0);
                        mHandler.sendEmptyMessageDelayed(MESSAGE_SCROLL_TO,DELAYED_TIME);
                    }
                    break;
                }

                default:
                    break;
            }
        }
    };

在1000ms内将View的内容向左移动100像素

标签:探索,int,Button,scrollTo,滑动,Android,view,View
来源: https://blog.csdn.net/zhougongjinxuan/article/details/114818816