导航:首页 > 源码编译 > androidview类源码解析

androidview类源码解析

发布时间:2023-03-05 14:51:52

Ⅰ View绘制流程(一)

最近在学习 View 的绘制流程,看了几篇不错的博客( ViewRootImpl的独白,我不是一个View(布局篇) 、 android应用层View绘制流程与源码分析 )自己对照源码,梳理了一遍。

在Activity的onResume之后,当前Activity的Window对象中的View会被添加在WindowManager中。

整个View树的绘图流程是在 ViewRootImpl 类的 performTraversals() 方法(这个方法巨长)开始的,该方法做的执行过程主要是根据之前设置的状态,判断是否重新计算视图大小 (measure) 、是否重新放置视图的位置 (layout) 、以及是否重绘 (draw) ,其核心也就是通过判断来选择顺序执行这三个方法。

ViewRootImpl 调用 performMeasure 执行Window对应的View的测量。

int widthMeasureSpec :他由两部分组成, 高2位表示MODE ,定义在MeasureSpec类(View的内部类)中,有三种类型, MeasureSpec.EXACTLY 表示确定大小, MeasureSpec.AT_MOST 表示最大大小, MeasureSpec.UNSPECIFIED 不确定。 低30位表示size ,也就是父View的大小。对于系统Window类的DecorVIew对象Mode一般都为MeasureSpec.EXACTLY ,而size分别对应屏幕宽高。对于子View来说大小是由父View和子View共同决定的。

默认的尺寸大小即传入的参数都是通过 getDefaultSize 返回的,我们就看一下该方法的实现。

到此一次最基础的元素View的 measure 过程就完成了。

View实际是嵌套的,而且measure是递归传递的,所以每个View都需要 measure ,能够嵌套的View都是ViewGroup的子类,所以在ViewGroup中定义了 measureChildren , measureChild , measureChildWithMargins 方法来对子视图进行测量, measureChildren 内部实质只是循环调用 measureChild , measureChild 和 measureChildWithMargins 的区别就是是否把 margin padding 也作为子视图的大小。 ViewGroup 本身不调用 measureChildWithMargins 和 measureChildren 方法,由继承类通过for循环调用此方法进行子View的测量。下面看一下ViewGroup中稍微复杂的 measureChildWithMargins 方法。

getChildMeasureSpec 的逻辑是通过其父View提供的 MeasureSpec 参数得到 specMode 和 specSize ,然后根据计算出来的 specMode 以及子View的 childDimension (layout_width或layout_height)来计算自身的 measureSpec ,如果其本身包含子视图,则计算出来的 measureSpec 将作为调用其子视图 measure 函数的参数,同时也作为自身调用 setMeasuredDimension 的参数,如果其不包含子视图则默认情况下最终会调用 onMeasure 的默认实现,并最终调用到 setMeasuredDimension 。

Activity 的 onResume 之后,当前 Activity Window 对象中的View(DecorView)会被添加在 WindowManager 中。也就是在 ActivityThread 的 handleResumeActivity 方法中调用 wm.addView(decor, l); 将DecorView添加到 WindowManager 中;

WindowManager 继承 ViewManager ,它的实现类为 WindowManagerImpl ,该类中的方法的具体实现是由其代理类 WindowManagerGlobal 实现的;

在它的 addView 方法中会创建 ViewRootImpl 的实例,然后将Window对应的View(DecorView),ViewRootImpl,LayoutParams顺序添加在WindowManager中,最后将Window所对应的View设置给创建的ViewRootImpl,通过 ViewRootImpl 来更新界面并完成Window的添加过程;

设置view调用的是 ViewRootImpl 的 setView 方法,在该方法中调用 requestLayout(); 方法来异步执行view的绘制方法;之后将 Window 添加到屏幕,通过 WMS (跨进程通信)

在 requestLayout 方法中最终会调用 ViewRootImpl 的 performTraversals(); 方法,该方法做的执行过程主要是根据之前设置的状态,判断是否重新计算视图大小 (measure) 、是否重新放置视图的位置 (layout) 、以及是否重绘 (draw) ,其核心也就是通过判断来选择顺序执行这三个方法: performMeasure 、 performLayout 、 performDraw ;

在 performMeasure 方法中调用的是 View 的 measure 方法,该方法是 final 修饰,不能被子类重写,在该方法中实际调用的是 View 的 onMeasure 方法,子类可以重写 onMeasure 方法来实现自己的测量规则。

