面试题Android面试宝典Android 面试

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

2016-06-08  本文已影响2393人  fuchenxuan

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 的几个构造函数

http://blog.csdn.net/vfush

View 的几个重要方法

自定义View

简单理解View的绘制

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

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

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

这里写图片描述

自定义View的分类

自定义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函数,所以我们需要了解三种测量模式:

关于ViewGroup重写onMeasure()时机

使用viewGroup的测量子view的方法

问题总结

  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,谢谢!

这里写图片描述
上一篇下一篇

猜你喜欢

热点阅读