View体系

本文详细介绍了Android中的View体系,包括View的基本概念、视图坐标系、滑动方法(如layout、动画、Scroller等)、属性动画的原理和使用,以及源码解析View的事件分发机制。此外,还探讨了Activity的构成、自定义View和ViewGroup的实现,特别是处理wrap_content、滑动冲突以及弹性滑动的技巧。
摘要由CSDN通过智能技术生成

View

1.View简介

1.View是Android所有控件的基类,同ViewGroup也是继承自View。
2.View的层级关系图:在这里插入图片描述

2.视图坐标系

在这里插入图片描述

View的获取方法

getHeight():获取View自身高度
getWidth():获取View自身宽度
getTop():获取View自身顶边到其父布局顶边的距离
getLeft():获取View自身左边到其父布局左边的距离
getRight():获取View自身右边到其父布局左边的距离
getBottom():获取View自身底边到其父布局顶边的距离

MotionEvent所提供的方法

getX():获取点击事件距离控件左边的距离,即视图坐标
getY():获取点击事件距离控件顶边的距离,即视图坐标
getRawX():获取点击事件距离整个屏幕左边距离,即绝对坐标
getRawY():获取点击事件距离整个屏幕顶边的的距离,即绝对坐标

3.View的滑动方法

Android中经常看到一些炫酷的效果,这些效果很多伴随着View的滑动。

1) layout()

使用layout方法会直接改变View的位置,其内部会把四个参数分别赋值给mLeft、mTop、mRight、mBottom四个变量,四个顶点的位置也就相应改变。

int mLeft = imageView.getLeft();//获取ImageView左上角初始横坐标
int mTop = imageView.getTop();//获取ImageView左上角初始纵坐标
int mRight = imageView.getRight();//获取ImageView右下角初始横坐标
int mBottom = imageView.getBottom();//获取ImageView右下角初始纵坐标
image.layout(mLeft + 300,mTop,mRight + 300,mBottom);//重新布局
2) offsetLeftAndRight()与offsetTopAndBottom()
imageView.offsetLeftAndRight(300);  //将imageView沿水平正方向偏移300px
imageView.offsetTopAndBottom(300); //将iamgeView沿竖直正方向偏移300px
3)LayoutParams(改变布局参数)
LinearLayout.LayoutParams layoutParams= (LinearLayout.LayoutParams) getLayoutParams();
              layoutParams.leftMargin = getLeft() + offsetX;
              layoutParams.topMargin = getTop() + offsetY;
              setLayoutParams(layoutParams);
4)使用动画

view动画

//这里用不同的方式去实现。
TranslateAnimation animation = new TranslateAnimation(0, 300, 0, 0);
animation.setDuration(1500);//设置动画时长
animation.setFillAfter(true);//让ImageView停留在动画结束的位置
imageView.startAnimation(animation);

属性动画

ObjectAnimator.ofFloat(imageView,"translationX",0,300).setDuration(1500).start();
5)scollTo与scollBy
linearLayout.scrollTo(-300,0);
linearLayout.scrollBy(-150,0);
linearLayout.scrollBy(-150,0);
6)Scroller
public class CustomView extends LinearLayout {  

    private static final String TAG = "Scroller";  

    private Scroller mScroller;  

    public CustomView(Context context, AttributeSet attrs) {  
        super(context, attrs);  
        mScroller = new Scroller(context);  
    }  

    //调用此方法滚动到目标位置  
    public void smoothScrollTo(int fx, int fy) {  
        int dx = fx - mScroller.getFinalX();  
        int dy = fy - mScroller.getFinalY();  
        smoothScrollBy(dx, dy);  
    }  
    //调用此方法设置滚动的相对偏移  
    public void smoothScrollBy(int dx, int dy) {  

        //设置mScroller的滚动偏移量  
        mScroller.startScroll(mScroller.getFinalX(), mScroller.getFinalY(), dx, dy);  
        invalidate();//这里必须调用invalidate()才能保证computeScroll()会被调用,否则不一定会刷新界面,看不到滚动效果  
    }  

    @Override  
    public void computeScroll() {  

        //先判断mScroller滚动是否完成  
        if (mScroller.computeScrollOffset()) {  

            //这里调用View的scrollTo()完成实际的滚动  
            scrollTo(mScroller.getCurrX(), mScroller.getCurrY());  

            //必须调用该方法,否则不一定能看到滚动效果  
            postInvalidate();  
        }  
        super.computeScroll();  
    }  
} 

