android-percent-support-lib-sample

来源:互联网 发布:淘宝会员名有什么用处 编辑:程序博客网 时间:2024/06/05 05:14

          我们做Android开发,经常会遇到适配的问题!同一个界面,在不同的分辨率的手机上会有不同的显示效果。

为了解决这个问题,我们在标明控件的高宽的时候。通常会用到dp作为单位。dp*density(像素密度)/160=px;这就保证了

用dp确定高宽的控件在不同的分辨率的手机上我固定的大小。在合适大小的手机屏幕上,当然没问题。但是如果手机屏幕过

大或者过小都会有些不合适。现在github上发现一个开源项目:android-percent-support-lib-sample。定义了两个新的控件:

PercentRelativeLayout,和PercentFrameLayout。能够在通过设置子控件的高宽的百分比。来使得子控件在不同分辨率的手机上保持

相对大小。提供了一个很好的解决适配问题的思路!

          下面介绍一下他们的用法:

<com.wang.percentlayout.PercentRelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    xmlns:wang="http://schemas.android.com/apk/res/com.wang.percentlayout"    android:layout_width="match_parent"    android:layout_height="match_parent"    tools:context="com.wang.percentlayout.MainActivity" >            <TextView         android:id="@+id/tv_left"        android:layout_width="0dp"        android:layout_height="0dp"        android:layout_alignParentTop="true"        android:layout_alignParentLeft="true"        android:background="#44ff0000"        android:text="width:30%,height:30%"        android:gravity="center"        wang:layout_widthPercent="30%"        wang:layout_heightPercent="30%"/>        <TextView         android:id="@+id/tv_below"        android:layout_width="0dp"        android:layout_height="0dp"        android:layout_below="@id/tv_left"        android:layout_alignParentLeft="true"        android:background="#440ff000"        android:text="width:40%,height:50%"        android:gravity="center"        wang:layout_widthPercent="40%"        wang:layout_heightPercent="50%"/>         <TextView         android:id="@+id/tv_right"        android:layout_width="0dp"        android:layout_height="0dp"        android:layout_alignParentTop="true"        android:layout_alignParentRight="true"        android:background="#4400ff00"        android:gravity="center"        android:text="width:70%,height:20%"        wang:layout_widthPercent="70%"        wang:layout_heightPercent="20%"/>          <TextView         android:id="@+id/tv_below_right"        android:layout_width="0dp"        android:layout_height="0dp"        android:layout_below="@id/tv_right"        android:layout_alignParentRight="true"        android:background="#44000ff0"        android:text="width:20%,height:60%"        android:gravity="center"        wang:layout_widthPercent="20%"        wang:layout_heightPercent="60%"/>           <TextView         android:id="@+id/tv_bottom"        android:layout_width="0dp"        android:layout_height="0dp"        android:layout_alignParentBottom="true"        android:layout_alignParentLeft="true"        android:background="#770000ff"        android:gravity="center"        android:text="width:100%,height:20%"        wang:layout_widthPercent="100%"        wang:layout_heightPercent="20%"/></com.wang.percentlayout.PercentRelativeLayout>

效果图


那么PercentRelativieLayout相对RelativeLayout又多做了哪些工作呢?

我们看一下源码:


