一、關於layout_margin
搞Android時間也不短了,對layout_margin也不陌生了,可近期遇到一個問題讓我發現,對它的認識還不夠深入全面。大量網絡資料上都說,layout_margin指view距離父view的距離。這個說法不夠嚴謹,正確的說法是,距離view的相對view的距離才更准確。
在Linearlayout下。能夠覺得是距離父view的距離。但在RelativeLayout下則不然,假設view A已經寫定在view B的右側,則view A的layout_marginLeft就是距離view B的距離,與父View無關。
另外。這個距離到底是兩個view中心的距離還是相鄰兩邊的距離?能夠這樣理解,每一個view都是一個矩形區域。假設view A已經設定在view B的右側。則layout_marginLeft是view B的右邊 和view A的左邊之間的距離,而非兩個view中心之間的距離。示意圖例如以下:
二、關於自己定義復合控件layout()無效的問題
如上圖所看到的。我將一個ImageView和一個TextView組合在一起作為一個自己定義控件。然后將四個這種自己定義控件放在底部的一個RelativeLayout里,兩邊的view,即“消息”和“設置”能夠通過設置RelativeLayout的左右padding來控制位置。可是第二個view和第三個View則須要自己來調整。
底部的這個RelativeLayout代碼例如以下:
<?xml version="1.0" encoding="utf-8"?>
<org.yanzi.ui.BottomControlPanel xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="60dp"
android:layout_alignParentBottom="true"
android:gravity="center_vertical"
android:paddingLeft="20dp"
android:paddingRight="20dp" >
<org.yanzi.ui.ImageText
android:id="@+id/btn_message"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true" />
<org.yanzi.ui.ImageText
android:id="@+id/btn_contacts"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="@id/btn_message" />
<org.yanzi.ui.ImageText
android:id="@+id/btn_news"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="@id/btn_contacts" />
<org.yanzi.ui.ImageText
android:id="@+id/btn_setting"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true" />
</org.yanzi.ui.BottomControlPanel>
這里我將第二個view設置在第一個view的右側。第三個view在第二個的右側。這個BottomControlPanel.java也是自己定義的,繼承自RelativeLayout。
為了讓四個view等間距分布,本能的想到重寫BottomControlPanel.java的onLayout()函數。注意這里。第二個view“聯系人”的寬度要寬於其它3個view。
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
// TODO Auto-generated method stub
super.onLayout(changed, left, top, right, bottom);
layoutItems(left, top, right, bottom);
// for(int i = 0; i< n; i++){
// ImageText v = viewList.get(i); //getChildAt(i);
// v.setVisibility(View.VISIBLE);
// int vTop = top;
// int vLeft = paddingLeft + i * (blankV + viewW);
// int vBottom = bottom;
// int vRight = vLeft + viewW;
// (v).layout(vLeft, vTop, vRight, vBottom);
// Log.i("yanzi"," vTop = " + vTop + " vLeft = " + vLeft + " vBottom = " + vBottom + " vRight = " + vRight);
// }
}
/**最左邊和最右邊的view由母布局的padding進行控制位置。這里需對第2、3個view的位置又一次設置
* @param left
* @param top
* @param right
* @param bottom
*/
private void layoutItems(int left, int top, int right, int bottom){
int n = getChildCount();
if(n == 0){
return;
}
int paddingLeft = getPaddingLeft();
int paddingRight = getPaddingRight();
Log.i("yanzi", "paddingLeft = " + paddingLeft + " paddingRight = " + paddingRight);
int width = right - left;
int height = bottom - top;
Log.i("yanzi", "width = " + width + " height = " + height);
int allViewWidth = 0;
for(int i = 0; i< n; i++){
View v = getChildAt(i);
Log.i("yanguoqi", "v.getWidth() = " + v.getWidth());
allViewWidth += v.getWidth();
}
int blankWidth = (width - allViewWidth - paddingLeft - paddingRight) / (n - 1);
Log.i("yanzi", "blankV = " + blankWidth );
LayoutParams params1 = (LayoutParams) viewList.get(1).getLayoutParams();
params1.leftMargin = blankWidth;
viewList.get(1).setLayoutParams(params1);
LayoutParams params2 = (LayoutParams) viewList.get(2).getLayoutParams();
params2.leftMargin = blankWidth;
viewList.get(2).setLayoutParams(params2);
}
最初我是使用onLayout()函數里凝視掉的這一段來控制的:
// for(int i = 0; i< n; i++){
// ImageText v = viewList.get(i); //getChildAt(i);
// v.setVisibility(View.VISIBLE);
// int vTop = top;
// int vLeft = paddingLeft + i * (blankV + viewW);
// int vBottom = bottom;
// int vRight = vLeft + viewW;
// (v).layout(vLeft, vTop, vRight, vBottom);
// Log.i("yanzi"," vTop = " + vTop + " vLeft = " + vLeft + " vBottom = " + vBottom + " vRight = " + vRight);
// }
非常顯然,一個view的繪制過程分為:onMeasure 進行測量大小,onLayout計算放的位置。onDraw進行繪制。可是在這里。layout()這個函數死活不起作用了。
我之前使用layout()時都是好好的,注意使用layout()時將上面代碼里的super.onLayout(changed, left, top, right, bottom);凝視掉。本來帶super()的時候運行的是默認流程,view還能夠畫出來。把這個super去掉,換成我自己計算的坐標layout()上。結果一個view都繪制不出來。
可的的確確。對單一布局,如將這里的ImageText換成ImageView則是能夠正常放置的,為此我又重寫了ImageText的onlayout()函數,仍然是什么都畫不出來。
通過onLayout()打印發現,在顯示過程中共進入這里兩次。第一次的時候view的width和height都是0,應該是還沒顯示出來。第二次時能得到width和height。(這塊描寫敘述不准確,下來還要再細致研究下)
最后為了解決這個問題。干脆就讓它super.onLayout()。在他以下加上自己寫的layoutItems()函數。通過計算leftMargin來確定位置。於是有了上面第一個問題關於margin的精確理解。最后問題完美解決,橫屏情況下也能正常放置:
最后補充一點,一個view假設設置了padding,那么view.getWidth()得到的長度是包括padding的。layoutItems()的過程是獲得整個父view的寬度。這個寬度包括最左邊和最右邊的兩個padding。然后計算allViewWidth,即四個view的總寬度。兩者相減除以3得到這3個空檔的距離。然后把參數設下去就ok了。
待我把源代碼梳理好再上傳。