百分比佈局,android自定義View。

       百分比佈局


  分析一下:只要子View在xml文件指定percentX percentY屬性和並分配屬性值給他們,父容器就可以算出座標給子View佈局, 這種佈局對座標很精確,而且不涉及適配問題,不會像絕對佈局那樣因爲適配問被拋棄掉了。但是絕對佈局有個問題,就是必須指定佈局的大小,也就是說在寫佈局的時候指定android:layout_width/height 屬性的時候,屬性值必須是明確的 如:**dp,MATH_PARENT ,爲什麼呢, 這是代碼設計有關哦,詳細請看下面.

<pre name="code" class="java" style="font-size: 13.3333px;"><span style="font-size: 18px;">package com.example.just_android_percent_layout_custom;

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;

public class PercentLayout  extends ViewGroup{

	private int mScreenWidth;
	private int mScreenHeight;

	public PercentLayout(Context context) {
		this(context,null);
	}

	public PercentLayout(Context context, AttributeSet attrs) {
		this(context, attrs,0);
	}

	public PercentLayout(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
	
	 init(context);
	}
 
	private void init(Context context) {
		mScreenWidth = context.getResources().getDisplayMetrics().widthPixels;
		mScreenHeight = context.getResources().getDisplayMetrics().heightPixels;
	}

	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
     	super.onMeasure(widthMeasureSpec, heightMeasureSpec);
		
		
		int widthMode=MeasureSpec.getMode(widthMeasureSpec); 
		int heightMode=MeasureSpec.getMode(heightMeasureSpec);
		
		int resultWidth=0;
		int resultHeight=0;
		
		int parentWidth=MeasureSpec.getSize(widthMeasureSpec);
		int parentHeight=MeasureSpec.getSize(heightMeasureSpec);
		
		int widthMeasured=0;
		int heightMeasured=0;
		
