导航:首页 > 源码编译 > view元素的源码

view元素的源码

发布时间:2022-09-25 12:39:08

1. ViewPager系列文章(一)- ViewPager源码分析及加载页面原理图

1>:点击 viewPager.setAdapter进入下边源码,会调用 populate() 方法,这个方法作用是创建和销毁子条目(子item):

在populate()方法中:
创建ItemView:mAdapter.instantiateItem(this, position);
销毁ItemView:mAdapter.destroyItem(this, pos, ii.object);
所以由ViewPager的源码可以看出,ViewPager里边无论放多少个页面都不会内存溢出,它会不断的去创建和销毁view;

和 ListView、RecyclerView不一样,ListView、RecyclerView是会不断的复用view,而viewpager是不断的创建和销毁view

轮播图刚打开默认显示当前页,是第一页,默认会缓存左右两个页面,如果左边没有,只有右边有,那么右边是第0页,当前页是第一页;
如果你滑动到第1页,ViewPager会默认把 左边第0页 和 右边第2页 创建出来;
如果你滑动到第2页,ViewPager会默认把第1页和第3页创建出来,而原来的第0页就会变成需要销毁的页面;

如果想要缓存多页,可以调用setOffscreenPageLimit()方法:
setOffscreenPageLimit(1):ViewPager机制默认就是缓存1,表示左边、右边各缓存1页,加上自己,总共是3页,其余页面全部销毁;
setOffscreenPageLimit(2):表示默认给左右各缓存2页,共4页,加上自己,总共缓存5页,其余页面全部销毁;
setOffscreenPageLimit(3):表示默认给左右各缓存3页,共6页,加上自己,总共缓存7页,其余页面全部销毁;

因为 smoothScrollTo()滑动方法也调用populate(),而populate()方法维护了当前显示页面和 左右缓存的页面,就能做到无限滑动而不出问题;

A:从populate()源码中可知:先判断页面是否在缓存范围内:如果在,则addNewItem添加进来,否则在destroyItem掉;
B:ViewPager会缓存左右两边页面+1(当前显示页面),默认认为当前页面的 左右两边各有1个,用户可以手动调用setOffscreenPageLimit()方法设置数量,如果传的值小于1,就默认设置为1;
ViewPager实际示意图如下:

2. 安卓开发中viewpager的源码是在api多少里

1.首先ViewPager在哪个包下?

答:如图,就是在v4.view包下,另外,ViewPager也是3.0(api 11)后Google推出的,对于低版本的
可以自行导入v4包来解决低版本兼容的问题!

2.ViewPager的简单介绍
答:ViewPager就是一个页面切换的组件而已,我们可以往里面填多个view,然后
我们左右滑动切换不同的view而已,和ListView一样,我们也需要一个Adapter(适配器),将要显示
的View和我们的ViewPager进行绑定,而ViewPager有特定的Adapter——PagerAdapter!
另外,Google官方是建议我们使用Fragment来填充ViewPager的,这样可以更加方便的生成
每个Page以及管理每个Page的生命周期!当然也给我们提供了两个不同的Adapter!分别是:
FragmentPageAdapter和FragmentStatePagerAdapter,前者适用于页面较少的情况,后者
适用于页面较多的情况,,对于两个Adapter的区别会在后面进行讲解!

3.ViewPager的适配器——PagerAdapter讲解
答:ViewPager和Listview这些组件其实都是类似的,只是前者单位是Page(页面),后者是Item(项)
而PagerAdapter也是特别的!

①必须重写的四个方法:

②方法简介:
先说下简单的两个吧:
getCount( ):获得viewpager中有多少个view
destroyItem( ):移除一个给定位置的页面。适配器有责任从容器中删除这个视图。这是为了确保
在finishUpdate(viewGroup)返回时视图能够被移除。
而另外两个就涉及到一个key的概念了:
instantiateItem( ):①将给定位置的view添加到ViewGroup(容器)中,创建并显示出来
②返回一个代表新增页面的Object(key),通常都是直接返回view本身就可以了,
当然你也可以自定义自己的key,但是key和每个view要一一对应的关系
isViewFromObject( ):判断instantiateItem(ViewGroup, int)函数所返回来的Key与一个页面视图是否是
代表的同一个视图(即它俩是否是对应的,对应的表示同一个View),通常我们直接写
return view == object;就可以了,至于为什么要这样讲起来比较复杂,后面有机会进行了解吧
貌似是ViewPager中有个存储view状态信息的ArrayList,根据View取出对应信息的吧!
③代码示例:
Fragment的简单用法:添加三个View到ViewPager,然后滑动
效果图如下:

