Android 面试题总结之Android 进阶(一)

Android 面试题总结之Android 进阶(一)

在前几篇文章中都是讲的基础,大家应该会觉得非常熟悉,但往往我们可能对于基础某些细节认识不够彻底或贯穿不全,我一直认为基础都是比较难的,那么本章节终于到进阶啦,主要讲的是View 的相关知识,在前面《Android 面试题总结之Android 基础 (六)》
中已经对View有了一定的了解,由于内容较多且也是面试必考题,所以将分两篇继续深入的理解View。

在阅读过程中有任何问题,请及时联系。如需转载请注明 fuchenxuan de Blog
本章系《Android 之美 从0到1 -- 高手之路》Android进阶(一)自定义View的过程

[TOC]

掌握

  1. 什么是View?
  2. View 坐标的基本概念
  3. View的生命周期
  4. 如何自定义View

什么是View?

android.app.View 就是手机的UI,View 负责绘制UI,处理事件(evnet),Android 利用 View 打造出所 Widgets,利用 Widget 可打造出互动式的使用者介面,每个View 负责一定区域的绘制。

一张图理解常用控件层级关系

这里写图片描述

View 坐标的基本概念

View的宽高是有top、left、right、bottom参数决定的 而X,Y和translationX,和translationY则负责View位置的改变。

从Android3.0开始,加入了translation的概念,即相对于父容器的偏移量以及X,Y坐标的概念,X,Y代表左上顶点的横纵坐标。当View在发生平移时,getX,getY,setX,setY
get/setTranslationX/Y来获得当前左上点的坐标。

X=left+translationX Y同理。
注意:在View发生改变的过程中,top,left等值代表原始位置,是不会改变的。改变的只有X,Y,translationX/Y。

一张图理解View的坐标概念


这里写图片描述

View的生命周期

Category Methods Description
Creation Constructors 几个View的构造函数
onFinishInflate() 当系统解析完View之后调用onFinishInflate方法
Layout onMeasure(int, int) 确定所有子View的大小
onLayout(boolean, int, int, int, int) 当ViewGroup分配所有的子View的大小和位置时触发
onSizeChanged(int, int, int, int) 当view的大小发生变化时触发
Drawing onDraw(android.graphics.Canvas) view渲染内容的细节
Event processing onKeyDown(int, KeyEvent) 有按键按下后触发
onKeyUp(int, KeyEvent) 有按键按下后弹起时触发
onTrackballEvent(MotionEvent) 轨迹球事件
onTouchEvent(MotionEvent) 触屏事件
Focus onFocusChanged(boolean, int, android.graphics.Rect) 当View获取或失去焦点时触发
onWindowFocusChanged(boolean) 当窗口包含的view获取或失去焦点时触发
Attaching onAttachedToWindow() 当view被附着到一个窗口时触发
onDetachedFromWindow() 当view离开附着的窗口时触发,该方法和 onAttachedToWindow() 是相反
onWindowVisibilityChanged(int) 当窗口中包含的可见的view发生变化时触发

对实现自定义View,不需要重写所有这些方法。事实上,你可以只onDraw(android.graphics.Canvas)

View 的几个构造函数

  • public MyView(Context context)
    java代码直接new一个Custom View实例的时候,会调用第一个构造函数

  • public MyView(Context context, AttributeSet attrs)
    在xml创建但是没有指定style的时候被调用.多了一个AttributeSet类型的参数,自定义属性,在通过布局文件xml创建一个view时,会把XML内的参数通过AttributeSet带入到View内。

  • public MyView(Context context, AttributeSet attrs, int defStyleAttr)
    构造函数中第三个参数是默认的Style,这里的默认的Style是指它在当前Application或Activity所用的Theme中的默认Style,且只有在明确调用的时候才会调用

  • @TargetApi(Build.VERSION_CODES.LOLLIPOP) public MyView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)
    该构造函数是在api21的时候才添加上的

http://blog.csdn.net/vfush

View 的几个重要方法

  • requestLayout
    View重新调用一次layout过程

  • invalidate
    View重新调用一次draw过程

  • forceLayout
    标识View在下一次重绘,需要重新调用layout过程。

  • postInvalidate
    这个方法与invalidate方法的作用是一样的,都是使View树重绘,但两者的使用条件不同,postInvalidate是在非UI线程中调用,invalidate则是在UI线程中调用。

