Android 百分比布局揭秘

这是一个百分比布局实例,github 地址是   android-percent-support-lib-sample

下面是自己参考其代码,实现的一个简易百分比布局,没有什么实际作用,只是在理解google官方如何实现。


步骤如下(下面源码版本是2.3):

1、任意继承一个容器如LinearLayout,ReleativeLayout或FrameLayout。

2、重写generateLayoutParams方法,布局被解析时会被调用,需要返回一个LayoutParams对象。

话说此方法是在LayoutInflater类的inflate方法中调用,也就是通过setContentView传进去的布局,最终调用PhoneWindow的installDecor方法,继而调用LayoutInflater类的inflate方法。

private LayoutInflater mLayoutInflater;
private void installDecor() {
	if (mDecor == null) {
		mDecor = generateDecor();
		mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
		mDecor.setIsRootNamespace(true);
	}
	if (mContentParent == null) {
		mContentParent = generateLayout(mDecor);
	}
}
	
protected ViewGroup generateLayout(DecorView decor) {
	int layoutResource;
	View in = mLayoutInflater.inflate(layoutResource, null);
	decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
	//id为content的view
	ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
	return contentParent;
}
public View inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot) {
        synchronized (mConstructorArgs) {
            View result = root;
            try {
                int type;
                final String name = parser.getName();

                if (TAG_MERGE.equals(name)) {
                    if (root == null || !attachToRoot) {
                        throw new InflateException("<merge /> can be used only with a valid " + "ViewGroup root and attachToRoot=true");
                    }
                    rInflate(parser, root, attrs, false);
                } else {
                    
                    View temp;
                    if (TAG_1995.equals(name)) {
                        temp = new BlinkLayout(mContext, attrs);
                    } else {
                        temp = createViewFromTag(root, name, attrs);
                    }

                    ViewGroup.LayoutParams params = null;

                    if (root != null) {
                       
                        // Create layout params that match root, if supplied
                        params = root.generateLayoutParams(attrs);
                        if (!attachToRoot) {
                            temp.setLayoutParams(params);
                        }
                    }
                    // Inflate all children under temp
                    rInflate(parser, temp, attrs, true);
                    
                    // We are supposed to attach all the views we found (int temp)to root. Do that now.
                    if (root != null && attachToRoot) {
                        root.addView(temp, params);
                    }

                    if (root == null || !attachToRoot) {
                        result = temp;
                    }
                }
             
            } finally {
            }
            return result;
        }
    }


3、自定义LayoutParams类继承自LinearLayout.LayoutParams,通过AttributeSet类获取布局里面定义的属性值并解析。

 <declare-styleable name="ParcentLayout">
        <attr name="parcentWidth" format="float" />
        <attr name="parcentHeight" format="float" />
        <attr name="parcentTextSize" format="string" />
    </declare-styleable>


4、重写onMeasure计算布局的尺寸,循环view获取自定义的LayoutParams对象,重新给view设置高度和宽度。

5、如果是TextView,适配字体大小。


public class PercentLinearLayout extends LinearLayout {
	private static final String REGEX_PERCENT = "^(([0-9]+)([.]([0-9]+))?|([.]([0-9]+))?)%([s]?[wh]?)$";
	
	public PercentLinearLayout(Context context) {
		super(context);
	}

	public PercentLinearLayout(Context context, AttributeSet attrs) {
		super(context, attrs);
	}

	private static final String TAG = "PercentLinearLayout";

	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		
		int heightSize = View.MeasureSpec.getSize(heightMeasureSpec);
		int heightMode = View.MeasureSpec.getMode(heightMeasureSpec);
		int tmpHeightMeasureSpec = MeasureSpec.makeMeasureSpec(heightSize,
				heightMode);

		int widthSize = View.MeasureSpec.getSize(widthMeasureSpec);
		int widthMode = View.MeasureSpec.getMode(widthMeasureSpec);
		int tmpWidthMeasureSpec = MeasureSpec.makeMeasureSpec(widthSize,
				widthMode);
		
		// 处理ScrollView
		if (heightMode == MeasureSpec.UNSPECIFIED && getParent() != null
				&& (getParent() instanceof ScrollView)) {
			int baseHeight = 0;
			Context context = getContext();
			if (context instanceof Activity) {
				Activity act = (Activity) context;
				//获取 PhoneWindow类中DecorView(继承FrameLayout)的根View的高度
				int measuredHeight = act.findViewById(android.R.id.content).getMeasuredHeight();
				baseHeight = measuredHeight;
			} else {
				baseHeight = getScreenHeight();
			}
			tmpHeightMeasureSpec = MeasureSpec.makeMeasureSpec(baseHeight,heightMode);
		}
		
		int width = View.MeasureSpec.getSize(tmpWidthMeasureSpec);
		int height = View.MeasureSpec.getSize(tmpHeightMeasureSpec);
		 
