天天看點

<50 android hacks>中的卡牌問題---自定義ViewGroup

學安卓的時間并不算短,但是一直都沒有認認真真的看過,前段時間看見<50 android hacks>,覺得這本書寫的真的不錯,國内也有中文版.

&lt;50 android hacks&gt;中的卡牌問題---自定義ViewGroup

要求顯示上面的效果,通常我就會用RelativeLayout和layout_margin*來實作

In this hack, we’ll look at another way of creating

the same type of layout—we’ll create a custom View-

Group . The benefits of using a custom ViewGroup

instead of adding margins by hand in an XML file are

these:

A.It’s easier to maintain if you’re using it in different activities.

B. You can use custom attributes to customize the position of the ViewGroup children.

C. The XML will be easier to understand because it’ll be more concise.

D. If you need to change the margins, you won’t need to recalculate by hand every child’s margin.

下面用ViewGroup實作:

java:

package com.example.lock;
import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
/**
 * 
 * @author kutear
 *
 */


public class ExampleView extends ViewGroup{
    private int horizontal_spacing = 0; 
    private int vertical_spacing = 0; 
	TypedArray t = null;
	public ExampleView(Context context, AttributeSet attrs) {
		super(context, attrs);
		// TODO Auto-generated constructor stub
	//這裡是取得< ExampleView />的自定義屬性
	 t = context.obtainStyledAttributes(attrs, com.example.lock.R.styleable.ExampleView);
	 horizontal_spacing = t.getDimensionPixelSize(com.example.lock.R.styleable.ExampleView_horizontal_spacing, 20);
	 vertical_spacing = t.getDimensionPixelSize(com.example.lock.R.styleable.ExampleView_vertical_spacing, 30);
	 int marginleft = t.getDimensionPixelSize(com.example.lock.R.styleable.ExampleView_layout_marginleft,10);
	 Log.v("XML-height",""+ horizontal_spacing);
	 Log.v("XML-width",""+ vertical_spacing);
	 Log.v("XML-MarginLeft",""+ marginleft);
	 t.recycle();
	}
   
	/**
	 * 用來計算Parent和Child的尺寸
	 */
	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		// TODO Auto-generated method stub
		
		int width = MeasureSpec.getSize(widthMeasureSpec); //if fill_parent,ViewGroup的寬為該值..
		int height = MeasureSpec.getSize(heightMeasureSpec);//同上
		
		Log.v("width", "width:"+width);
		Log.v("height", "height:"+height);
		int modeWidth = MeasureSpec.getMode(widthMeasureSpec);
		int modeHeight = MeasureSpec.getMode(heightMeasureSpec);
		Log.v("Mode--H:", ""+modeHeight);
		Log.v("Mode--W:", ""+modeWidth);
		Log.v("AT_MOST:",""+MeasureSpec.AT_MOST);
		Log.v("EXACTLY:",""+MeasureSpec.EXACTLY);
		Log.v("UNSPECIFIED:",""+MeasureSpec.UNSPECIFIED);
		int childHeight = 0; //Child的總高度....用來确定ViewGroup是wrap_content時的高度
		int childWidth = 0;  //同上
		int count = getChildCount();
		
		for(int i=0;i<count;i++){
			View child = getChildAt(i);
			//This will display 0
			Log.v("before-Child-size",""+ child.getMeasuredHeight());
			//計算Child的大小
			measureChild(child,widthMeasureSpec,heightMeasureSpec);
			//This will display ready size
			Log.v("after-Child-Size",""+ child.getMeasuredHeight());
			ExampleView.LayoutParams lp = (ExampleView.LayoutParams) child.getLayoutParams();
			int cHeight = child.getMeasuredHeight();
			int cWidth = child.getMeasuredWidth();
			if (i==0) {
				childHeight += cHeight;
				childWidth += cWidth+lp.marginleft;
			}else {
				childHeight += vertical_spacing;
				childWidth +=horizontal_spacing+lp.marginleft;
			}
		}
		Log.v("Wrap_content:", childWidth+"---"+childHeight);
		