View 默认的 onMeasure 方法很简单只是调用了 setMeasuredDimension 方法,该方法的作用是给 View 的成员变量 mMeasuredWidth mMeasuredHeight 赋值,View的测量主要就是给这两个变量赋值,这两个变量一旦赋值,也就意味着测量过程的结束。

setMeasuredDimension 方法传入的尺寸是通过 getDefaultSize(int size, int measureSpec); 方法返回的,在
getDefaultSize 方法中解析 measureSpec Mode Size ,如果Mode为 MeasureSpec.AT_MOST 或者 MeasureSpec.EXACTLY ,最终的size的值为解析后的size;如果 Mode MeasureSpec.UNSPECIFIED ,最终的size为建议的最小值= getSuggestedMinimumWidth ,该方法的具体实现为 return (mBackground == null) ? mMinWidth : max(mMinWidth, mBackground.getMinimumWidth()); ,建议的最小宽度和高度都是由View的Background尺寸与通过设置View的 miniXXX 属性共同决定的

measureSpec 是由 getRootMeasureSpec 方法决定的: measureSpec = View.MeasureSpec.makeMeasureSpec(windowSize, View.MeasureSpec.EXACTLY); 根布局的大小是 Window 的大小,Window大小是不能改变的,总是全屏的。

View实际是嵌套的,而且measure是递归传递的,所以每个View都需要measure,能够嵌套的View都是ViewGroup的子类,所以在ViewGroup中定义了 measureChildren , measureChild , measureChildWithMargins 方法来对子视图进行测量, measureChildren 内部实质只是循环调用 measureChild , measureChild 和 measureChildWithMargins 的区别就是是否把 margin padding 也作为子视图的大小。

measureChildWithMargins 方法的作用就是对 父View 提供的 measureSpec 参数结合 子View LayoutParams 参数进行了调整,然后再来调用 child.measure() 方法,具体通过方法 getChildMeasureSpec 方法来进行参数调整。计算出来自身的 measureSpec 作为调用其子视图 measure 方法的参数,同时也作为自身调用 setMeasuredDimension 的参数,如果其不包含子视图则默认情况下最终会调用 onMeasure 的默认实现,并最终调用到 setMeasuredDimension 。

最终决定 View measure 大小是 View 的 setMeasuredDimension 方法,该方法就是设置mMeasuredWidth和mMeasuredHeight的大小,ViewGroup在 onMeasure 方法调用 setMeasuredDimension 之前调整了 measureSpec

Ⅱ Android的View类是怎样定义的源代码是什么

view的定义还真不是一两句话能说清楚的。源码里代码2万多行,最前面的注释有500多行。

如果你用android studio,直接Ctrl 点击View应该就能看到源码。
当然也可以在网页里查看源码
http ://androidxref.com

Ⅲ Android TV 焦点原理源码解析

相信很多刚接触AndroidTV开发的开发者,都会被各种焦点问题给折磨的不行。不管是学技术还是学习其他知识,都要学习和理解其中原理,碰到问题我们才能得心应手。下面就来探一探Android的焦点分发的过程。

Android焦点事件的分发是从ViewRootImpl的processKeyEvent开始的,源码如下:

源码比较长,下面我就慢慢来讲解一下具体的每一个细节。

dispatchKeyEvent方法返回true代表焦点事件被消费了。

ViewGroup的dispatchKeyEvent()方法的源码如下:

(2)ViewGroup的dispatchKeyEvent执行流程

(3)下面再来瞧瞧view的dispatchKeyEvent方法的具体的执行过程

惊奇的发现执行了onKeyListener中的onKey方法,如果onKey方法返回true,那么dispatchKeyEvent方法也会返回true

可以得出结论:如果想要修改ViewGroup焦点事件的分发,可以这么干:

注意:实际开发中,理论上所有焦点问题都可以通过给dispatchKeyEvent方法增加监听来来拦截来控制。

(1)dispatchKeyEvent方法返回false后,先得到按键的方向direction值,这个值是一个int类型参数。这个direction值是后面来进行焦点查找的。

(2)接着会调用DecorView的findFocus()方法一层一层往下查找已经获取焦点的子View。
ViewGroup的findFocus方法如下:

View的findFocus方法

说明:判断view是否获取焦点的isFocused()方法, (mPrivateFlags & PFLAG_FOCUSED) != 0 和view 的isFocused()方法是一致的。

其中isFocused()方法的作用是判断view是否已经获取焦点,如果viewGroup已经获取到了焦点,那么返回本身即可,否则通过mFocused的findFocus()方法来找焦点。mFocused其实就是ViewGroup中获取焦点的子view,如果mView不是ViewGourp的话,findFocus其实就是判断本身是否已经获取焦点,如果已经获取焦点了,返回本身。