④实现流程:
step 1:定义三个布局,等下用来填充ViewPager的,例子比较简单,直接用不同TextView与背景颜色来区分
view2,view3只需要一下,然后改下颜色与文字就可以了

view1.xml

step 2:主界面布局文件的编写,一个TextView + ViewPager,注意ViewPager的标签:

是这样的:android.support.v4.view.ViewPager

activity_main.xml:

step 3:编写我们的自定义PagerAdapter适配器类,继承PagerAdapter,实现四个基本的方法:
getCount( ),isViewFormObject( ),instantiateItem( ),destoryItem( ),同时还要定义一个View的
集合,用来放viewpager中的view
MyPagerAdapter.java:

package com.jay.example.viewpagerdemo1;

import java.util.ArrayList;

import android.support.v4.view.PagerAdapter;
import android.view.View;
import android.view.ViewGroup;

public class MyPageAdapter extends PagerAdapter {

private ArrayList viewLists;

public MyPageAdapter() {}
public MyPageAdapter(ArrayList viewLists)
{
super();
this.viewLists = viewLists;
}

@Override
public int getCount() {
return viewLists.size();
}

@Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}

@Override
public Object instantiateItem(ViewGroup container, int position) {
container.addView(viewLists.get(position));
return viewLists.get(position);
}

@Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView(viewLists.get(position));
}
}

step 4:最后就是MainActivity了,也很简单,实例化ViewPager对象以及View集合,然后通过LayoutInflater动态
加载三个view,通过add方法添加到View集合中,接着把View集合作为参数传递给MyPagerAdapter对象,
最后 调用setAdapter(mAdapter);就可以了
MainActivity.java

3. 电脑view控件视频录制源码,view收到视频画面,通过button按钮将view画面保

imageView上好像是不好添加组件吧。。。 你一定需要一个imageView吗? 如果不一定,其实可以不添加ImageView,直接在你的布局里设置背景图片为本来ImageView中要添加的图片,然后再在该布局上添加Button

4. Android UI绘制之View绘制的工作原理

这是AndroidUI绘制流程分析的第二篇文章,主要分析界面中View是如何绘制到界面上的具体过程。

ViewRoot 对应于 ViewRootImpl 类,它是连接 WindowManager 和 DecorView 的纽带,View的三大流程均是通过 ViewRoot 来完成的。在 ActivityThread 中,当 Activity 对象被创建完毕后,会将 DecorView 添加到 Window 中,同时会创建 ViewRootImpl 对象,并将 ViewRootImpl 对象和 DecorView 建立关联。

measure 过程决定了 View 的宽/高, Measure 完成以后,可以通过 getMeasuredWidth 和 getMeasuredHeight 方法来获取 View 测量后的宽/高,在几乎所有的情况下,它等同于View的最终的宽/高,但是特殊情况除外。 Layout 过程决定了 View 的四个顶点的坐标和实际的宽/高,完成以后,可以通过 getTop、getBottom、getLeft 和 getRight 来拿到View的四个顶点的位置,可以通过 getWidth 和 getHeight 方法拿到View的最终宽/高。 Draw 过程决定了 View 的显示,只有 draw 方法完成后 View 的内容才能呈现在屏幕上。

DecorView 作为顶级 View ,一般情况下,它内部会包含一个竖直方向的 LinearLayout ,在这个 LinearLayout 里面有上下两个部分,上面是标题栏,下面是内容栏。在Activity中,我们通过 setContentView 所设置的布局文件其实就是被加到内容栏中的,而内容栏id为 content 。可以通过下面方法得到 content:ViewGroup content = findViewById(R.android.id.content) 。通过 content.getChildAt(0) 可以得到设置的 view 。 DecorView 其实是一个 FrameLayout , View 层的事件都先经过 DecorView ,然后才传递给我们的 View 。

MeasureSpec 代表一个32位的int值,高2位代表 SpecMode ,低30位代表 SpecSize , SpecMode 是指测量模式,而 SpecSize 是指在某种测量模式下的规格大小。
SpecMode 有三类,如下所示:

UNSPECIFIED

EXACTLY

AT_MOST

LayoutParams需要和父容器一起才能决定View的MeasureSpec,从而进一步决定View的宽/高。

对于顶级View,即DecorView和普通View来说,MeasureSpec的转换过程略有不同。对于DecorView,其MeasureSpec由窗口的尺寸和其自身的LayoutParams共同确定;

对于普通View,其MeasureSpec由父容器的MeasureSpec和自身的Layoutparams共同决定;

MeasureSpec一旦确定,onMeasure就可以确定View的测量宽/高。