4.Android属性动画

1)优点

不再局限于View对象,无对象也可以进行动画处理
不再局限于四种基本变换:平移,旋转,缩放,透明度
可以灵活的操作任意对象属性,根据自己的业务来实现自己想要的结果

2)核心点

ObjectAnimator 对象动画
ValueAnimator 值动画
PropertyValueHolder 用于同时执行多个动画
TypeEvaluator 估值器
AnimatorSet 动画集合
Interpolator 差值器

ObjectAnimator

使用ObjectAnimator完成在一段时间内透明度的变化

  /**
     * ObjectAnimator基本使用继承子ValueAnimator
     * 对对象v的alpha参数进行操作,alpha的值从1.0变到0.3
     *
     * @param v
     */
    public void startObjectAnimatorAnim(View v) {
        ObjectAnimator alphaAnim = ObjectAnimator.ofFloat(v, "alpha", 1.0f, 0.3f);
        //执行事件
        alphaAnim.setDuration(1000);
        //延迟
        alphaAnim.setStartDelay(300);
        alphaAnim.start();
    }
ValueAnimator

使用ValueAnimator完成在一段事件内缩放

 /**
     * 在一段时间内生成连续的值完成view的缩放
     * @param v
     */
    public void startValueAnimatorAnim(final View v) {
        //不改变属性大小,只在一段事件内生成连续的值
        ValueAnimator animator = ValueAnimator.ofFloat(0f, 100f);
        animator.setDuration(500);
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                //百分比对应的值
                float value = (float) animation.getAnimatedValue();
                Log.e("TAG", "onAnimationUpdate: " + value);
                v.setScaleX(0.5f + value / 200);
                v.setScaleY(0.5f + value / 200);
            }
        });
        animator.start();
    }
PropertyValueHolde

使用PropertyValueHolder完成上面的俩个动画同时执行

 /**
     * 一个动画实现多个效果的变换
     *
     * @param v
     */
    public void startPropertyValueHolderAnim(View v) {
        PropertyValuesHolder alphaProper = PropertyValuesHolder.ofFloat("alpha", 0.5f, 1f);
        PropertyValuesHolder scaleXProper = PropertyValuesHolder.ofFloat("scaleX", 0.5f, 1f);
        PropertyValuesHolder scaleYProper = PropertyValuesHolder.ofFloat("scaleY", 0.5f, 1f);
        ValueAnimator animator = ObjectAnimator.ofPropertyValuesHolder(v, alphaProper, scaleXProper, scaleYProper);
        animator.setDuration(500);
        animator.start();
    }
AnimatorSet

AnimatorSet多个动画按制定顺序执行

 /**
     * 执行多个动画并控制动画顺序
     *
     * @param v
     */
    public void startAnimatorSet(View v) {
        ObjectAnimator animator1 = ObjectAnimator.ofFloat(v, "translationX", 0f, 100f);
        ObjectAnimator animator2 = ObjectAnimator.ofFloat(v, "alpha", 0f, 1f);
        ObjectAnimator animator3 = ObjectAnimator.ofFloat(v, "scaleX", 0f, 1f);
        AnimatorSet animatorSet = new AnimatorSet();
        animatorSet.setDuration(500);
        //动画1,2同时执行
        animatorSet.play(animator1).with(animator2);
        //动画2执行完成后执行动画3
        animatorSet.play(animator3).after(animator2);
        animatorSet.start();
    }
    
animatorSet.play(animator1).with(animator2);动画1和动画2同时执行
animatorSet.play(animator3).after(animator2);动画3在动画2执行完成后执行
animatorSet.playSequentially(animator1,animator2,animator3)动画1,2,3按顺序执行
animatorSet.playTogether(animator1,animator2,animator3)三个动画同时执行
TypeEvaluator

