【Android View】初识 View

Android程序中的一个个界面都是由View组成的,下面让我们一起来了解一下View吧!

View 的分类

在这里插入图片描述
如上图所示,系统的View大体分为两类,即ViewViewGroup,分别对应了控件与布局。View是所有控件的基类,而ViewGroup则可以理解为View的组合,它可以包含很多View和ViewGroup,而它所包含的ViewGroup又可以包含View和ViewGroup,形成一个View树

View 的重要方法

在这里插入图片描述

View 的工作流程

一个View显示在屏幕上,经过了measure,layout,draw三个过程。其中,measure用来测量View的宽和高,layout用来确定View的位置,draw则用来绘制View。

MeasureSpec

MeasureSpec : View的一个内部类,其封装的一个View的规格尺寸,包括viewl的宽和高等信息。它的作用是在Measure流程中,系统会将view的LayoutParams等信息,根据父容器所施加的规则转转换成对应的MeasureSpec,然后在onMeasure方法中根据这个MearsureSpec来确定view的宽和高。
其中定义了三个常量,

三个常量都是32位的int值,高2位代表了SpecMode,低30位则代表SpecSize,前者代表测量模式,后者指的是测量大小。SpecMode有三种模式,

  • UNSPECIFIED 未指定模式:view想多大就有多大,富容器不做限制,一般用于系统内部的测量listview
  • AT_MOST 最大模式:对应于wrap_content属性,子view的最终大小是父View指定的SpecSize值,并且子View的大小不能大于这个值
  • EXACTLY 精确模式:对应于match_parent属性和具体的数值,父容器测量出view所需要的大小,也就是specsize的值

继承自View的控件,在默认情况下wrap_content与match_parent的显示效果是一样的,原因可从View的源码中(onMeasure())看到。

//    实用程序返回默认大小。如果MeasureSpec未施加任何约束,则使用提供的尺寸。
//    如果测量允许,将变大。
//    参数:size–此视图的默认大小
//          measureSpec–父级施加的约束
//    返回:此视图应具有的大小
    public static int getDefaultSize(int size, int measureSpec) {
        int result = size;
        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize = MeasureSpec.getSize(measureSpec);

        switch (specMode) {
        	case MeasureSpec.UNSPECIFIED:
            	result = size;
	          	break;
        	case MeasureSpec.AT_MOST:
        	case MeasureSpec.EXACTLY:
        	//默认在最大模式和精确模式中均返回SpecSize
        	//导致wrap_content与match_parent属性效果相同
            	result = specSize;
            	break;
        }
        return result;
    }

因此如果要实现自定义View的wrap_content,则要重写onMeasure(),并对自定义View的wrap_content属性进行处理(具体处理方法可参考:)。

Measure

measure 用来测量View的宽和高,View的measure流程测量自己,而ViewGroup的measure流程除了要完成自己的测量,还要遍历地调用子元素的measure方法。分别对应View的onMeasure方法,ViewGroup的masureChildren方法。

Layout

layout的作用是确定元素的位置。ViewGroup中的layout方法用来确定子元素的位置,View中的layout方法则用来确定自身的位置。

Draw

View的draw方法主要做了以下的工作:
绘制背景;保存当前canvas层;绘制View内容;绘制子View;绘制View的褪色边缘;绘制装饰

坐标系

Android 系统中有两种坐标系,分别为Android 坐标系和 View 坐标系。

Android 坐标系

在Android中,以屏幕左上角的顶点为 Android 坐标系的原点,从该原点向右、向下为正方向。

View 坐标系

View坐标系可以一图说明。
在这里插入图片描述
根据此图,可以得到很多结论,如:
View的宽度:width = getRight() - getLeft() = getWidth()
View的高度:height = getBottom() - getTop() = getHeight()

点击相关

MotionEvent 在用户交互中作用重大,其内部提供了很多事件常量,比如常用的ACTION_UPACTION_DOWNACTION_MOVE。此外,MotionEvent 也提供了获取点击焦点坐标的方法。
getX():获取点击事件距离控件左边的距离,即视图坐标
getY():获取点击事件距离控件顶边的距离,即视图坐标
getRawX():获取点击事件距离屏幕左边的距离,即绝对坐标
getRawY():获取点击事件距离屏幕顶边的距离,即绝对坐标

View 的滑动处理

当点击事件传到View时,系统会记下触摸点的坐标,手指移动是系统记下移动后触摸的坐标并计算出偏移量,并通过偏移量来修改View的坐标。
实现View滑动涉及许多方法,这里简单介绍六种。

layout()

View进行绘制的时候会调用onLayout()来设置显示的位置,在滑动时,可通过在onTouchEvent()中的获取触摸点坐标,再计算得出滑动后的控件左边,再调用layout方法重新对屏幕进行布局,从而达到移动View的效果。

public boolean onTouchEvent(MotionEvent event){
	...
	//获取手指触摸点的横坐标与纵坐标
	int x = (int) event.getX();
	int y = (int) event.getY();
	
	switch(event.getAction()){
		case MotionEvent.ACTION_DOWN:
			lastX = x;
			lastY = y;
			break;
		case MotionEvent.ACTION_MOVE:
			int offsetX = x - lastX;
			int offsetY = y - lastY;
			layout(getLeft() + offserX, getTop() + offsetY,
				   getRight() + offsetX, getBottom() + offsetY);
			break;
		...
	}
}

offsetLeftAndRight() 与 offsetTopAndBottom()

这两个方法与layout方法思想相同,只在最后调用的时候略有区别。

case MotionEvent.ACTION_MOVE:
	int offsetX = x - lastX;
	int offsetY = y - lastY;
	offsetLeftAndRight(offsetX);
	offsetTopAndBottom(offsetY);
	break;

LayoutParams

LayoutParams主要保存了一个View的布局参数,因此我们可以通过LayoutParams来改变View的布局参数从而达到改变View位置的效果。

case MotionEvent.ACTION_MOVE:
	int offsetX = x - lastX;
	int offsetY = y - lastY;
	LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams)  getLayoutParams();
	layoutParams.leftMargin = getLeft() + offsetX;
	layoutParams.topMargin = getTop() + offsetY;
	setLayoutParams(layoutParams);
	break;
	

动画

可以通过属性动画对控件进行移动,这里暂不叙述,可参考动画相关的文章。

scrollTo scrollBy

二者关系类似于increaseTo 与 increaseBy , 前者 scrollTo(x,y) 代表移动到一个具体的坐标点,而后者 scrllBy(dx, dy) 表示移动的增量。

注意,传入的偏移量应为实际的相反数,因为实际移动的是手机屏幕在画布上的位置

参考文章:
https://juejin.cn/post/7023992403983859720
《Android 进阶之光》
https://www.cnblogs.com/wjtaigwh/p/6593580.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值