編輯:關於Android編程
分類
自定義Layout可以分為兩種情況。
流程
自定義View的流程是:onMeasure()->onLayout()->onDraw()。自定義ViewGroup的時候一般是不要去實現onDraw的,當然也可能有特殊的需求,比如:CoordinatorLayout。
所以onMeasure和onLayout基本能做大部分我們接觸的ViewGroup。但是僅僅的知道在onMeasure中測量ViewGroup的大小以及在onLayout中計算Child View的位置還是不夠。
比如:怎麼可以給ViewGroup的Child View設置屬性?
一個例子。
寫一個自定義的ViewGroup,增加一個屬性控制Child View的大小(長寬)占ViewGroup的比例。
假設是一個LinearLayout,那麼就先定義一個CustomLinearLayout。
public class CustomLinearLayout extends LinearLayout {
public CustomLinearLayout(Context context) {
super(context);
}
public CustomLinearLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CustomLinearLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public CustomLinearLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
}
其它拋開不說,我們是需要增加屬性用來控制Child View的大小。那麼就先在value/attr.xml中定義那個屬性(使用CustomLinearLayout_Layout來與CustomLinearLayout區分下,當然這個名字是隨意的)。
<declare-styleable name="CustomLinearLayout_Layout"> <!-- 定義比例 --> <attr name="inner_percent" format="float"/> </declare-styleable>
ViewGroup調用addView()的時候最終都會調用到這個方法。
public void addView(View child, int index, LayoutParams params)
這個params代表的就是View的配置,ViewGroup.LayoutParams中就包含了width、height,LinearLayout.LayoutParams增加了weight屬性等等。那麼我們就應該實現一個LayoutParams。那麼現在就是這樣了。
public class CustomLinearLayout extends LinearLayout {
public CustomLinearLayout(Context context) {
super(context);
}
public CustomLinearLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CustomLinearLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public CustomLinearLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
public static class LayoutParams extends LinearLayout.LayoutParams {
private float innerPercent;
private static final int DEFAULT_WIDTH = WRAP_CONTENT;
private static final int DEFAULT_HEIGHT = WRAP_CONTENT;
public LayoutParams() {
super(DEFAULT_WIDTH, DEFAULT_HEIGHT);
innerPercent = -1.0f;
}
public LayoutParams(float innerPercent) {
super(DEFAULT_WIDTH, DEFAULT_HEIGHT);
this.innerPercent = innerPercent;
}
public LayoutParams(ViewGroup.LayoutParams p) {
super(p);
}
@TargetApi(Build.VERSION_CODES.KITKAT)
public LayoutParams(LinearLayout.LayoutParams source) {
super(source);
}
@TargetApi(Build.VERSION_CODES.KITKAT)
public LayoutParams(LayoutParams source) {
super(source);
this.innerPercent = source.innerPercent;
}
public LayoutParams(Context c, AttributeSet attrs) {
super(c, attrs);
init(c, attrs);
}
private void init(Context context, AttributeSet attrs) {
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CustomLinearLayout_Layout);
innerPercent = a.getFloat(R.styleable.CustomLinearLayout_Layout_inner_percent, -1.0f);
a.recycle();
}
}
}
現在就可以在xml使用我們的屬性了。
<?xml version="1.0" encoding="utf-8"?>
<com.egos.samples.custom_layout.CustomLinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="200dp"
android:layout_height="200dp"
android:id="@+id/test_layout"
android:background="#ffff0000"
android:gravity="center"
android:orientation="vertical">
<ImageView
android:text="Egos"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:onClick="add"
android:background="#ff00ff00"
app:inner_percent="0.8"/>
</com.egos.samples.custom_layout.CustomLinearLayout>
只是然並軟,並沒有作用。
那麼到底是怎麼去控制Child View的大小呢?當然是在onMeasure控制的。addView會執行下面的代碼。
requestLayout(); invalidate(true);
這樣的話就會重新的走一遍onMeasure(),onLayout()了。實現onMeasure()的方法以後直接去處理Child View的大小,因為我繼承的是LinearLayout,所以其實是會處理到measureChildBeforeLayout()。最終是在measureChildBeforeLayout的時候來處理Child View的大小。
@Override
protected void measureChildWithMargins(View child, int parentWidthMeasureSpec,
int widthUsed, int parentHeightMeasureSpec, int heightUsed) {
// 在xml強制寫成match_parent,然後在這裡強制設置成
if (child != null && child.getLayoutParams() instanceof LayoutParams &&
((LayoutParams) child.getLayoutParams()).innerPercent != -1.0f) {
parentWidthMeasureSpec = MeasureSpec.makeMeasureSpec((int) (MeasureSpec.getSize(parentWidthMeasureSpec) *
((LayoutParams) child.getLayoutParams()).innerPercent), MeasureSpec.getMode(parentWidthMeasureSpec));
parentHeightMeasureSpec = MeasureSpec.makeMeasureSpec((int) (MeasureSpec.getSize(parentHeightMeasureSpec) *
((LayoutParams) child.getLayoutParams()).innerPercent), MeasureSpec.getMode(parentHeightMeasureSpec));
super.measureChildWithMargins(child, parentWidthMeasureSpec, widthUsed,
parentHeightMeasureSpec, heightUsed);
} else {
super.measureChildWithMargins(child, parentWidthMeasureSpec, widthUsed,
parentHeightMeasureSpec, heightUsed);
}
}
這樣就可以實現最開始的需求了。

其實還有一些細節是需要處理的,下面的代碼就是。
/**
* 當checkLayoutParams返回false的時候就會執行到這裡的generateLayoutParams
*/
@Override
protected LinearLayout.LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) {
return super.generateLayoutParams(lp);
}
/**
* 當addView的時候沒有設置LayoutParams的話就會默認執行這裡的generateDefaultLayoutParams
*/
@Override
protected LayoutParams generateDefaultLayoutParams() {
return new LayoutParams();
}
/**
* 寫在xml中屬性的時候就會執行這裡的generateLayoutParams
*/
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
return new LayoutParams(getContext(), attrs);
}
總結一下
做Android開發,自身需要自定義View的地方確實是比較的多,只是大部分都會有相應的開源庫。但是我們還是應該需要熟練的知道該如何自定義一個ViewGroup。
自己是一個比較健忘的人,所以寫的詳細點。
完整的代碼戳這裡。
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持本站。
Android--AIDL學習
1、AIDL:Android Interface Definition Language,即Android接口定義語言。 Android
Android基礎筆記
Android下訪問網絡資源和一些注意事項 Android下異步消息處理線程技術 Android下異步消息處理線程技術的基本原理 模仿新聞客戶端小案例 GET方式提交數據
Android網絡請求庫 - Say hello to retrofit
之前對Android裡常用的網絡請求庫OkHttp與Volley做了簡單的學習歸納與總結,這裡看這個系列中的最後一篇,來認識一下Retrofit。Retrofit可以認為
Android中Canvas的常用方法總結
一、對Canvas進行操作對Canvas的一系列操作,是指對Canvas進行旋轉、平移、縮放等操作。這些操作可以讓Canvas對象使用起來更加便捷。二、Canvas平移/