autolayout教程Android,AndroidAutoLayout的简单阅读
前段時間hongyang大神發布了一個庫,AndroidAutoLayout。該庫的使用,是用戶(該庫的使用者,即,猿們)告訴app,設計圖的寬和高為多少像素,然后在UI布局里直接使用px作為單位,該庫會自動將填寫的px值轉換為屏幕的百分比值,以此來完成適配。
該庫的使用方式有兩種,一種是直接將Activity extends AutoLayoutActivity,另一種是在布局文件里使用該庫提供的三個代替LinearLayout, FrameLayout, RelativeLayout的AutoLinearLayout, AutoFrameLayout, AutoRelativeLayout控件.
如果是使用直接繼承AutoLayoutActivity的方法,這個相當省事。其實現是:在AutoLayoutActivity類內部重寫了onCreateView(),這個方法會在Activity的onCreate()之后調用,onCreateView調用完畢才調用Activity生命周期函數onStart().
這是它在Activity源碼中的說明,默認實現是返回null,它是LayoutInflater.Factory#onCreateView()的實現。
/**
* Standard implementation of
* {@link android.view.LayoutInflater.Factory#onCreateView} used when
* inflating with the LayoutInflater returned by {@link #getSystemService}.
* This implementation does nothing and is for
* pre-{@link android.os.Build.VERSION_CODES#HONEYCOMB} apps. Newer apps
* should use {@link #onCreateView(View, String, Context, AttributeSet)}.
*
* @see android.view.LayoutInflater#createView
* @see android.view.Window#getLayoutInflater
*/
@Nullable
public View onCreateView(String name, Context context, AttributeSet attrs) {
return null;
}
而Factory是LayoutInflater中的一個接口
public interface Factory {
/**
* Hook you can supply that is called when inflating from a LayoutInflater.
* You can use this to customize the tag names available in your XML
* layout files.
*
*
* Note that it is good practice to prefix these custom names with your
* package (i.e., com.coolcompany.apps) to avoid conflicts with system
* names.
*
* @param name Tag name to be inflated.
* @param context The context the view is being created in.
* @param attrs Inflation attributes as specified in XML file.
*
* @return View Newly created view. Return null for the default
* behavior.
*/
public View onCreateView(String name, Context context, AttributeSet attrs);
}
在這個方法傳進來的參數中的name是每一個View(放在布局文件中的View)的名字,比如LinearLayout, FrameLayout,TextView。
在Activity中onCreateView()會被調用多次,順序是LinearLayout , ViewStub , FrameLayout(這個就是ContentFrameLayout),之后就是我們寫在xml里的View了,所以在這里我們能拿到寫在xml文件里的控件名字。
LayoutInflater創建View的方法是createView(),其流程是:從xml中解析出來信息后,判斷是否帶包名,如果沒帶包名就給name拼接上前綴(也就是系統的包名,例如TextView變成android.widget.TextView),帶包名的(使用自定義View,擴展包View的時候要帶包名)就不需要加前綴。拿到了這個View的包名+類名,類加載器加載class,取得構造方法,將View new出來。
而如果onCreateView返回的不是null,而是一個View,就不會走createView()。
作者就是在AutoLayoutActivity中重寫了該方法,并判斷如果是LinearLayout,就返回該庫里的AutoLinearLayout。RelativeLayout,FrameLayout同上。如此便做到了偷梁換柱的效果,我們在xml里使用的是LinearLayout,但實際生成的AutoLinearLayout。
public View onCreateView(String name, Context context, AttributeSet attrs) {
Object view = null;
if(name.equals("FrameLayout")) {
view = new AutoFrameLayout(context, attrs);
}
if(name.equals("LinearLayout")) {
view = new AutoLinearLayout(context, attrs);
}
if(name.equals("RelativeLayout")) {
view = new AutoRelativeLayout(context, attrs);
}
return (View)(view != null?view:super.onCreateView(name, context, attrs));
}
而用戶不是選擇使用Activity extends AutoLayoutActivity這種方式,而是在xml文件里直接寫上包名+AutoXXXLayout,額。。。這里好像沒什么好說的。
接下來,我們來看該庫的自動布局類,這里分析AutoFrameLayout,其余兩個類似。
先說下背景,由于我們已經告訴app我們設計圖的大小了(該庫的使用條件之一,在Manifest.xml中添加meta,兩個字段design_width,design_height),程序就能拿到設計圖的寬高(單位為像素),而設備的屏幕寬高我們也是可以拿到的。當我們在布局文件里將View的屬性值設為像素值時,自然就可以通過換算得到實際上在不同的屏幕上該占多少百分比的像素了。以寬為例:
(edit_width / design_width)* screen_width 這個結果就是實際的像素值。edit_width是用戶填寫的px值。這部分代碼就不分析了。
既然已經把FrameLayout偷梁換柱成我們自己的View(AutoFrameLayout)了,那么就可以拿到布局文件時寫的各個值(包括自定義屬性,系統屬性)。
作者在AutoFrameLayout中主要的代碼是:
public AutoFrameLayout.LayoutParams generateLayoutParams(AttributeSet attrs) {
return new AutoFrameLayout.LayoutParams(this.getContext(), attrs);
}
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if(!this.isInEditMode()) {
this.mHelper.adjustChildren();
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
重寫generateLayoutParams(),讓該ViewGroup中的每一個View的LayoutParams變成該類的靜態內部類LayoutParams。
而在onMeasure()方法中借由AutoLayoutHelper來調整每一個子View的屬性(該庫支持9個屬性)。
而在AutoFrameLayout.LayoutParams類中會把該庫現有兩個自定義屬性(basewidth,baseheight),和9個支持的系統屬性的值拿出來,然后將2個自定義屬性和9個屬性值放在AutoFrameLayout.LayoutParams的成員變量AutoLayoutInfo中,并提供getter。
public static class LayoutParams extends android.widget.FrameLayout.LayoutParams implements AutoLayoutParams {
private AutoLayoutInfo mAutoLayoutInfo;
public LayoutParams(Context c, AttributeSet attrs) {
super(c, attrs);
this.mAutoLayoutInfo = AutoLayoutHelper.getAutoLayoutInfo(c, attrs);
}
//...這處代碼省略
public AutoLayoutInfo getAutoLayoutInfo() {
return this.mAutoLayoutInfo;
}
}
這里有一點需要提一下,在記錄過程中,只有屬性是以“px”結尾的屬性才會被記錄下來。也就是說,如果你在AutoFrameLayout中某個View某個屬性不想使用這個百分比自動布局的功能,只要不把屬性寫成px的就行了。(我在用這個庫的時候,有一個View的寬屬性無法事前確定,只能wrap_content。還在想該庫是不是可以提供一個自定義屬性ignore可以讓該View免于自動布局,看了代碼才知道作者早就設計好了)
下面是adjustChildren()的代碼:
public void adjustChildren() {
AutoLayoutConifg.getInstance().checkParams();
int i = 0;
for(int n = this.mHost.getChildCount(); i < n; ++i) {
View view = this.mHost.getChildAt(i);
LayoutParams params = view.getLayoutParams();
if(params instanceof AutoLayoutHelper.AutoLayoutParams) {
AutoLayoutInfo info = ((AutoLayoutHelper.AutoLayoutParams)params).getAutoLayoutInfo();
if(info != null) {
info.fillAttrs(view);
}
}
}
}
作者先做了一次檢查,checkParams(),如果用戶沒有在Manifest.xml中提供design_width,design_height會報錯。
之后就是拿到mHost(AutoXXXLayout)中的每一個View的LayoutParams,取出之前記錄了自定義屬性和屬性值的AutoLayoutInfo,然后將里面的每一個屬性作用到View上。這樣就完成了View屬性的更改。
該庫提供兩個自定義屬性layout_auto_basewidth, layout_auto_baseheight。由于手機屏幕的碎片化(Android手機你懂的),比如想讓一個ImageView的寬高一致時,寫寬20px,高20px,經過百分比換算屏幕大小,很可能就不一致了。所以需要一個單位基準,比如寬20px,高20px,layout_auto_basewidth=height,那么高度就會以width為基準了,其換算公式是(edit_height/design_width) * screen_width。如此,這個ImageView的寬高就一樣大小了,正方形。
AutoAttr這個類作者用來封裝每一個屬性值的信息,一般就是多少px,是哪個方向(寬,高)為基準。如果我們在xml里沒有指定某屬性的基準,就會使用該屬性的默認基準。默認基準是,豎直方向上以高為基準,水平方向以寬為基準。即:marginTop以height為基準,marginLeft以width為基準,textSize以height為基準,其他的同上。
AutoAttr的apply()方法代碼如下:
public void apply(View view) {
boolean log = view.getTag() != null && view.getTag().toString().equals("auto");
if(log) {
L.e(" pxVal = " + this.pxVal + " ," + this.getClass().getSimpleName());
}
int val;
if(this.useDefault()) {
val = this.defaultBaseWidth()?this.getPercentWidthSize():this.getPercentHeightSize();
if(log) {
L.e(" useDefault val= " + val);
}
} else if(this.baseWidth()) {
val = this.getPercentWidthSize();
if(log) {
L.e(" baseWidth val= " + val);
}
} else {
val = this.getPercentHeightSize();
if(log) {
L.e(" baseHeight val= " + val);
}
}
val = Math.max(val, 1);
this.execute(view, val);
}
以上是我對AndroidAutoLayout的理解,如果錯了,還請指正,謝謝。
總結
以上是生活随笔為你收集整理的autolayout教程Android,AndroidAutoLayout的简单阅读的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: idea启动tomcat时蓝屏
- 下一篇: axure 破解 key