(3)回到processKeyEvent方法中,如果findFocus方法返回的mFocused不为空,说明找到了当前获取焦点的view(mFocused),接着focusSearch会把direction(遥控器按键按下的方向)作为参数,找到特定方向下一个将要获取焦点的view,最后如果该view不为空,那么就让该view获取焦点。

(4)focusSearch方法的具体实现。

focusSearch方法的源码如下:

可以看出focusSearch其实是一层一层地网上调用父View的focusSearch方法,直到当前view是根布局(isRootNamespace()方法),通过注释可以知道focusSearch最终会调用DecorView的focusSearch方法。而DecorView的focusSearch方法找到的焦点view是通过FocusFinder来找到的。

(5)FocusFinder是什么?

它其实是一个实现 根据给定的按键方向,通过当前的获取焦点的View,查找下一个获取焦点的view这样算法的类。焦点没有被拦截的情况下,Android框架焦点的查找最终都是通过FocusFinder类来实现的。

(6)FocusFinder是如何通过findNextFocus方法寻找焦点的。

下面就来看看FocusFinder类是如何通过findNextFocus来找焦点的。一层一层往下看,后面会执行findNextUserSpecifiedFocus()方法,这个方法会执行focused(即当前获取焦点的View)的findUserSetNextFocus方法,如果该方法返回的View不为空,且isFocusable = true && isInTouchMode() = true的话,FocusFinder找到的焦点就是findNextUserSpecifiedFocus()返回的View。

(7)findNextFocus会优先根据XML里设置的下一个将获取焦点的View ID值来寻找将要获取焦点的View。

看看View的findUserSetNextFocus方法内部都干了些什么,OMG不就是通过我们xml布局里设置的nextFocusLeft,nextFocusRight的viewId来找焦点吗,如果按下Left键,那么便会通过nextFocusLeft值里的View Id值去找下一个获取焦点的View。

可以得出以下结论:

1. 如果一个View在XML布局中设置了focusable = true && isInTouchMode = true,那么这个View会优先获取焦点。

2. 通过设置nextFocusLeft,nextFocusRight,nextFocusUp,nextFocusDown值可以控制View的下一个焦点。

Android焦点的原理实现就这些。总结一下:

为了方便同志们学习,我这做了张导图,方便大家理解~

Ⅳ Android RecyclerView的布局管理器 GridLayoutManager源码分析<三>

Android RecyclerView绘制页面的源码分析<一>

Android RecyclerView布局管理器LinearLayoutManager源码分析<二>

以上两篇讲述了RecycerView LinearLayoutManager 页面绘制以及子条目的布局,LinearLayoutManager 是一个线性的管理器即是控制垂直以及水平展示 一个条目表示一行,然而显示生活中有很多需求是一行展示多个子条目这个时候就用到了 GridLayoutManager 表格布局管理器 来实现这种需求
GridLayoutManager :继承于LinearLayoutManager的网格状布局管理器 默认一行展示一个

GridLayoutManager网格布局管理器 实现一行展示多个条目,本章解释了GridLayoutManager的简单源码实现表格布局的大概调用,下一章展示复杂的表格布局即展示不顾地条目数的表格布局

Ⅳ Android源码解析Window系列第(一)篇---Window的基本认识和Activity的加载流程

您可能听说过View ,ViewManager,Window,PhoneWindow,WindowManager,WindowManagerService,可是你知道这几个类是什么关系,干嘛用的。概括的来说,View是放在Window中的,Window是一个抽象类,它的具体实现是PhoneWindow,PhoneWindow还有个内部类DecorView,WindowManager是一个interface,继承自ViewManager,它是外界访问Window的入口,,提供了add/remove/updata的方法操作View,WindowManager与WindowManagerSerice是个跨进程的过程,WindowManagerService的职责是对系统中的所有窗口进行管理。如果您不太清楚,建议往下看,否则就不要看了。

Android系统的Window有很多种,大体上来说,Framework定义了三种窗口类型;

这就是Framework定义了三种窗口类型,这三种类型定义在WindowManager的内部类LayoutParams中,WindowManager讲这三种类型 进行了细化,把每一种类型都用一个int常量来表示,这些常量代表窗口所在的层,WindowManagerService在进行窗口叠加的时候,会按照常量的大小分配不同的层,常量值越大,代表位置越靠上面, 所以我们可以猜想一下,应用程序Window的层值常量要小于子Window的层值常量,子Window的层值常量要小于系统Window的层值常量。 Window的层级关系如下所示。