估值器可以自定义变换规则,普通动画是匀速执行

 /**
     * 使用估值器实现重力下落
     *
     * @param v
     */
    public void startEvaluator(final View v) {
        ValueAnimator animator = new ValueAnimator();
        animator.setDuration(3000);
        animator.setObjectValues(new PointF(0, 0));
        final PointF pointF = new PointF();
        animator.setEvaluator(new TypeEvaluator() {
            @Override
            public Object evaluate(float fraction, Object startValue, Object endValue) {
                //fraction是运动中的匀速变化的值
                //根据重力计算实际的运动y=vt=0.5*g*t*t
                //g越大效果越明显
                pointF.x = 100 * (fraction * 5);
                pointF.y = 0.5f * 300f * (fraction * 5) * (fraction * 5);
                return pointF;
            }
        });
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                PointF p = (PointF) animation.getAnimatedValue();
                v.setX(p.x);
                v.setY(p.y);
            }
        });
        animator.start();
    }
Interpolator

插值器已经定义好计算规则的估值器(API中已经定义好了算法)

 //加速查值器,参数越大,速度越来越快
        animator.setInterpolator(new AccelerateInterpolator(10));
        //减速差值起,和上面相反
        animator.setInterpolator(new DecelerateInterpolator(10));
        //先加速后减速插值器
        animator.setInterpolator(new AccelerateDecelerateInterpolator());
        //张力值,默认为2,T越大,初始的偏移越大,而且速度越快
        animator.setInterpolator(new AnticipateInterpolator(3));
        //张力值tension,默认为2,张力越大,起始时和结束时的偏移越大
        animator.setInterpolator(new AnticipateOvershootInterpolator(6));
        //弹跳插值器
        animator.setInterpolator(new BounceInterpolator());
        //周期插值器
        animator.setInterpolator(new CycleInterpolator(2));
        //线性差值器,匀速
        animator.setInterpolator(new LinearInterpolator());

5.从源码解析Scroller

Scroller类是滚动的一个封装类,可以实现View的平滑滚动效果,举个简单的例子,一个View从在我们指定的时间内从一个位置滚动到另外一个位置,我们利用Scroller类可以实现匀速滚动,可以先加速后减速,可以先减速后加速等等效果,而不是瞬间的移动的效果,所以Scroller可以帮我们实现很多滑动的效果。

6.从源码解析View的事件分发机制

1) 处理点击事件的方法

dispatchTouchEvent(MotionEvent ev):用来进行事件的分发
onInterceptTouchEvent(MotionEvent ev):用来进行事件的拦截,在dispatchTouchEvent()中调用,需要注意的是View没有提供该方法
onTouchEvent(MotionEvent ev):用来处理点击事件,在dispatchTouchEvent()方法中进行调用

2)点击事件分发的传递规则

点击事件由上而下的传递规则

对于根ViewGroup,点击事件首先传递给它的dispatchTouchEvent()方法,如果该ViewGroup的onInterceptTouchEvent()方法返回true,则表示它要拦截这个事件,这个事件就会交给它的onTouchEvent()方法处理,如果onInterceptTouchEvent()方法返回false,则表示它不拦截这个事件,则交给它的子元素的dispatchTouchEvent()来处理,如此的反复下去。如果传递给最底层的View,View是没有子View的,就会调用View的dispatchTouchEvent()方法,一般情况下最终会调用View的onTouchEvent()方法。

点击事件由下而上的传递规则

点击事件传给最底层的View,如果他的onTouchEvent()方法返回true,则事件由最底层的View消耗并处理了,如果返回false则表示该View不做处理,则传递给父View的onTouchEvent()处理,如果父View的onTouchEvent()仍旧返回返回false,则继续传递给改父View的父View处理,如此的反复下去。

7.从源码解析Activity的构成

从源码解析Activity的构成

1.Activity的结构分为三层,分别是:Activity、Window和View。
2.结构的创建过程分为三步:1. 创建Activity,并且创建与其相关的WindowManager和Window,对应着是Activity的attach方法的调用;2. 初始化DecorView和ContentView,对应着的是Activity的onCreate方法;3. 创建ViewRootImpl,并且将DecorView添加到ViewRootImpl中,同时触发View树的三大流程。
3.在generateLayout方法里面,会根据设置不同的flags来计算DecorView的大小和位置,计算逻辑在updateColorViews方法里面;还是根据设置不同的features方法来选择默认加载到DecorView中,比如说设置了NO_ACTION_BAR的features,就会加载不带ActionBar的布局。

图解Activity的构成

在这里插入图片描述

8.从源码解析View的measure流程

