Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android 面試題總結之Android 進階(一)

Android 面試題總結之Android 進階(一)

編輯:關於Android編程

掌握

什麼是View? View 坐標的基本概念 View的生命周期 如何自定義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的過程

自定義 View 首先要實現一個繼承自 View 的類

添加類的構造方法,通常是三個構造方法,不過從 Android5.0 開始構造方法已經添加到 4 個了

override 父類的方法,如 onDraw,(onMeasure)

自定義屬性,需要在 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。

打開布局文件我們可以看到有很多的以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的過程

自定義 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

問題總結

getWidth()和getMeasuredWidth()的區別?
getMeasuredWidth():只要一執行完 setMeasuredDimension() 方法,就有值了,並且不再改變。
getWidth():必須執行完 onMeasure() 才有值,可能發生改變。
如果 onLayout 沒有對子 View 實際顯示的寬高進行修改,那麼 getWidth() 的值 == getMeasuredWidth() 的值。

onLayout() 和Layout()的區別?
onLayout() ViewGroup中子View的布局方法,layout()是子View布局的方法

View 裡面的 onSavedInstanceState和onRestoreInstanceState的作用?
View和Activity一樣的,每個View都有onSavedInstanceState和onRestoreInstanceState這兩個方法,可用於保存和恢復view的狀態。

在本章節中我們知道什麼是View?,View 坐標的基本概念,理解了View的生命周期,學習了如何自定義View?雖然全是理論知識總結,在後續我們會一起來自定義View的實戰學習。不管有沒有任何疑問,歡迎在下方留言吧。

 

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved