文档章节

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

fuchenxuan
 fuchenxuan
发布于 2016/06/09 00:37
字数 2565
阅读 34
收藏 0

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

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

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

 

 

掌握

  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。

  5. 打开布局文件我们可以看到有很多的以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,谢谢!
    这里写图片描述

© 著作权归作者所有

fuchenxuan
粉丝 10
博文 29
码字总数 52850
作品 0
南昌
私信 提问
Android知识体系总结(全方面覆盖Android知识结构,面试&进阶)

Android知识体系总结(全方面覆盖Android知识结构,面试&进阶 Version-1.0.1 时间:2018.09) 基本内容 : Android基础知识:基本涵盖Android所有知识体系,四大组件,Fragment,WebView,事件分发...

Java高级架构
2018/11/01
0
0
Android--面试中遇到的问题总结(三)

《Android 开发工程师面试指南 LearningNotes 》,作者是陶程,由梁观全贡献部分。大家可以去知乎关注这两位用心的少年。这份指南包含了大部分Android开发的基础、进阶知识,不仅可以帮助准备...

sealin
2017/02/22
0
0
WebView内存泄露的解决方案

一、简介: 做Android开发的相信都对webview不会陌生,而且也对系统自带的webview本身存在的问题也是怨念很久了,一方面是本身对js的支持不是很好另外一方面就是经常被人诟病的内存泄露了,网...

北辰丨丶
03/06
44
0
金三银四背后,一个 Android 程序员的面试心得

回顾一下自己这段时间的经历,九月份的时候,公司通知了裁员,我匆匆忙忙地出去面了几家,但最终都没有拿到offer,我感觉今年的寒冬有点冷。到十二月份,公司开始第二波裁员,我决定主动拿赔...

终端研发部
03/19
48
0
Android Studio Activity模版开发

为什么要使用模版开发 在开发之中,我们总会遇到一些比较通用的模块,比如启动页,登陆功能,网络请求,MVP模式等等。有些功能甚至需要重复使用多次,我们通常的做法是把之前的模块拷贝过来,...

北辰丨丶
03/04
17
0

没有更多内容

加载失败,请刷新页面

加载更多

ES6

ES6:不改变原理的基础上,让API变得更简单 一、let:代替var用于声明变量 1、var的缺点: (1)声明提前 (2)没有块级作用域 2、let的优点: (1)组织了申明提前 (2)让let所在的块({}),...

wytao1995
今天
3
0
kubernetes 环境搭建 —— minikube

创建集群 minikube start 搭建好 k8s 集群后,可以查看集群的状态以及部署应用。主要用到的是 k8s 的 api,这通常需借助于 kutectl 命令行工具 基本操作 kubectl versionkubectl cluster-i...

lemos
今天
9
0
关于js混淆与反混淆还原操作

使用js的混淆加密,其目的是为了保护我们的前端代码逻辑,对应一些搞技术吃饭的公司来说,为了防止被竞争对手抓取或使用自己的代码,就会考虑如何加密,或者混淆js来达到代码保护。 1、为什么...

开源oschina
今天
11
0
用盛金公式解三次方程(ansi c版)

/* cc cubic.c -lm gcc cubic.c -lm Shengjin's Formulas Univariate cubic equation aX ^ 3 + bX ^ 2 + cX + d = 0, (a, b, c, d < R, and a!= 0). Multiple root disc......

wangxuwei
今天
9
0
JBolt开发平台入门(16)- 弹出Dialog显示指定区域的html内容

在JBolt里,有个主从表Demo。 子表中除了普通的table显示,还有其它从表显示形式 比如下方案例:是针对一个升级管理子表中存放版本的changelog富文本信息。 需求是点击左上角的弹出查看按钮,...

山东-小木
今天
46
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部