上面说了Window分为三种,用Window的type区分,在搞清楚Window的创建之前,我们需要知道怎么去描述一个Window,我们就把Window当做一个实体类,给我的感觉,它必须要下面几个字段。

实际上WindowManager.LayoutParams对Window有很详细的定义。

提取几个重要的参数

Window是一个是一个抽象的概念,千万不要认为我们所看到的就是Window,我们平时所看到的是视图,每一个Window都对应着一个View,View和Window通过ViewRootImpl来建立联系。有了View,Window的存在意义在哪里呢,因为View不能单独存在,它必须依附着Window,所以有视图的地方就有Window,比如Activity,一个Dialog,一个PopWindow,一个菜单,一个Toast等等。

通过上面我们知道视图和Window的关系,那么有一个问题,是先有视图,还是先有Window。这个答案只有在源码中找了。应用程序的入口类是ActivityThread,在ActivityThread中有performLaunchActivity来启动Activity,这个performLaunchActivity方法内部会创建一个Activity。

如果activity不为null,就会调用attach,在attach方法中通过PolicyManager创建了Window对象,并且给Window设置了回调接口。

PolicyManager的实现类是Policy

这样Window就创建出来了, 所以先有Window,后有视图,视图依赖Window存在 ,再说一说视图(Activity)为Window设置的回调接口。

Activity实现了这个回调接口,当Window的状态发生变化的时候,就会回调Activity中实现的这些接口,有些回调接口我们还是熟悉的,dispatchTouchEvent,onAttachedToWindow,onDetachedFromWindow等。

下面分析view是如何附属到window上的,通过上面可以看到,在attach之后就要执行callActivityOnCreate,在onCreate中我们会调用setContentView方法。

getWindow获取了Window对象,Window的具体实现类是PhoneWindow,所以要看PhoneWindow的setContentView方法。

这里涉及到一个mContentParent变量,他是一个DecorView的一部分,DecorView是PhoneWindow的一个内部类,我先介绍一下关于DecorView的知识。

DecorView是Activity的顶级VIew,DecorView继承自FrameLayout,在DecorView中有上下两个部分,上面是标题栏,下面是内容栏,我们通过PhoneWindow的setContentView所设置的布局文件是加到内容栏(mContentParent)里面的,View层的事件都是先经过DecorView在传递给我们的View的。

OK在回到setContentView的源码分析,我们可以得到Activity的Window创建需要三步。

- 1、 如果没有DecorView,在installDecor中创建DecorView。

- 2、将View添加到decorview中的mContentParent中。

- 3、回调Activity的onContentChanged接口。

先看看第一步,installDecor的源码

installDecor中调用了generateDecor,继续看

直接给new一个DecorView,有了DecorView之后,就可以加载具体的布局文件到DecorView中了,具体的布局文件和系统和主题有关系。

在看第二步,将View添加到decorview中的mContentParent中。

直接将Activity视图加到DecorView的mContentParent中,最后一步,回调Activity的onContentChanged接口。在Activity中寻找onContentChanged方法,它是个空实现,我们可以在子Activity中处理。

到此DecorView被创建完毕,我们一开始从Thread中的handleLaunchActivity方法开始分析,首先加载Activity的字节码文件,利用反射的方式创建一个Activity对象,调用Activity对象的attach方法,在attach方法中,创建系统需要的Window并为设置回调,这个回调定义在Window之中,由Activity实现,当Window的状态发生变化的时候,就会回调Activity实现的这些回调方法。调用attach方法之后,Window被创建完成,这时候需要关联我们的视图,在handleLaunchActivity中的attach执行之后就要执行handleLaunchActivity中的callActivityOnCreate,在onCreate中我们会调用setContentView方法。通过setContentView,创建了Activity的顶级View---DecorView,DecorView的内容栏(mContentParent)用来显示我们的布局。 这个是我们上面分析得到了一个大致流程,走到这里,这只是添加的过程,还要有一个显示的过程,显示的过程就要调用handleLaunchActivity中的handleResumeActivity方法了。最后会调用makeVisible方法。

这里面首先拿到WindowManager对象,用tWindowManager 的父接口ViewManager接收,ViewManager可以
最后调用 mDecor.setVisibility(View.VISIBLE)设置mDecor可见。到此,我们终于明白一个Activity是怎么显示在我们的面前了。
参考链接:
http://blog.csdn.net/feiclear_up/article/details/49201357

Ⅵ android的自定义View的实现原理哪位能给我个思路呢。谢谢。

