Android View体系总结

前言

Android View体系是界面编程的核心,他的重要性不亚于Android四大组件,View是Android中所有控件的基类,是一种界面层的控件的一种抽象,它代表了一个控件。

一、视图坐标系

1.Android坐标系的分类

Android 中的坐标系可以分为三类: 屏幕坐标系, 布局坐标系, 以及视图坐标系,这里我们详解视图坐标系。
所谓视图坐标系就是:该坐标系是在 view 绘制过程中, 绘制的内容将该坐标系作为参考, 来绘制 view.也就是内容在view里面的位置。

2.View中的方法

getTop():获取View自身顶边到其父布局顶边的距离
getLeft():获取View自身左边到其父布局左边的距离
getRight():获取View自身右边到其父布局左边的距离
getBottom():获取View自身底边到其父布局顶边的距离
以上方法得到的值都是 该view针对他所在父容器的坐标 (布局坐标系)
getHeight():获取View自身高度
getWidth():获取View自身宽度

view 又增加了一些关于 view 的属性
x, y: 标示 view 左上角的坐标, 其值为:x和y默认为0
x = mleft + tranlationX
y = mtop + tranlationY
translationX, translationY: 表示 view 位置的偏移量(相对于原位置), 初始值为0
该坐标系主要用在 view 的动画操作上面,这样可以控制view整个内容的偏移

3.MotionEvent当中的方法

getY(): 获取点击事件相对控件左边的x轴坐标,即点击事件距离控件左边的距离
getY():获取点击事件相对控件顶边的y轴坐标,即点击事件距离控件顶边的距离
getRawX():获取点击事件相对整个屏幕左边的x轴坐标,即点击事件距离整个屏幕左边的距离
getRawY():获取点击事件相对整个屏幕顶边的y轴坐标,即点击事件距离整个屏幕顶边的距离

二、实现View滑动的六种方法

1.通过Layout方法来实现滑动