/* * Copyright (C) 2015 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * *      http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */package com.wang.percentlayout;import android.content.Context;import android.content.res.TypedArray;import android.util.AttributeSet;import android.view.ViewGroup;import android.widget.RelativeLayout;/** * Subclass of {@link RelativeLayout} that supports percentage based dimensions and * margins. * * You can specify dimension or a margin of child by using attributes with "Percent" suffix. Follow * this example: * * <pre class="prettyprint"> * <android.support.percent.PercentRelativeLayout *         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"/> *     <ImageView *         app:layout_widthPercent="50%" *         app:layout_heightPercent="50%" *         app:layout_marginTopPercent="25%" *         app:layout_marginLeftPercent="25%"/> * </android.support.percent.PercentFrameLayout/> * </pre> * * The attributes that you can use are: * <ul> *     <li>{@code layout_widthPercent} *     <li>{@code layout_heightPercent} *     <li>{@code layout_marginPercent} *     <li>{@code layout_marginLeftPercent} *     <li>{@code layout_marginTopPercent} *     <li>{@code layout_marginRightPercent} *     <li>{@code layout_marginBottomPercent} *     <li>{@code layout_marginStartPercent} *     <li>{@code layout_marginEndPercent} * </ul> * * It is not necessary to specify {@code layout_width/height} if you specify {@code * layout_widthPercent.} However, if you want the view to be able to take up more space than what * percentage value permits, you can add {@code layout_width/height="wrap_content"}. In that case * if the percentage size is too small for the View's content, it will be resized using * {@code wrap_content} rule. */public class PercentRelativeLayout extends RelativeLayout {    private final PercentLayoutHelper mHelper = new PercentLayoutHelper(this);    public PercentRelativeLayout(Context context) {        super(context);    }    public PercentRelativeLayout(Context context, AttributeSet attrs) {        super(context, attrs);    }    public PercentRelativeLayout(Context context, AttributeSet attrs, int defStyle) {        super(context, attrs, defStyle);    }    @Override    public LayoutParams generateLayoutParams(AttributeSet attrs) {        return new LayoutParams(getContext(), attrs);    }    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        mHelper.adjustChildren(widthMeasureSpec, heightMeasureSpec);        super.onMeasure(widthMeasureSpec, heightMeasureSpec);        if (mHelper.handleMeasuredStateTooSmall()) {            super.onMeasure(widthMeasureSpec, heightMeasureSpec);        }    }    @Override    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {        super.onLayout(changed, left, top, right, bottom);        mHelper.restoreOriginalParams();    }    public static class LayoutParams extends RelativeLayout.LayoutParams            implements PercentLayoutHelper.PercentLayoutParams {        private PercentLayoutHelper.PercentLayoutInfo mPercentLayoutInfo;        public LayoutParams(Context c, AttributeSet attrs) {            super(c, attrs);            mPercentLayoutInfo = PercentLayoutHelper.getPercentLayoutInfo(c, attrs);        }        public LayoutParams(int width, int height) {            super(width, height);        }        public LayoutParams(ViewGroup.LayoutParams source) {            super(source);        }        public LayoutParams(MarginLayoutParams source) {            super(source);        }        @Override        public PercentLayoutHelper.PercentLayoutInfo getPercentLayoutInfo() {            return mPercentLayoutInfo;        }        @Override        protected void setBaseAttributes(TypedArray a, int widthAttr, int heightAttr) {            PercentLayoutHelper.fetchWidthAndHeight(this, a, widthAttr, heightAttr);        }    }}
我们可以看到,这里先是自定义一个新的LayoutParams(里面定义了新的属性),然后重写了generateLayoutParams方法。

在自定义的LayoutParams中实现了接口PercentLayoutHelper.PercentLayoutParams;

 public interface PercentLayoutParams    {        PercentLayoutInfo getPercentLayoutInfo();    }
重写了getPercentLayoutInfo()方法,返回mPercentLayoutInfo:

@Override        public PercentLayoutHelper.PercentLayoutInfo getPercentLayoutInfo() {            return mPercentLayoutInfo;        }
而,mPercentLayoutInfo中包含了自定义的百分比属性(实在构造方法中取到的):

public LayoutParams(Context c, AttributeSet attrs) {            super(c, attrs);            mPercentLayoutInfo = PercentLayoutHelper.getPercentLayoutInfo(c, attrs);        }