如果说要按类型来划分的话,自定义View的实现方式大概可以分为三种,自绘控件、组合控件、以及继承控件。那么下面我们就来依次学习一下,每种方式分别是如何自定义View的。

一、自绘控件

自绘控件的意思就是,这个View上所展现的内容全部都是我们自己绘制出来的。绘制的代码是写在onDraw()方法中的,而这部分内容我们已经在Android视图绘制流程完全解析,带你一步步深入了解View(二)中学习过了。

下面我们准备来自定义一个计数器View,这个View可以响应用户的点击事件,并自动记录一共点击了多少次。新建一个CounterView继承自View,代码如下所示:

<?xmlversion="1.0"encoding="utf-8"?>
<RelativeLayoutxmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="50dp"
android:background="#ffcb05">

<Button
android:id="@+id/button_left"
android:layout_width="60dp"
android:layout_height="40dp"
android:layout_centerVertical="true"
android:layout_marginLeft="5dp"
android:background="@drawable/back_button"
android:text="Back"
android:textColor="#fff"/>

<TextView
android:id="@+id/title_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="ThisisTitle"
android:textColor="#fff"
android:textSize="20sp"/>

</RelativeLayout>

在这个布局文件中,我们首先定义了一个RelativeLayout作为背景布局,然后在这个布局里定义了一个Button和一个TextView,Button就是标题栏中的返回按钮,TextView就是标题栏中的显示的文字。

接下来创建一个TitleView继承自FrameLayout,代码如下所示:

{

privateButtonleftButton;

privateTextViewtitleText;

publicTitleView(Contextcontext,AttributeSetattrs){
super(context,attrs);
LayoutInflater.from(context).inflate(R.layout.title,this);
titleText=(TextView)findViewById(R.id.title_text);
leftButton=(Button)findViewById(R.id.button_left);
leftButton.setOnClickListener(newOnClickListener(){
@Override
publicvoidonClick(Viewv){
((Activity)getContext()).finish();
}
});
}

publicvoidsetTitleText(Stringtext){
titleText.setText(text);
}

publicvoidsetLeftButtonText(Stringtext){
leftButton.setText(text);
}

(OnClickListenerl){
leftButton.setOnClickListener(l);
}

}

TitleView中的代码非常简单,在TitleView的构建方法中,我们调用了LayoutInflater的inflate()方法来加载刚刚定义的title.xml布局,这部分内容我们已经在Android LayoutInflater原理分析,带你一步步深入了解View(一)这篇文章中学习过了。

接下来调用findViewById()方法获取到了返回按钮的实例,然后在它的onClick事件中调用finish()方法来关闭当前的Activity,也就相当于实现返回功能了。

另外,为了让TitleView有更强地扩展性,我们还提供了setTitleText()、setLeftButtonText()、setLeftButtonListener()等方法,分别用于设置标题栏上的文字、返回按钮上的文字、以及返回按钮的点击事件。

到了这里,一个自定义的标题栏就完成了,那么下面又到了如何引用这个自定义View的部分,其实方法基本都是相同的,在布局文件中添加如下代码:

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

<com.example.customview.TitleView
android:id="@+id/title_view"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</com.example.customview.TitleView>

</RelativeLayout>

这样就成功将一个标题栏控件引入到布局文件中了,运行一下程序。

现在点击一下Back按钮,就可以关闭当前的Activity了。如果你想要修改标题栏上显示的内容,或者返回按钮的默认事件,只需要在Activity中通过findViewById()方法得到TitleView的实例,然后调用setTitleText()、setLeftButtonText()、setLeftButtonListener()等方法进行设置就OK了。
阅读全文

与androidview类源码解析相关的资料

热点内容
数控车床子程序编程 浏览:103
floydwarshall算法 浏览:713
丢失微信app怎么找 浏览:248
php能写前端吗 浏览:3
服务器如何更改raid模式 浏览:82
方舟服务器怎么导出来 浏览:606
手机显示服务器异常什么鬼 浏览:377
新闻服务器的网址是什么 浏览:669
程序员年底招人 浏览:319
广发app怎么查房贷 浏览:860
安卓手机怎么下薯仔 浏览:921
只有一个app显示网络异常怎么回事 浏览:988
解压玩具是水宝宝 浏览:817
压缩机保护怎么解决 浏览:944
单片机简易电子时钟 浏览:402
pdf影印版 浏览:689
单片机的中断技术 浏览:626
表格加密才能打开 浏览:39
多态可以提高编译可靠性吗 浏览:599
拼好魔方的app怎么下载 浏览:503