自定义View

简单理解View的绘制

这里我们先简单理解View 的绘制,后续文章我们会深入理解。
1.测量——onMeasure():决定View的大小

2.布局——onLayout():决定View在ViewGroup中的位置

3.绘制——onDraw():如何绘制这个View。

这里写图片描述

自定义View的分类

  • 继承View
  • 继承ViewGroup
  • 继承系统控件(Button,LinearLayout...)

自定义View的过程

  1. 自定义 View 首先要实现一个继承自 View 的类

  2. 添加类的构造方法,通常是三个构造方法,不过从 Android5.0 开始构造方法已经添加到 4 个了

  3. override 父类的方法,如 onDraw,(onMeasure)

  4. 自定义属性,需要在 values 下建立 attrs.xml 文件,在其中定义属性

通过context.obtainStyledAttributes将构造函数中的attrs进行解析出来,就可以拿到相对应的属性.
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.MyView);
mColor = typedArray.getColor(R.styleable.MyView_myColor, 0XFF00FF00);

【注意】三个函数获取尺寸的区别:
getDimension()是基于当前DisplayMetrics进行转换,获取指定资源id对应的尺寸
getDimensionPixelSize()getDimension()功能类似,不同的是将结果转换为int,并且小数部分四舍五入
getDimensionPixelOffset()getDimension()功能类似,不同的是将结果转换为int,取整去除小数。举个例子
列如getDimension()返回结果是20.5f,那么getDimensionPixelSize()返回结果就是 21,getDimensionPixelOffset()返回结果就是20。

  1. 打开布局文件我们可以看到有很多的以xmlns开头的字段。其实这个就是XML name space 的缩写。我们可以使用res-atuo命名空间,就不用在添加自定义View全类名。
    xmlns:app="http://schemas.android.com/apk/res-auto"
/**
 * Created by fuchenxuan on 16/6/4.
 */

public class MyView extends View {
    private int mRadius=200;
    private int mColor;

    public MyView(Context context) {
        this(context,null);
    }

    public MyView(Context context, AttributeSet attrs) {
        this(context, attrs,0);
    }

    public MyView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        //read custom attrs
        TypedArray t = context.obtainStyledAttributes(attrs,
                R.styleable.rainbowbar, 0, 0);
       mRadius = t.getDimensionPixelSize(R.styleable.coutom_radius, (int) hSpace);
        t.getDimensionPixelOffset(R.styleable.coutom_at1, (int) vSpace);
        mColor=t.getColor(R.styleable.color, barColor);
        t.recycle();   // we should always recycle after used
     
     
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        //super.onMeasure(widthMeasureSpec, heightMeasureSpec);
         int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);

        //set size
        setMeasuredDimension(widthMode == MeasureSpec.AT_MOST ? (int) mRadius * 3 : widthSize, heightMode == MeasureSpec.AT_MOST ? (int) mRadius * 3 : heightSize);
    }


    //draw be invoke clire.
    int index = 0;
    @Override
    protected void onDraw(Canvas canvas) {
        //super.onDraw(canvas);
         mPaint = new Paint();
        mPaint.setColor(mColor);
        mPaint.setAntiAlias(true);
         canvas.drawCircle(mRadius, mRadius, mRadius, mPaint);
     }
}

这里是一个普通的自定义View,里面画了圆,根据不同的模式设置了父View的大小。

关于View重写onMeasure()时机
如果用了wrap_content。那么在onMeasure()中就要调用setMeasuredDimension()
来指定view的宽高。如果使用的是match_parent或者一个具体的dp值。那么直接使用super.onMeasure()即可。

自定义ViewGroup

自定义ViewGroup的过程

  1. 自定义 ViewGroup 和自定义View 一样,只是继承自 ViewGroup 的类,和必须实现onLayout()函数
 /**
 * Created by fuchenxuan on 16-6-6.
 */
public class CostumViewGroup extends ViewGroup {


    public CostumViewGroup(Context context) {
        super(context);
    }