public static PercentLayoutInfo getPercentLayoutInfo(Context context,                                                         AttributeSet attrs)    {        PercentLayoutInfo info = null;        TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.PercentLayout_Layout);        int index = R.styleable.PercentLayout_Layout_layout_widthPercent;        String sizeStr = array.getString(index);        PercentLayoutInfo.PercentVal percentVal = getPercentVal(sizeStr, true);        if (percentVal != null)        {            if (Log.isLoggable(TAG, Log.VERBOSE))            {                Log.v(TAG, "percent width: " + percentVal.percent);            }            info = checkForInfoExists(info);            info.widthPercent = percentVal;        }        sizeStr = array.getString(R.styleable.PercentLayout_Layout_layout_heightPercent);        percentVal = getPercentVal(sizeStr, false);        if (sizeStr != null)        {            if (Log.isLoggable(TAG, Log.VERBOSE))            {                Log.v(TAG, "percent height: " + percentVal.percent);            }            info = checkForInfoExists(info);            info.heightPercent = percentVal;        }        // value = array.getFraction(R.styleable.PercentLayout_Layout_layout_marginPercent, 1, 1, -1f);        sizeStr = array.getString(R.styleable.PercentLayout_Layout_layout_marginPercent);        // just for judge        percentVal = getPercentVal(sizeStr, false);        if (percentVal != null)        {            if (Log.isLoggable(TAG, Log.VERBOSE))            {                Log.v(TAG, "percent margin: " + percentVal.percent);            }            info = checkForInfoExists(info);            info.leftMarginPercent = getPercentVal(sizeStr, true);            info.topMarginPercent = getPercentVal(sizeStr, false);            info.rightMarginPercent = getPercentVal(sizeStr, true);            info.bottomMarginPercent = getPercentVal(sizeStr, false);        }        //value = array.getFraction(R.styleable.PercentLayout_Layout_layout_marginLeftPercent, 1, 1,        //      -1f);        sizeStr = array.getString(R.styleable.PercentLayout_Layout_layout_marginLeftPercent);        percentVal = getPercentVal(sizeStr, true);        if (percentVal != null)        {            if (Log.isLoggable(TAG, Log.VERBOSE))            {                Log.v(TAG, "percent left margin: " + percentVal.percent);            }            info = checkForInfoExists(info);            info.leftMarginPercent = percentVal;        }        //  value = array.getFraction(R.styleable.PercentLayout_Layout_layout_marginTopPercent, 1, 1,        //        -1f);        sizeStr = array.getString(R.styleable.PercentLayout_Layout_layout_marginTopPercent);        percentVal = getPercentVal(sizeStr, false);        if (percentVal != null)        {            if (Log.isLoggable(TAG, Log.VERBOSE))            {                Log.v(TAG, "percent top margin: " + percentVal.percent);            }            info = checkForInfoExists(info);            info.topMarginPercent = percentVal;        }        // value = array.getFraction(R.styleable.PercentLayout_Layout_layout_marginRightPercent, 1, 1,        //       -1f);        sizeStr = array.getString(R.styleable.PercentLayout_Layout_layout_marginRightPercent);        percentVal = getPercentVal(sizeStr, true);        if (percentVal != null)        {            if (Log.isLoggable(TAG, Log.VERBOSE))            {                Log.v(TAG, "percent right margin: " + percentVal.percent);            }            info = checkForInfoExists(info);            info.rightMarginPercent = percentVal;        }        //value = array.getFraction(R.styleable.PercentLayout_Layout_layout_marginBottomPercent, 1, 1,        //  -1f);        sizeStr = array.getString(R.styleable.PercentLayout_Layout_layout_marginBottomPercent);        percentVal = getPercentVal(sizeStr, false);        if (percentVal != null)        {            if (Log.isLoggable(TAG, Log.VERBOSE))            {                Log.v(TAG, "percent bottom margin: " + percentVal.percent);            }            info = checkForInfoExists(info);            info.bottomMarginPercent = percentVal;        }        // value = array.getFraction(R.styleable.PercentLayout_Layout_layout_marginStartPercent, 1, 1,        //       -1f);        sizeStr = array.getString(R.styleable.PercentLayout_Layout_layout_marginStartPercent);        percentVal = getPercentVal(sizeStr, true);        if (percentVal != null)        {            if (Log.isLoggable(TAG, Log.VERBOSE))            {                Log.v(TAG, "percent start margin: " + percentVal.percent);            }            info = checkForInfoExists(info);            info.startMarginPercent = percentVal;        }        //value = array.getFraction(R.styleable.PercentLayout_Layout_layout_marginEndPercent, 1, 1,        //      -1f);        sizeStr = array.getString(R.styleable.PercentLayout_Layout_layout_marginEndPercent);        percentVal = getPercentVal(sizeStr, true);        if (percentVal != null)        {            if (Log.isLoggable(TAG, Log.VERBOSE))            {                Log.v(TAG, "percent end margin: " + percentVal.percent);            }            info = checkForInfoExists(info);            info.endMarginPercent = percentVal;        }        //textSizePercent        sizeStr = array.getString(R.styleable.PercentLayout_Layout_layout_textSizePercent);        percentVal = getPercentVal(sizeStr, false);        if (percentVal != null)        {            if (Log.isLoggable(TAG, Log.VERBOSE))            {                Log.v(TAG, "percent text size: " + percentVal.percent);            }            info = checkForInfoExists(info);            info.textSizePercent = percentVal;        }        //maxWidth        percentVal = getPercentVal(array,                R.styleable.PercentLayout_Layout_layout_maxWidthPercent,                true);        if (percentVal != null)        {            checkForInfoExists(info);            info.maxWidthPercent = percentVal;        }        //maxHeight        percentVal = getPercentVal(array,                R.styleable.PercentLayout_Layout_layout_maxHeightPercent,                false);        if (percentVal != null)        {            checkForInfoExists(info);            info.maxHeightPercent = percentVal;        }        //minWidth        percentVal = getPercentVal(array,                R.styleable.PercentLayout_Layout_layout_minWidthPercent,                true);        if (percentVal != null)        {            checkForInfoExists(info);            info.minWidthPercent = percentVal;        }        //minHeight        percentVal = getPercentVal(array,                R.styleable.PercentLayout_Layout_layout_minHeightPercent,                false);        Log.d(TAG,"minHeight = "+percentVal);        if (percentVal != null)        {            checkForInfoExists(info);            info.minHeightPercent = percentVal;        }        array.recycle();        if (Log.isLoggable(TAG, Log.DEBUG))        {            Log.d(TAG, "constructed: " + info);        }        return info;    }