在View绘制的时候,系统都会调用layout(int l, int t, int r, int b)方法来确定View的具体位置。系统既然是这样来设置View的位置的,那么我们也可以通过调用layout(int l, int t, int r, int b)`方法修改left,top,right,bottom这四个属性来控制View的位置。


当然使用 getX()、getY()方法和使用getRawX()、geRawtY()的效果是一样的,只不过前者使用的是相对位置,而后者使用的是绝对位置。
但是要注意,在使用绝对坐标系的时候,每次执行完 ACTION_MOVE的逻辑后,一定要重新设置初始坐标,这样才能获得准确的偏移量。

2.通过offsetLeftAndRight()与offsetTopAndBottom()来实现滑动

这两种方法和layout()方法效果方法差不多,具体案例参照:https://github.com/zq12119/ViewActivity3

3通过.LayoutParams实现滑动

LayoutParams主要保存了一个View的布局参数,因此我们可以通过LayoutParams来改变View的布局的参数从而达到了改变View的位置的效果。同样的我们将ACTION_MOVE中的代码替换:https://github.com/zq12119/ViewActivity4

4.通过动画实现滑动

可以采用View动画来移动,在res目录新建anim文件夹并创建translate.xml:

然后在代码中引用即可。

5.通过scollTo与scollBy实现滑动

scollTo(x,y)表示移动到一个具体的坐标点,而scollBy(dx,dy)则表示移动的增量为dx、dy。其中scollBy最终也是要调用scollTo的。scollTo、scollBy移动的是View的内容,如果在ViewGroup中使用则是移动他所有的子View。我们将上述ACTION_MOVE中的代码替换成如下代码:
((View)getParent()).scrollBy(-offsetX,-offsetY);
这里要实现CustomView随着我们手指移动的效果的话,我们就需要将偏移量设置为负值。

6.通过Scroller实现滑动

我们用scollTo/scollBy方法来进行滑动时,这个过程是瞬间完成的,所以用户体验不大好。这里我们可以使用Scroller来实现有过度效果的滑动,这个过程不是瞬间完成的,而是在一定的时间间隔完成的。Scroller本身是不能实现View的滑动的,它需要配合View的computeScroll()方法才能弹性滑动的效果。
在这里我们实现CustomView平滑的向右移动。
首先我们要初始化Scroller:

接下来重写computeScroll()方法,系统会在绘制View的时候在draw()方法中调用该方法,这个方法中我们调用父类的scrollTo()方法并通过Scroller来不断获取当前的滚动值,每滑动一小段距离我们就调用invalidate()方法不断的进行重绘,重绘就会调用computeScroll()方法,这样我们就通过不断的移动一个小的距离并连贯起来就实现了平滑移动的效果:

调用Scroller.startScroll()方法。我们在CustomView中写一个smoothScrollTo()方法,调用Scroller.startScroll()方法,在2000毫秒内沿X轴平移delta像素:

最后我们在ViewSlideActivity.java中调用CustomView的smoothScrollTo()方法:

三、属性动画

1.背景

实现动画效果在Android开发中非常常见,因此Android系统一开始就提供了两种实现动画的方式:
逐帧动画(Frame Animation)
补间动画( Tweened animation )

2.问题

逐帧动画 & 补间动画存在一定的缺点:
a. 作用对象局限:View
b. 没有改变View的属性,只是改变视觉效果
c. 动画效果单一
为了解决补间动画的缺陷在 Android 3.0(API 11)开始,系统提供了一种全新的动画模式:属性动画(Property Animation)。

3、简介

属性动画的作用对象:任意JAVA对象。
实现的动画效果:可自驾游各种动画效果3.1

4.特点及工作原理

作用对象进行了扩展:不只是View对象,甚至没对象也可以;
动画效果:不只是4种基本变换,还有其他动画效果;
作用领域:API11后引入的。
工作原理:在一定时间间隔内,通过不断对值进行改变,并不断将该值赋给对象的属性,从而实现该对象在该属性上的动画效果。

5.具体使用

5.1 ValueAnimator类

ValueAnimator不提供任何动画效果,它更像一个数值发生器,用来产生一定规律数字,从而让调用者来控制动画的实现过程。通常情况下,在ValueAnimator的AnimatorUpdateListener中监听数值的变化,从而完成动画的变换。

5.2动画 的监听

完整的动画具有start,Repeat,End,Cancel四个过程:

5.3综合动画

这个类提供了一个play()方法,如果我们向这个方法中传入一个Animator对象(ValueAnimator或ObjectAnimator)将会返回一个AnimatorSet.Builder的实例,AnimatorSet.Builder中包括以下四个方法:

after(Animator anim) 将现有动画插入到传入的动画之后执行
after(long delay) 将现有动画延迟指定毫秒后执行
before(Animator anim) 将现有动画插入到传入的动画之前执行
with(Animator anim) 将现有动画和传入的动画同时执行

四、View的事件分发机制

1.处理点击事件的方法

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

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

(1)点击事件由上而下的传递规则;
(2)点击事件由下而上的传递规则;

3.点击事件传递时的其他问题

(1)如果我们设置了OnTouchListener并且onTouch()方法返回true,则onTouchEvent()方法不会被调用,否则则会调用onTouchEvent()方法,可见OnTouchListener的优先级要比onTouchEvent()要高。在OnTouchEvent()方法中,如果当前设置了
(2)OnClickListener则会执行它的onClick()方法。
View的OnTouchEvent()方法默认都会返回true,除非它是不可点击的也就是CLICKABLE和LONG_CLICKABLE都为false。

五、自定义View

1、自定义View简介

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

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

这种自定义View在系统控件的基础上进行拓展,一般是添加新的功能或者修改显示的效果,一般情况下我们在onDraw()方法中进行处理。具体代码如下:https://github.com/zq12119/ViewActivity5

3.继承View的自定义View

与上面的继承系统控件的自定义View不同,继承View的自定义View实现起来要稍微复杂一些,不只是要实现onDraw()方法,而且在实现过程中还要考虑到wrap_content属性以及padding属性的设置;为了方便配置自己的自定义View还会对外提供自定义的属性,另外如果要改变触控的逻辑,还要重写onTouchEvent()等触控事件的方法。
按照上面链接的例子我们再写一个RectView类继承View来画一个正方形:

在布局中引用RectView:

六、自定义组合控件

有时候在做开发的时候,android提供给我们的视图并不能满足我们的要求,所以有时候我们需要自己创建自己的view。我们只需要将我们想要的继承于View,然后重写里面的方法就可以了。
然后我们只需要在Layout中使用这个view就可以了。

如果我们要自定义属性,就像android:layout_height="wrap_content"这种:
首先我们要学习declare-styleable,它是给自定义控件添加自定义属性时用的。
我们在res/values下建立一个myAttrs.xml。

这个就是自定义的属性名称。在R.java中会生成对应的名字,这个地方是MyTextView_fontSize.

七、自定义ViewGroup

1、ViewGroup的职责

ViewGroup相当于一个放置View的容器,并且我们在写布局xml的时候,会告诉容器(凡是以layout为开头的属性,都是为用于告诉容器的),我们的宽度(layout_width)、高度(layout_height)、对齐方式(layout_gravity)等;当然还有margin等;于是乎,ViewGroup的职能为:给childView计算出建议的宽和高和测量模式 ;决定childView的位置;为什么只是建议的宽和高,而不是直接确定呢,别忘了childView宽和高可以设置为wrap_content,这样只有childView才能计算出自己的宽和高。

2、View的职责是啥?

View的职责,根据测量模式和ViewGroup给出的建议的宽和高,计算出自己的宽和高;同时还有个更重要的职责是:在ViewGroup为其指定的区域内绘制自己的形态。

3.如何实现自定义的ViewGroup

要实现自定义的ViewGroup,首先要继承ViewGroup并调用父类构造方法,实现抽象方法等。如下我们定义了名字叫HorizontalView的类并继承 ViewGroup,onLayout这个抽象方法是必须要实现的,我们暂且什么都不做。

4、自定义ViewGroup的大体步骤:

1.重载构造函数;
2.复写onMeasure,返回值给getMeasuredWidth和getMeasuredHeight(optional);
3.复写onLayout,负责摆放子View。

5、绘图

1.onMeasure(测量所有的childView从而确定自己的宽高)

体现在我们的OnMeasure步骤里,就是根据xml文件的设定,把所有的child View都测量一遍,然后设置自己的宽高,具体的值可以通过getMeasuredWidth和getMeasuredHeight获取。
一般的onMeasure是这么写的

注意onMeasure的两个参数,widthMeasureSpec(存储了所有子View以及该ViewGroup的MODE和Size信息,xml文件里设定的值)和heightMeasureSpec。
MeasureSpec有三种MODE,一般来讲
1.EXACTLY,像是60dp,match_parent这种;
2.AT_MOST,像是wrap_content这种;
3.unspecified用的不多。
(onMeasure默认的实现仅仅调用了setMeasuredDimension,setMeasuredDimension函数是一个很关键的函数,它对View的成员变量mMeasuredWidth和mMeasuredHeight变量赋值,而measure的主要目的就是对View树中的每个View的mMeasuredWidth和mMeasuredHeight进行赋值,一旦这两个变量被赋值,则意味着该View的测量工作结束。)

2、onLayout(摆放自己的childView)

这部分就是把所有的控件摆放位置了,一般的做法是遍历所有的child,计算出left,top,right和bottom的值,并调用child.layout(l,t,r,b)完成摆放,就像这样:

onLayout传进来的五个参数,第一个不管,后四个限制了该自定义ViewGroup的范围,也就是说,自定义ViewGroup就好像在屏幕上给你开辟了一块四边形,允许你摆放控件。像自动换行那种ViewGroup用的就是width累加是否超过r来判断的。

更多相关文章

  1. Eclipse启动失败Android(安卓)sdk content loader 0%的四种解决
  2. Android(安卓)studio build.gradle 各种错误解决总结
  3. Android前端—显示GIF动画
  4. Android(安卓)属性动画工作原理
  5. android adapter中到converView机制
  6. Android中Serializable和Parcelable的用法及区别
  7. 详解Android中解析XML的方法
  8. Android(安卓)中 Context介绍(sundy深入浅出)之基础概念
  9. 深入Animation,在SurfaceView中照样使用Android—Tween Animatio

随机推荐

  1. Android(安卓)TextView 45°倾斜效果
  2. ubuntu Android(安卓)Studio
  3. Android(安卓)- 比较日期的时间部分
  4. Android调用相机相册并显示照片在ImageVi
  5. Android:监听WIFI网络状态
  6. What Android(安卓)Chipset Can Support
  7. Android(安卓)获取CPU架构(指令集)
  8. Android(安卓)使用广播(BroadcastReceiver
  9. Android(安卓)创建与解析XML(五)—— Dom4j
  10. Android(安卓)自定义seekbar