		Log.d(TAG, "width = " + width + " , height = " + height);
		//获取子View并设置其宽度和高度
		int childCount = getChildCount();
		for (int i = 0; i < childCount; i++) {
			View view = getChildAt(i);
			LayoutParams params = (LayoutParams) view.getLayoutParams();
			float widthp = 0;
			float heightp = 0;
			if (params instanceof PercentLinearLayout.LayoutParams) {
				widthp = params.w;
				heightp = params.h;
			}
			if (widthp != 0) {
				params.width = (int) (width * widthp);
			}
			if (heightp != 0) {
				params.height = (int) (height * heightp);
			}
			
			//设置文字大小
			if (params.hasParcentTextSize) {
				supportTextSize(width,height,view,params.parcentTextSize);
			}

		}
		 
		super.onMeasure(widthMeasureSpec, heightMeasureSpec);
	}

	/**
	 * 该方法在LayoutInflater类的inflate方法中调用
	 */
	@Override
	public android.widget.LinearLayout.LayoutParams generateLayoutParams(
			AttributeSet attrs) {
		Log.i(TAG, "generateLayoutParams method execute ... ");
		return new PercentLinearLayout.LayoutParams(getContext(), attrs);
	}

	public static class LayoutParams extends LinearLayout.LayoutParams {
		public float h;
		public float w;
		public String parcentTextSize ;
		public boolean hasParcentTextSize=false;

		public LayoutParams(Context context, AttributeSet attrs) {
			super(context, attrs);
			TypedArray a = context.obtainStyledAttributes(attrs,
					R.styleable.ParcentLayout);

			w = a.getFloat(R.styleable.ParcentLayout_parcentWidth, -1);
			h = a.getFloat(R.styleable.ParcentLayout_parcentHeight, -1);
			parcentTextSize = a.getString(R.styleable.ParcentLayout_parcentTextSize);
			if (w < 0) {
				w = 0;
			}
			if (w > 1) {
				w = 1;
			}
			h = h < 0 ? 0 : h;
			h = h > 1 ? 1 : h;
			
			if (!TextUtils.isEmpty(parcentTextSize)) {
				hasParcentTextSize = true;
			}
			a.recycle();
		}

	}

	/**
	 * 设置文字大小
	 * @param widthHint 宽度
	 * @param heightHint 高度
	 * @param view
	 * @param percentStr 百分比字符串(20%w)
	 */
	private void supportTextSize(int widthHint, int heightHint, View view,
			String percentStr) {
		Pattern p = Pattern.compile(REGEX_PERCENT);
		Matcher matcher = p.matcher(percentStr);
		if (!matcher.matches()) {
			throw new RuntimeException(
					"the value of layout_xxxPercent invalid! ==>" + percentStr);
		}
		int len = percentStr.length();
		// extract the float value
		String floatVal = matcher.group(1);

		float percent = Float.parseFloat(floatVal) / 100f;
		
		int base = 0;
		if (percentStr.endsWith("w")) {
			base = view.getMeasuredWidth();
		} else if (percentStr.endsWith("h")) {
			base = view.getMeasuredHeight();
		}
		
		

		float textSize = (int) (base * percent);

		System.out.println("ss "+textSize);
		
		// Button 和 EditText 是TextView的子类
		if (view instanceof TextView) {
			((TextView) view).setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize);
		}
	}
	
	
	
	private int getScreenHeight() {
		WindowManager wm = (WindowManager) getContext().getSystemService(
				Context.WINDOW_SERVICE);
		DisplayMetrics outMetrics = new DisplayMetrics();
		wm.getDefaultDisplay().getMetrics(outMetrics);
		return outMetrics.heightPixels;
	}

}

<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#ffffff" >

    <com.example.zzl.viewgroup.PercentLinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical" >

        <TextView
            android:layout_width="0dp"
            android:layout_height="0dp"
            android:background="#ff44aacc"
            android:gravity="center"
            android:text="width:55%,height:5%,ts:60%h"
            android:textColor="#ffffff"
            app:parcentHeight="0.05"
            app:parcentTextSize="60%h"
            app:parcentWidth="0.55" />

        <TextView
            android:layout_width="0dp"
            android:layout_height="0dp"
            android:layout_marginTop="20dip"
            android:background="#ff99aacc"
            android:gravity="center"
            android:text="width:75%,height:20%,ts:40%h"
            android:textColor="#ffffff"
            app:parcentHeight="0.1"
            app:parcentTextSize="40%h"
            app:parcentWidth="0.75" />

        <TextView
            android:layout_width="0dp"
            android:layout_height="0dp"
            android:layout_marginTop="20dip"
            android:background="#ff4455cc"
            android:gravity="center"
            android:text="width:90%,height:30%,ts:20%h"
            android:textColor="#ffffff"
            app:parcentHeight="0.2"
            app:parcentTextSize="20%h"
            app:parcentWidth="0.9" />

        <TextView
            android:layout_width="0dp"
            android:layout_height="0dp"
            android:layout_marginTop="20dip"
            android:background="#ff44aa77"
            android:gravity="center"
            android:text="width:100%,height:40%,ts:10%h"
            android:textColor="#ffffff"
            app:parcentHeight="0.4"
            app:parcentTextSize="10%h"
            app:parcentWidth="1" />
    </com.example.zzl.viewgroup.PercentLinearLayout>

</ScrollView>


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值