    public CostumViewGroup(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int childCount = getChildCount();
        for (int i = 0; i < childCount; i++) {
            View childView = getChildAt(i);
            measureChild(childView, widthMeasureSpec, heightMeasureSpec);
        }
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        if (changed) {
            int childCount = getChildCount();
            for (int i = 0; i < childCount; i++) {
                View childView = getChildAt(i);
                childView.layout(i * childView.getMeasuredWidth(), 0, (i + 1) * childView.getMeasuredWidth(), childView.getMeasuredHeight());
            }
        }
    }

}

这里是一个简单的自定义ViewGroup,实现类似LinearLayout 横向排放子View位置。这就是一个简单的ViewGroup过程。

彻底理解MeasureSpec三种模式

View的大小不仅由自身所决定,同时也会受到父控件的影响,为了我们的控件能更好的适应各种情况,一般会自己进行测量。他们是由 mode+size两部分组成的。widthMeasureSpec和heightMeasureSpec转化成二进制数字表示,他们都是30位的。前两位代表mode(测量模 式),后面28位才是他们的实际数值(size);MeasureSpec.getMode()获取模式,MeasureSpec.getSize()获取尺寸
测量View大小使用的是onMeasure函数,所以我们需要了解三种测量模式:

  • EXACTLY:一般是设置了明确的值(100dp)或者是MATCH_PARENT
  • AT_MOST:表示子布局限制在一个最大值内,一般为WARP_CONTENT
  • UNSPECIFIED:表示子布局想要多大就多大,很少使用

关于ViewGroup重写onMeasure()时机

  • 首先要先测量子View的宽高:
    getChildAt(int index)可以拿到index上的子view。
    getChildCount()得到子view的个数,再循环遍历出子view。

  • 使用子view自身的测量方法
    childView.measure(int wSpec, int hSpec);

使用viewGroup的测量子view的方法

  • measureChild(subView, int wSpec, int hSpec);
    测量某一个子view,多宽,多高, 内部加上了viewGroup的padding值

  • measureChildren(int wSpec, int hSpec);
    测量所有子view 都是 多宽,多高, 内部调用了measureChild方法

  • measureChildWithMargins(subView, intwSpec, int wUsed, int hSpec, int hUsed);测量某一个子view,多宽,多高, 内部加上了viewGroup的padding值、margin值和传入的宽高wUsed、hUsed

问题总结

  1. getWidth()和getMeasuredWidth()的区别?
    getMeasuredWidth():只要一执行完 setMeasuredDimension() 方法,就有值了,并且不再改变。
    getWidth():必须执行完 onMeasure() 才有值,可能发生改变。
    如果 onLayout 没有对子 View 实际显示的宽高进行修改,那么 getWidth() 的值 == getMeasuredWidth() 的值。

  2. onLayout() 和Layout()的区别?
    onLayout() ViewGroup中子View的布局方法,layout()是子View布局的方法

  3. View 里面的 onSavedInstanceState和onRestoreInstanceState的作用?
    View和Activity一样的,每个View都有onSavedInstanceState和onRestoreInstanceState这两个方法,可用于保存和恢复view的状态。

在本章节中我们知道什么是View?,View 坐标的基本概念,理解了View的生命周期,学习了如何自定义View?虽然全是理论知识总结,在后续我们会一起来自定义View的实战学习。不管有没有任何疑问,欢迎在下方留言吧。

更多Android 面试题总结,请点击下方图片哦。

水平有限,若有错漏,欢迎指正,批评,如需转载,请注明出处--http://blog.csdn.net/vfush,谢谢!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,332评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,508评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,812评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,607评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,728评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,919评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,071评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,802评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,256评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,576评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,712评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,389评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,032评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,026评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,473评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,606评论 2 350

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,834评论 25 707
  • Android Studio JNI流程首先在java代码声明本地方法 用到native关键字 本地方法不用去实现...
    MigrationUK阅读 11,848评论 7 123
  • 原文地址:http://www.android100.org/html/201606/06/241682.html...
    AFinalStone阅读 911评论 0 1
  • 这个夏天格外的炎热,不知道是撞上了闰六月的缘故,可这还是不能阻挡我们去浪的脚步。 一张飞机票,一箱的行李,一个人背...
    小小当铺阅读 357评论 0 0
  • 我不知道为何这部剧叫九又二分之一爱情。不知道你心里装着谁。你说过姑娘成组你害怕。于是每次我都单约你。我们的对话从一...
    tristeolive阅读 154评论 0 0