小结一下

当子 View 的宽高采用 wrap_content 时,不管父容器的模式是精确模式还是最大模式,子 View 的模式总是最大模式+父容器的剩余空间。

View 的工作流程主要是指 measure 、 layout 、 draw 三大流程,即测量、布局、绘制。其中 measure 确定 View 的测量宽/高, layout 确定 view 的最终宽/高和四个顶点的位置,而 draw 则将 View 绘制在屏幕上。

measure 过程要分情况,如果只是一个原始的 view ,则通过 measure 方法就完成了其测量过程,如果是一个 ViewGroup ,除了完成自己的测量过程外,还会遍历调用所有子元素的 measure 方法,各个子元素再递归去执行这个流程。

如果是一个原始的 View,那么通过 measure 方法就完成了测量过程,在 measure 方法中会去调用 View 的 onMeasure 方法,View 类里面定义了 onMeasure 方法的默认实现:

先看一下 getSuggestedMinimumWidth 和 getSuggestedMinimumHeight 方法的源码:

可以看到, getMinimumWidth 方法获取的是 Drawable 的原始宽度。如果存在原始宽度(即满足 intrinsicWidth > 0),那么直接返回原始宽度即可;如果不存在原始宽度(即不满足 intrinsicWidth > 0),那么就返回 0。

接着看最重要的 getDefaultSize 方法:

如果 specMode 为 MeasureSpec.UNSPECIFIED 即未指定模式,那么返回由方法参数传递过来的尺寸作为 View 的测量宽度和高度;
如果 specMode 不是 MeasureSpec.UNSPECIFIED 即是最大模式或者精确模式,那么返回从 measureSpec 中取出的 specSize 作为 View 测量后的宽度和高度。

看一下刚才的表格:

当 specMode 为 EXACTLY 或者 AT_MOST 时,View 的布局参数为 wrap_content 或者 match_parent 时,给 View 的 specSize 都是 parentSize 。这会比建议的最小宽高要大。这是不符合我们的预期的。因为我们给 View 设置 wrap_content 是希望View的大小刚好可以包裹它的内容。

因此:

如果是一个 ViewGroup,除了完成自己的 measure 过程以外,还会遍历去调用所有子元素的 measure 方法,各个子元素再递归去执行 measure 过程。

ViewGroup 并没有重写 View 的 onMeasure 方法,但是它提供了 measureChildren、measureChild、measureChildWithMargins 这几个方法专门用于测量子元素。

如果是 View 的话,那么在它的 layout 方法中就确定了自身的位置(具体来说是通过 setFrame 方法来设定 View 的四个顶点的位置,即初始化 mLeft , mRight , mTop , mBottom 这四个值), layout 过程就结束了。

如果是 ViewGroup 的话,那么在它的 layout 方法中只是确定了 ViewGroup 自身的位置,要确定子元素的位置,就需要重写 onLayout 方法;在 onLayout 方法中,会调用子元素的 layout 方法,子元素在它的 layout 方法中确定自己的位置,这样一层一层地传递下去完成整个 View 树的 layout 过程。

layout 方法的作用是确定 View 本身的位置,即设定 View 的四个顶点的位置,这样就确定了 View 在父容器中的位置;
onLayout 方法的作用是父容器确定子元素的位置,这个方法在 View 中是空实现,因为 View 没有子元素了,在 ViewGroup 中则进行抽象化,它的子类必须实现这个方法。

1.绘制背景( background.draw(canvas); );
2.绘制自己( onDraw );
3.绘制 children( dispatchDraw(canvas) );
4.绘制装饰( onDrawScrollBars )。

dispatchDraw 方法的调用是在 onDraw 方法之后,也就是说,总是先绘制自己再绘制子 View 。

对于 View 类来说, dispatchDraw 方法是空实现的,对于 ViewGroup 类来说, dispatchDraw 方法是有具体实现的。

通过 dispatchDraw 来传递的。 dispatchDraw 会遍历调用子元素的 draw 方法,如此 draw 事件就一层一层传递了下去。dispatchDraw 在 View 类中是空实现的,在 ViewGroup 类中是真正实现的。

如果一个 View 不需要绘制任何内容,那么就设置这个标记为 true,系统会进行进一步的优化。

当创建的自定义控件继承于 ViewGroup 并且不具备绘制功能时,就可以开启这个标记,便于系统进行后续的优化;当明确知道一个 ViewGroup 需要通过 onDraw 绘制内容时,需要关闭这个标记。

参考:《Android开发艺术探索》

5. 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

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

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

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