获得属性后,在onMeasure中对器子控件,重新计算高宽。

@Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        mHelper.adjustChildren(widthMeasureSpec, heightMeasureSpec);        super.onMeasure(widthMeasureSpec, heightMeasureSpec);        if (mHelper.handleMeasuredStateTooSmall()) {            super.onMeasure(widthMeasureSpec, heightMeasureSpec);        }    }


public void adjustChildren(int widthMeasureSpec, int heightMeasureSpec)    {        if (Log.isLoggable(TAG, Log.DEBUG))        {            Log.d(TAG, "adjustChildren: " + mHost + " widthMeasureSpec: "                    + View.MeasureSpec.toString(widthMeasureSpec) + " heightMeasureSpec: "                    + View.MeasureSpec.toString(heightMeasureSpec));        }        int widthHint = View.MeasureSpec.getSize(widthMeasureSpec);        int heightHint = View.MeasureSpec.getSize(heightMeasureSpec);        Log.d(TAG, "widthHint = " + widthHint + " , heightHint = " + heightHint);        for (int i = 0, N = mHost.getChildCount(); i < N; i++)        {            View view = mHost.getChildAt(i);            ViewGroup.LayoutParams params = view.getLayoutParams();            if (Log.isLoggable(TAG, Log.DEBUG))            {                Log.d(TAG, "should adjust " + view + " " + params);            }            if (params instanceof PercentLayoutParams)            {                PercentLayoutInfo info =                        ((PercentLayoutParams) params).getPercentLayoutInfo();//关键调用                if (Log.isLoggable(TAG, Log.DEBUG))                {                    Log.d(TAG, "using " + info);                }                if (info != null)                {                    supportTextSize(widthHint, heightHint, view, info);                    supportMinOrMaxDimesion(widthHint, heightHint, view, info);                    if (params instanceof ViewGroup.MarginLayoutParams)                    {                        info.fillMarginLayoutParams((ViewGroup.MarginLayoutParams) params,                                widthHint, heightHint);                    } else                    {                        info.fillLayoutParams(params, widthHint, heightHint);                    }                }            }        }    }

上面关键代码:

PercentLayoutInfo info =                        ((PercentLayoutParams) params).getPercentLayoutInfo();







1 0