		setMeasuredDimension(MeasureSpec.AT_MOST==modeWidth?childWidth:width,//warp_content時為前者
				MeasureSpec.AT_MOST==modeHeight?childHeight:height); //parent's Size, fill_parent or wrap_content
	}
	
	
	/**
	 * 繪制Child的位置
	 */
	@Override
	protected void onLayout(boolean changed, int l, int t, int r, int b) {
		/**
		 * 參數表示:l-->left  ViewGroup的左邊的坐标
		 *         其他的類似
		 *   ViewGroup在整個螢幕中的位置....
		 *   不是ViewGroup的相對位置...
		 *   
		 *   
		 */
		
		// TODO Auto-generated method stub
		 int WT_S=0; //下一View的開始位置 
		 //int WT_E=0; //下一VIew的結束位置
		 int childCount = getChildCount();   
		 Log.v("-----", "L="+l+"--T="+t+"--R="+r+"--B="+b);
		    for (int i = 0; i < childCount; i++) {   
		        View childView = getChildAt(i);   
		        // 擷取在onMeasure中計算的視圖尺寸   
		        int measureHeight = childView.getMeasuredHeight();   
		        int measuredWidth = childView.getMeasuredWidth();   
		        Log.v("Child-Height", measureHeight+"");
		        Log.v("Child-Width", measuredWidth+"");
		        ExampleView.LayoutParams lp = (ExampleView.LayoutParams) childView.getLayoutParams();
		        Log.v("leftMargin",""+lp.marginleft);
		        Log.v("leftMargin",""+lp.rightMargin);
		        WT_S += (i==0?0:horizontal_spacing)+lp.marginleft;
		        Log.v("WT_S", WT_S+"");
		        //這裡的四個方向的參數位相對與ViewGroup的位置,,ViewGroup的左上角位(0,0)
		        childView.layout(WT_S, vertical_spacing*i,
		        		WT_S+measuredWidth, vertical_spacing*i+measureHeight);
		        
		        
		    }   
	}
	
	@Override
    protected android.view.ViewGroup.LayoutParams generateDefaultLayoutParams() {
        return new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.MATCH_PARENT);
    }
 
    @Override
    public android.view.ViewGroup.LayoutParams generateLayoutParams(
            AttributeSet attrs) {
        return new LayoutParams(getContext(), attrs);
    }
 
    @Override
    protected android.view.ViewGroup.LayoutParams generateLayoutParams(
            android.view.ViewGroup.LayoutParams p) {
        return new LayoutParams(p);
    }
	
	
    public static class LayoutParams extends MarginLayoutParams {
        private int horizontal_spacing = 0; 
        private int vertical_spacing = 0; 
        public int marginleft = -1;
        public LayoutParams(Context c, AttributeSet attrs) {
            super(c, attrs);
 
            TypedArray ta = c.obtainStyledAttributes(attrs,
                    R.styleable.ExampleView);
           //此處是取得ViewGroup中的Child中的自定義屬性
            horizontal_spacing = ta.getInt(R.styleable.ExampleView_horizontal_spacing, -1);
            vertical_spacing = ta.getInt(R.styleable.ExampleView_vertical_spacing, -1);
            marginleft = ta.getDimensionPixelSize(R.styleable.ExampleView_layout_marginleft, 0);
            int paddingleft = ta.getIndex(R.styleable.View_paddingEnd);
            Log.v("Padding",""+paddingleft); 
            Log.v("marginleft",""+marginleft); 
            ta.recycle();
        }
 
        public LayoutParams(int width, int height) {
            super(width, height);
        }
 
 
        public LayoutParams(android.view.ViewGroup.LayoutParams source) {
            super(source);
        }
 
        public LayoutParams(MarginLayoutParams source) {
            super(source);
        }
    }

}
           

xml:

attrs.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>

    <declare-styleable name="ExampleView">
        <attr name="horizontal_spacing" format="dimension" />
        <attr name="vertical_spacing" format="dimension" />
        <attr name="layout_marginleft" format="dimension"/>
    </declare-styleable>

</resources>
           

test.aml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:cascade="http://schemas.android.com/apk/res/com.example.lock"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >
    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="MMMMMM" >
    </TextView>


    <com.example.lock.ExampleView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        cascade:layout_marginleft="20dp"
        android:background="#abcdef"
        cascade:horizontal_spacing="30dp"
        cascade:vertical_spacing="30dp" >

        <TextView
            android:layout_width="100dp"
            android:layout_height="150dp"
            android:background="#FF0000"
            android:text="AAA" />

        <TextView
            android:layout_width="100dp"
            android:layout_height="150dp"
            cascade:layout_marginleft="20dp"
            android:background="#0000FF"
            android:text="BBB" />

        <TextView
            android:layout_width="100dp"
            android:layout_height="150dp"
            android:background="#00FF00"
            android:text="CCC" />
    </com.example.lock.ExampleView>

</LinearLayout>