7. android,中像activity 和view 这种常用类的源码查看方法

  1. 打开SDK Manager,下载源码(sources for android sdk)。

  2. 然后在eclipse中,ctrl+左键点击比如View或者Activity,在打开的界面中选择change attached source。

  3. 选择external location。如果源码是jar文件,选择external file。但是通过sdk manager下载的都是文件夹

  4. 指向SDK路径/sources/android-xx目录即可。

8. View源码——fitSystemWindows详解

该方法在窗口的insets发生变化时,被调用。View调用该方法,以调整内容来适应窗口的变化。窗口的insets变化,包括status bar、软键盘、navigation bar等的显隐。
一般情况下我们不需要关心这个方法。但如果设置 SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN、SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION 等标识开启沉浸式,默认情况下,我们的内容区域就会被status bar、软键盘等遮挡。
该方法的默认实现会根据insets值来设置view的padding,并返回true,防止该事件继续传递(即只有一个view会真正fitSystemWindows)。要开启该方法,需要执行 setFitsSystemWindows(true) ,或在XML中设置 android:fitsSystemWindows="true" 。
如果只需要为XML文件的根布局设置fitSystemWindows,该方法的默认实现就能满足。如果需要适配更加复杂的布局(比如有两个子View,一个在顶部,一个在底部,则顶部的需要根据insets设置paddingTop,底部的需要根据insets设置paddingBottom),你就需要重写该方法,自行处理insets。
需要说明的是,如果不做任何处理,所有view接收到的insets都是一样的(比如top是status bar的高度,bottom是软键盘的高度)。该方法的执行在layout之前。

WindowInsets

该类封装了几种不同的insets。 mSystemWindowInsets 对应status bar、软键盘等引起的insets。可用方法如下:

获取四个边的inset

消费掉insets,使之不再传递

生成新的WindowInsets对象

该方法会被第一个调用,如果设置了listener,则执行自定义的listener,否则执行 onApplyWindowInsets 。

默认情况下该方法会执行第一个分支,即执行上面的 fitSystemWindows 。api20以上,android建议覆写该方法,而不是已废弃的 fitSystemWindows 。

监听fitSystemWindow事件。
listener类如下:

ViewGroup:

可以看到,从根布局开始,先执行本身的 super.dispatchApplyWindowInsets 方法,然后遍历执行子View的 dispatchApplyWindowInsets 方法,如果被消费掉,则停止传递。

布局如下:

设置沉浸式:

设置软键盘适配方式:

现在布局是这个样子的:

图1标题栏被状态栏遮挡,图2页面被软键盘遮挡。

再次强调一个概念,默认情况下,设置 android:fitsSystemWindows="true" 只有一个View会生效。

为根布局设置 android:fitsSystemWindows="true" ,同时为了方便观察,给根布局设置一个灰色背景:

可以看到已经适配了软键盘,但顶部toolbar区域也显示了根布局的灰色背景,显然默认实现满足不了我们的需求。

解决方式有很多,这里介绍两种比较优雅的方式。

首先需要为Toolbar也设置 android:fitsSystemWindows="true"

达到了预期效果。

自定义根布局

自定义toolbar

两种方法实际上是等价,不过显然还是第一种方式更友好,只需要设置一个listener就能搞定,但因为api版本限制,所以很多情况下还是要使用第二种方式。

如果覆写了 fitSystemWindows(insets) 或者 onApplyWindowInsets(WindowInsets) ,覆写方法中不调用对应的super方法,则不需要设置 setFitsSystemWindows(true) 或者 android:fitsSystemWindows="true" 。

原因如下:

9. sas中,如何查看viewtable 的源代码

informat or format 命令可以指定和修改变量格式类型。

阅读全文

与view元素的源码相关的资料

热点内容
服务器怎么设定公用网络 浏览:97
程序员自己尝尿检测出糖尿病 浏览:590
打印添加pdf 浏览:930
苹果解压专家账号 浏览:840
度晓晓app为什么关闲 浏览:226
net文件是伪编译码吗 浏览:149
伴随矩阵的matlab编程 浏览:63
单片机和h桥是什么意思 浏览:312
51单片机光控设计论文 浏览:652
涡旋式压缩机无油 浏览:729
企业网搭建及应用pdf 浏览:744
symanteclinux 浏览:878
程序员朋友化妆改造 浏览:493
应用被加密但不知道密码 浏览:586
百度云黑马android 浏览:773
java格式化long 浏览:893
汽车如何加密文档 浏览:625
公司理财第9版pdf 浏览:524
微信个人表情在文件夹 浏览:833
加密狗密码监控 浏览:437