編輯:關於Android編程
什麼是ViewGroup?
在Android的樹狀結構圖中,ViewGroup類衍生出我們所熟悉的LinearLayout、RelativeLayout等布局:
簡單來說,ViewGroup其實就相當於所有布局的父親,所以我們可以通過自定義ViewGroup類實現千變萬化的布局。
public class FlowLayoutextends ViewGroup{ public FlowLayout(Context context) { // TODO Auto-generated constructor stub this(context,null); } public FlowLayout(Context context, AttributeSet attrs) { // TODO Auto-generated constructor stub this(context,attrs,0); } public FlowLayout(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); // TODO Auto-generated constructor stub } }
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { // TODO Auto-generated method stub measureChildren(widthMeasureSpec, heightMeasureSpec); super.onMeasure(widthMeasureSpec, heightMeasureSpec); }
對這個方法的使用見下文,此處記得添加measureChildren(widthMeasureSpec, heightMeasureSpec);表示由系統自己測量每個子View的大小
public static class FlowLayoutParams extends ViewGroup.MarginLayoutParams{ public FlowLayoutParams(Context context, AttributeSet attrs) { super(context, attrs); // TODO Auto-generated constructor stub } }
@Override protected void onLayout(boolean arg0, int left, int top, int right, int bottom) { // TODO Auto-generated method stub //獲得FlowLayout所測量出來的寬度 int mViewGroupWidth = getMeasuredWidth(); /** * paintX:繪制每個View時的光標起點的橫坐標 * paintY:繪制每個View時的光標起點的縱坐標 */ int paintX = left; int paintY = top; //用於記錄上一行的最大高度 int maxlineHeight = 0; int childCount = getChildCount(); for(int i=0; imViewGroupWidth){ //繪制的起點的橫坐標重新移回FlowLayout的橫坐標 paintX = left; //繪制的起點的縱坐標要向下移動一行的高度 paintY = paintY + maxlineHeight + params.topMargin + params.bottomMargin; maxlineHeight = 0; } maxlineHeight = Math.max(childViewHeight, maxlineHeight); childView.layout(paintX+params.leftMargin, paintY+params.topMargin, paintX+childViewWidth+params.leftMargin, paintY+childViewHeight+params.topMargin); //每繪制一次,起點光標就要向右移動一次 paintX = paintX + childViewWidth + params.leftMargin + params.rightMargin; } }
解析:在onLayout方法中,我們對每個子View進行遍歷並設置好它們應該在的位置,比如是LinearLayout的話,在onLayout中系統會規定它的子View只能按著橫向或者豎向排列下去,也就是說,onLayout裡子View的排布是按著我們自己的想法來決定,到底這個自定義布局會有怎樣的特性?
此處我們demo是自定義FlowLayout,也就是每一行都向右排列下去,直到這一行不夠容納,則子View自動換行,進入下一行,依此類推,從而實現流式布局,為了實現這樣的效果,最關鍵的應該是在換行的時候,需要實現讓我們的子View能夠判斷到底換不換行,代碼思路如下:
1.首先需要記錄FlowLayout的寬度, 作為每一行的寬度上限:
int mViewGroupWidth = getMeasuredWidth();
/** * paintX:繪制每個View時的光標起點的橫坐標 * paintY:繪制每個View時的光標起點的縱坐標 */ int paintX = left; int paintY = top;
FlowLayout.FlowLayoutParams params = (FlowLayout.FlowLayoutParams)childView.getLayoutParams();
int childViewWidth = childView.getMeasuredWidth(); int childViewHeight = childView.getMeasuredHeight();
//繪制的起點的橫坐標重新移回FlowLayout的橫坐標 paintX = left; //繪制的起點的縱坐標要向下移動一行的高度 paintY = paintY + maxlineHeight + params.topMargin + params.bottomMargin; //由於換行,所以當前行變成了下一行,最大高度自然也就置為當前這個新起行的子View的高度(因為它是下一行的第一個View) maxlineHeight = childViewHeight;
5.繪制當前子View,光標移動至下一個起點:
//每次都要計算當前行的最大高度 maxlineHeight = Math.max(childViewHeight, maxlineHeight); childView.layout(paintX+params.leftMargin, paintY+params.topMargin, paintX+childViewWidth+params.leftMargin, paintY+childViewHeight+params.topMargin); //每繪制一次,起點光標就要向右移動一次 paintX = paintX + childViewWidth + params.leftMargin + params.rightMargin;
運行:
可以看到達到了我們所要的效果,但這裡我們設置的FlowLayout的大小都是fill_parent,如果改為wrap_content會怎樣?
將FlowLayout的layout_height設置為wrap_content,雖然屏幕上仍然呈現的是一樣的效果,可是在預覽窗口中點擊FlowLayout,可以看到其實FlowLayout依舊是fill_parent的大小,而不是貼著子View的邊緣,如圖:
這不是我們想要的效果,正確的做法應該是:設置為wrap_content時,FlowLayout的邊界是緊緊貼著子View的邊緣的,所以我們應該修改onMeasure方法:
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { // TODO Auto-generated method stub //measureChildren(widthMeasureSpec, heightMeasureSpec); super.onMeasure(widthMeasureSpec, heightMeasureSpec); //得到FlowLayout的模式和寬高 int widthMode = MeasureSpec.getMode(widthMeasureSpec); int widthSize = MeasureSpec.getSize(widthMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); //記錄wrap_content下的最終寬度和高度 int width = 0; int height = 0; //記錄每一行的最大寬度和最大高度 int lineWidth = 0; int lineHeight = 0; int childCount = getChildCount(); for(int i=0;iwidthSize){ width = Math.max(lineWidth, childWidth); height = height + lineHeight; lineWidth = childWidth; lineHeight = childHeight; }else // 否則累加值lineWidth,lineHeight取最大高度 { lineWidth += childWidth; lineHeight = Math.max(lineHeight, childHeight); } //如果是最後一個子View if (i == childCount - 1){ width = Math.max(width, lineWidth); height += lineHeight; } } //根據模式來決定FlowLayout的大小,如果不是EXACTLY,則說明布局文件中設置的模式應該是wrap_content /** * 如果是wrap_content,則將FlowLayout寬度和高度設置為我們計算出來的最終寬高 * 如果是fill_parent或者具體數值,則將FlowLayout寬度和高度設置為一開始getMode和getSize得到的那個寬高 */ setMeasuredDimension((widthMode == MeasureSpec.EXACTLY) ? widthSize : width, (heightMode == MeasureSpec.EXACTLY) ? heightSize : height); }
再次查看預覽窗口:
最近公司沒事,研究了下多嵌套滾動組件的事件分發,雖然以前也接觸過,但都是拿網上的用,也是特別簡單的,正好朋友也需要,就研究了下。這個Demo也不是很完善,放上來也是讓各位
先把來源貼上http://zrgiu.com/blog/2011/01/making-your-android-app-look-better/http://www.di
360奇酷手機出自酷派和360合資公司,奇酷手機以及酷派手機都屬於安卓系統,那麼奇酷手機、酷派手機怎麼刷機呢?手機有毛病可以嘗試恢復手機出廠設置,操作方法如
首先,很榮幸此專欄能被CSDN推薦到主頁。榮幸的同時,也激勵自己會把這個專欄一直更新下去。進入今天的主題:我們在qq登錄的時候,會有一個下拉的按鈕,來查看歷史登錄賬號。這