View的工作流程,就是measure、layout和draw。measure用来测量View的宽高,layout用来确定View的位置,draw则用来绘制View。这一讲我们来看看measure流程,measure流程分为View的measure流程和ViewGroup的measure流程,只不过ViewGroup的measure流程除了要完成自己的测量还要遍历去调用子元素的measure()方法。

9.从源码解析View的layout和draw流程

1)layout的重点

View的四个点的坐标,它的坐标不是相对屏幕的原点,而且相对于它的父布局来说的。 l 和 t 是子控件左边缘和上边缘相对于父类控件左边缘和上边缘的距离;
r 和 b是子控件右边缘和下边缘相对于父类控件左边缘和上边缘的距离。
在setFrame()方法里主要是用来设置View的四个顶点的值,也就是mLeft 、mTop、mRight和 mBottom的值。在调用setFrame()方法后,调用onLayout()方法
onLayout()方法没有去做什么,这个和onMeasure()方法类似,确定位置时根据不同的控件有不同的实现,所以在View和ViewGroup中均没有实现onLayout()方法。
在setChildFrame()方法中调用子元素的layout()方法来确定自己的位置。我们看到childTop这个值是逐渐增大的,这是为了在垂直方向,子元素是一个接一个排列的而不是重叠的。

2)draw的重点

如果有设置背景,则绘制背景
保存canvas层
绘制自身内容
如果有子元素则绘制子元素
绘制效果
绘制装饰品(scrollbars)

10.自定义View

1)简介

自定义View按照笔者的划分,分为两大类,一种是自定义View,一种是自定义ViewGroup;其中自定义View又分为继承View和继承系统控件两种。这篇文章首先先了解下两大类的其中一种:自定义View。

2)继承系统控件的自定义View

这种自定义View在系统控件的基础上进行拓展,一般是添加新的功能或者修改显示的效果,一般情况下我们在onDraw()方法中进行处理。

3) 继承View的自定义View

不只是要实现onDraw()方法,而且在实现过程中还要考虑到wrap_content属性以及padding属性的设置;为了方便配置自己的自定义View还会对外提供自定义的属性,另外如果要改变触控的逻辑,还要重写onTouchEvent()等触控事件的方法。

11.自定义组合控件

自定义组合控件就是多个控件组合起来成为一个新的控件,主要用来解决多次重复的使用同一类型的布局。

1)组合控件的xml布局

2)组合控件的Java代码

3)自定义属性

4)xml中引用组合控件

5)调用组合控件

12.自定义ViewGroup

1)继承ViewGroup

要实现自定义的ViewGroup,首先要继承ViewGroup并调用父类构造方法,实现抽象方法等。

2)对wrap_content属性进行处理

3)实现onLayout

布局子元素,因为每一种布局方式子View的布局都是不同的,所以这个是ViewGroup唯一一个抽象方法,需要我们自己去实现

4)处理滑动冲突

ViewGroup是水平滑动,如果里面是ListView,则ListView是垂直滑动,如果我们检测到的滑动方向是水平的话,就让父View拦截用来进行View的滑动切换

5)弹性滑动到其他页面

进入onTouchEvent事件,然后我们需要进行滑动切换页面,这里需要用到Scroller。

6)快速滑动到其他页面

我们不只滑动超过一半才切换到上/下一个页面,如果滑动速度很快的话,我们也可以判定为用户想要滑动到其他页面,这样的体验也是好的。 这部分也是在onTouchEvent中的ACTION_UP部分:
这里又需要用到VelocityTracker,它用来测试滑动速度的。使用方法也很简单,首先在构造函数中进行初始化,接着改写onTouchEvent部分。

7)再次触摸屏幕阻止页面继续滑动

当我们快速向左滑动切换到下一个页面的情况,在手指释放以后,页面会弹性滑动到下一个页面,可能需要一秒才完成滑动,这个时间内,我们再次触摸屏幕,希望能拦截这次滑动,然后再次去操作页面。
要实现在弹性滑动过程中再次触摸拦截,肯定要在onInterceptTouchEvent中的ACTION_DOWN中去判断,如果在ACTION_DOWN的时候,scroller还没有完成,说明上一次的滑动还正在进行中,则直接中断scrolle。

8)应用HorizontalView

首先我们在主布局中引用HorizontalView,它作为父容器,里面有两个ListView,接着在代码中为ListView填加数据,最后贴上HorizontalView的源码。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值