		float childPercentX=0;
		float childPercentY=0;
		if(widthMode==MeasureSpec.AT_MOST||MeasureSpec.UNSPECIFIED==widthMode){
			
			throw new RuntimeException("PercentLayout layout_width android layout_height must more than 0 dp");
//			/**
//			 * 測量子view
//			 */
//			int childCount=this.getChildCount();
//			for(int i=0;i<childCount;i++){
//				View child=this.getChildAt(i);
//				if(child.getVisibility()==View.GONE){
//					continue;
//				}
//				PercentLayoutParams mPercentLayoutParams=(PercentLayoutParams) child.getLayoutParams();
//				if(mPercentLayoutParams==null)
//					continue;
//				int childWidth=mPercentLayoutParams.width;
//				if(childWidth>0){
//					
//					widthMeasured=MeasureSpec.makeMeasureSpec(childWidth, MeasureSpec.EXACTLY);
//					
//				}else if(childWidth==LayoutParams.WRAP_CONTENT){
//					
//					widthMeasured=MeasureSpec.makeMeasureSpec(parentWidth, MeasureSpec.AT_MOST);
//				}else if(childWidth==LayoutParams.MATCH_PARENT){
//					widthMeasured=MeasureSpec.makeMeasureSpec(parentWidth, MeasureSpec.EXACTLY);
//				}
//				
//				int childHeight=mPercentLayoutParams.height;
//				if(childHeight>0){
//					heightMeasured=MeasureSpec.makeMeasureSpec(childHeight, MeasureSpec.EXACTLY);
//				}else if (childHeight==-1){
//					heightMeasured=MeasureSpec.makeMeasureSpec(parentHeight, MeasureSpec.EXACTLY);
//				}else if(childHeight==-2){
//					heightMeasured=MeasureSpec.makeMeasureSpec(parentHeight, MeasureSpec.AT_MOST);
//				}
//				
//				child.measure(widthMeasured, heightMeasured);
//				//這裏不考慮padding和margin
//				resultWidth=Math.max(child.getMeasuredWidth()+(int)(mPercentLayoutParams.percentX*mScreenWidth), resultWidth);
//				resultHeight=Math.max(child.getMeasuredHeight()+(int)(mPercentLayoutParams.percentY*mScreenHeight), resultHeight);
//			}//測量完成
			
		}else{
			resultWidth=parentWidth;//如果爲EXACTY 直接把父view傳過來期望的大小賦值給其
			resultHeight=parentHeight;
			
			//不考慮padding 和margin的測量子view
			int viewCount=this.getChildCount();
			PercentLayoutParams mParams;
			for(int i=0;i<viewCount;i++){
				
				View child=this.getChildAt(i);
				mParams=(PercentLayoutParams) child.getLayoutParams();
				if(mParams==null)
					continue;
				int viewWidth=mParams.width;
				int viewHeight=mParams.height;
				int viewMeasuredWidth=this.getChildMeasureSpec(widthMeasureSpec, 0, viewWidth);
				int viewMeasuredHeight=this.getChildMeasureSpec(heightMeasureSpec, 0, viewHeight);
				this.measureChild(child, viewMeasuredWidth, viewMeasuredHeight);
			}
		}
		
		
		this.setMeasuredDimension(resultWidth, resultHeight);
	}
	
	
	@Override
	protected void onLayout(boolean changed, int l, int t, int r, int b) {
		
		int childCount=this.getChildCount();
		for(int i=0;i<childCount;i++){
			
			View child=this.getChildAt(i);
			PercentLayoutParams mParams=(PercentLayoutParams) child.getLayoutParams();
			if(mParams==null)
				continue;
			float percentX=mParams.percentX;
			float percentY=mParams.percentY;
			
			l=(int)((percentX*this.getMeasuredWidth())-child.getMeasuredWidth()/2);
			t=(int) ((percentY*this.getMeasuredHeight())-child.getMeasuredHeight()/2);
			if(l<0){
				l=0;
			}
			if(t<0){
				t=0;
			}
			r= l+child.getMeasuredWidth();
			b=t+child.getMeasuredHeight();
			child.layout(l, t,r,b );
			
		}
		
		
	}
	
	@Override
	public LayoutParams generateLayoutParams(AttributeSet p) {
		return new PercentLayoutParams(getContext(),p);
	}
	
	class PercentLayoutParams extends ViewGroup.LayoutParams{

		
		public float percentX;
		public float percentY;
		
		
		public PercentLayoutParams(Context context, AttributeSet attr) {
			super(context, attr);
			
			TypedArray ta=context.getResources().obtainAttributes(attr, R.styleable.PercentLayout);
			int count=ta.getIndexCount();
			for(int i=0;i<count;i++){
				switch (ta.getIndex(i)) {
				
				case R.styleable.PercentLayout_percentX:
							percentX=ta.getFloat(ta.getIndex(i), -1f);
							if(percentX>1||percentX<0){
								percentX=0;
							}
					break;
				case R.styleable.PercentLayout_percentY:
					percentY=ta.getFloat(ta.getIndex(i), -1f);
					if(percentY>1||percentY<0){
						percentY=0;
					}
					break;

				 
				}
			}
			
			
		}

		public PercentLayoutParams(int arg0, int arg1) {
			super(arg0, arg1);
		}

		public PercentLayoutParams(LayoutParams l) {
			super(l);
			
				
		}			
		
	}

}</span>



通過上面可以看出widthMode=MeasureSpec.AT_MOST , 也就是讓子View決定父容易的大小, 問題就出現在這裏, 因爲相對佈局是子View相對父容器的位置,現在反過來子View決定父容器的大小,顯然不合理,所以我在這裏直接拋了異常,其實這裏還有多種解決方式,比如讓percentX,percentY屬性失效,不拋出異常, 比較拋出異常不是那麼好。

 

自定義屬性:

<<span style="font-size:24px;color:#000099;">?xml version="1.0" encoding="utf-8"?>
<resources>
    
    
     <declare-styleable name="PercentLayout" >
         
         <attr name="percentX" format="float"></attr>
         <attr name="percentY" format="float"></attr>
         
     </declare-styleable> 
    
</resources></span>


佈局:

<span style="font-size:24px;color:#3333ff;"><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:justson="http://schemas.android.com/apk/res/com.example.just_android_percent_layout_custom" >


    <com.example.just_android_percent_layout_custom.PercentLayout 
        android:background="#3d77c0"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/percentLayout">
       
         <Button 
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:id="@+id/button"
            android:background="#ff0000"
            justson:percentX="0.8"
            justson:percentY="0.6"
            android:gravity="center"
            android:text="justson"/>
        
         
         
         <Button 
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:background="#ff0000"
            justson:percentX="0.2"
            justson:percentY="0.3"
            android:gravity="center"
            android:text="justson"/>
         
    </com.example.just_android_percent_layout_custom.PercentLayout>


</RelativeLayout></span>


效果圖

<span style="font-size:24px;color:#3333ff;"><img src="https://img-blog.csdn.net/20160518230504716?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="" /></span>



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章