android机型适配,这是android开发者心中的一个痛,真的很痛。
上面这篇文章,是我把谷歌官方的适配解决方案的翻译。但是官方的机型适配解决方案还是有几个缺点:
(1)机型适配的分辨率太多,并且每个分辨率都要分别调整UI布局
**-hdpi, **-mdpi, **-lmdpi, **-xdpi, **-xxdpi, *******等等,这些分辨率只能是大概的针对各个机型,但是对于特定的机型,UI效果是非常的不理想,要调整许多参数,就是在同一个分辨率下,不同的机型,也是要修改许多的参数,这样来说,机型适配的工作量,是非常大的,这也是开发者心中痛的一个重要原因。
(2)机型适配主方式,不能做到个各个机型的UI效果一致
针对不同分辨率的机型,分别做UI调整,是能适配各种机型,但是不能做到各个机型UI效果一致,这其实是机型适配的一个非常大的缺点,也就是说,其实他还是没有提供一个统一的适配所有机型的解决方案
比例伸缩适配(PercentageLayout)
有没有一种界面的适配方案,能解决上面二个缺点呢,我这提供一个解决方案,比例伸缩适配(PercentageLayout),能很好的把所有机型当做一个分辨率来对待,并能保持所有机型UI效果的一致。(说明,这不是我想出来的,是别人做出来的)。
比例伸缩适配(PercentageLayout)核心思想:
PercentageLayout 继承相对布局RelativeLayout,然后绘制UI时,对PercentageLayout 自己和PercentageLayout 下的所有控制margin参数,宽,高,padding参数,还有TextView的字体大小按比例来进行伸缩(放大或缩小)。
比例伸缩适配(PercentageLayout)的核心代码:
private void scaleChild(View child){
float widthScale = screenWidth/baseWidth;
float heightScale = screenHeight/baseHeight;
Log.i(TAG,"scaleChild---widthScale="+widthScale+"--heightScale="+heightScale);
Log.i(TAG,"!Boolean.TRUE.equals(scaledMap.get(child)):"+(!Boolean.TRUE.equals(scaledMap.get(child))));
if(!Boolean.TRUE.equals(scaledMap.get(child))){
ViewGroup.LayoutParams st = child.getLayoutParams();
if(st instanceof ViewGroup.MarginLayoutParams){
MarginLayoutParams margin = (MarginLayoutParams)st;
margin.leftMargin*=widthScale;
margin.rightMargin*=widthScale;
margin.topMargin*=heightScale;
margin.bottomMargin*=heightScale;
Log.i(TAG,"scaleChild---margin.leftMargin="+margin.leftMargin+"--margin.rightMargin="+margin.rightMargin
+"--margin.topMargin="+margin.topMargin+"--margin.bottomMargin="+margin.bottomMargin);
}
boolean constraitRatio = false;
if(st instanceof PercentageLayout.LayoutParams){
constraitRatio = ((PercentageLayout.LayoutParams)st).constraitRatio;
}
Log.i(TAG,"scaleChild--origin---st.width="+st.width+"--st.height="+st.height);
if(st.width>0){
if(child!=this||scaleItSelf){
if(!constraitRatio){
st.width=(int) (st.width*widthScale);
Log.i(TAG,"scaleChild---st.width="+st.width);
}else{
st.width=(int) (st.width*heightScale);
Log.i(TAG,"scaleChild--else---st.width="+st.width);
}
}
}
if(st.height>0){
if(child!=this||scaleItSelf){
st.height*=heightScale;
Log.i(TAG,"scaleChild---st.height="+st.height);
}
}
Drawable background = child.getBackground();
if(background == null||!(background instanceof NinePatchDrawable)){
Log.i(TAG,"scaleChild---child.getPadding--"+(int)(child.getPaddingLeft())+","+(int)(child.getPaddingTop())+","
+(int)(child.getPaddingRight())+","+(int)(child.getPaddingBottom()));
child.setPadding((int)(child.getPaddingLeft()*widthScale), (int)(child.getPaddingTop()*heightScale),
(int)(child.getPaddingRight()*widthScale), (int)(child.getPaddingBottom()*heightScale));
Log.i(TAG,"scaleChild---child.setPadding--"+(int)(child.getPaddingLeft()*widthScale)+","+(int)(child.getPaddingTop()*heightScale)+","
+(int)(child.getPaddingRight()*widthScale)+","+(int)(child.getPaddingBottom()*heightScale));
}
if(strenchTextsize && heightScale!=1 && child instanceof TextView){
if(screenHeight <= 320){
heightScale = widthScale;
}
TextView t = (TextView)child;
float size = t.getTextSize();
t.setTextSize(TypedValue.COMPLEX_UNIT_PX, size*heightScale);
Log.i(TAG,"scaleChild---t.setTextSize--size="+size+"size*heightScale="+size*heightScale);
if(t instanceof AutoCompleteTextView){
AutoCompleteTextView at = (AutoCompleteTextView)t;
// at.setDropDownHeight((int) (at.getDropDownHeight()*heightScale));
}
}
scaledMap.put(child, Boolean.TRUE);
}
if((child instanceof ViewGroup) && !(child instanceof PercentageLayout)){
Log.i(TAG,"(child instanceof ViewGroup) && !(child instanceof PercentageLayout):"+
((child instanceof ViewGroup) && !(child instanceof PercentageLayout)));
scaleDimensions((ViewGroup) child);
}
}
比例伸缩适配(PercentageLayout)的一个Demo:
(1)PercentageLayout.java
package com.example.testpercentagelayout;
import java.util.HashMap;
import java.util.Map;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.NinePatchDrawable;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AutoCompleteTextView;
import android.widget.ImageView;
import android.widget.PopupWindow;
import android.widget.RelativeLayout;
import android.widget.TextView;
public class PercentageLayout extends RelativeLayout {
private static String TAG = "PercentageLayout_test";
private float baseWidth = 854;
private float baseHeight = 480;
private float screenWidth = 427;
private float screenHeight = 240;
private boolean strenchTextsize = true;
/**是否对自身大小进行缩放*/
private boolean scaleItSelf = true;
private Map<View,Boolean>scaledMap = new HashMap<View,Boolean>();
public PercentageLayout(Context context) {
super(context);
// TODO Auto-generated constructor stub
}
public PercentageLayout(Context context, AttributeSet attrs) {
this(context, attrs,0);
// TODO Auto-generated constructor stub
}
public PercentageLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initializeScreenSize(context);
// TODO Auto-generated constructor stub
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.PercentageLayout);
baseWidth = a.getDimension(R.styleable.PercentageLayout_base_width, baseWidth);
baseHeight = a.getDimension(R.styleable.PercentageLayout_base_height, baseHeight);
screenWidth = a.getDimension(R.styleable.PercentageLayout_des_width, screenWidth);
screenHeight = a.getDimension(R.styleable.PercentageLayout_des_height, screenHeight);
strenchTextsize = a.getBoolean(R.styleable.PercentageLayout_strenchTextsize, strenchTextsize);
scaleItSelf = a.getBoolean(R.styleable.PercentageLayout_scaleItSelf, scaleItSelf);
a.recycle();
Log.i(TAG,"PercentageLayout--baseWidth:"+baseWidth+"--baseHeight:"+baseHeight+"--screenWidth:"+
screenWidth+"--screenHeight:"+screenHeight+"--strenchTextsize:"+strenchTextsize+
"--scaleItSelf:"+scaleItSelf);
}
public void initializeScreenSize(Context context) {
DisplayMetrics metrics = context.getResources().getDisplayMetrics();
screenHeight = Math.min(metrics.heightPixels,metrics.widthPixels);
screenWidth = Math.max(metrics.widthPixels,metrics.heightPixels);
Log.i(TAG, "initializeScreenSize---屏幕宽度:" + metrics.widthPixels + "px 屏幕高度:"
+ metrics.heightPixels + "px");
Log.i(TAG, "initializeScreenSize---屏幕密度:" + metrics.density);
Log.i(TAG, "initializeScreenSize---屏幕DPI:" + metrics.densityDpi);
Log.i(TAG,"initializeScreenSize----screenWidth="+screenWidth+" --screenHeight="+screenHeight);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
scaleDimensions(this);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
private void scaleDimensions(ViewGroup group){
int count = group.getChildCount();
Log.i(TAG,"scaleDimensions-----count="+count);
if(group instanceof PercentageLayout){
Log.i(TAG,"scaleDimensions-----scaleChild(group)");
scaleChild(group);
}
for (int i = 0; i < count; i++) {
View child = group.getChildAt(i);
Log.i(TAG,"scaleDimensions-----scaleChild(child)");
scaleChild(child);
}
}
private void scaleChild(View child){
float widthScale = screenWidth/baseWidth;
float heightScale = screenHeight/baseHeight;
Log.i(TAG,"scaleChild---widthScale="+widthScale+"--heightScale="+heightScale);
Log.i(TAG,"!Boolean.TRUE.equals(scaledMap.get(child)):"+(!Boolean.TRUE.equals(scaledMap.get(child))));
if(!Boolean.TRUE.equals(scaledMap.get(child))){
ViewGroup.LayoutParams st = child.getLayoutParams();
if(st instanceof ViewGroup.MarginLayoutParams){
MarginLayoutParams margin = (MarginLayoutParams)st;
margin.leftMargin*=widthScale;
margin.rightMargin*=widthScale;
margin.topMargin*=heightScale;
margin.bottomMargin*=heightScale;
Log.i(TAG,"scaleChild---margin.leftMargin="+margin.leftMargin+"--margin.rightMargin="+margin.rightMargin
+"--margin.topMargin="+margin.topMargin+"--margin.bottomMargin="+margin.bottomMargin);
}
boolean constraitRatio = false;
if(st instanceof PercentageLayout.LayoutParams){
constraitRatio = ((PercentageLayout.LayoutParams)st).constraitRatio;
}
Log.i(TAG,"scaleChild--origin---st.width="+st.width+"--st.height="+st.height);
if(st.width>0){
if(child!=this||scaleItSelf){
if(!constraitRatio){
st.width=(int) (st.width*widthScale);
Log.i(TAG,"scaleChild---st.width="+st.width);
}else{
st.width=(int) (st.width*heightScale);
Log.i(TAG,"scaleChild--else---st.width="+st.width);
}
}
}
if(st.height>0){
if(child!=this||scaleItSelf){
st.height*=heightScale;
Log.i(TAG,"scaleChild---st.height="+st.height);
}
}
Drawable background = child.getBackground();
if(background == null||!(background instanceof NinePatchDrawable)){
Log.i(TAG,"scaleChild---child.getPadding--"+(int)(child.getPaddingLeft())+","+(int)(child.getPaddingTop())+","
+(int)(child.getPaddingRight())+","+(int)(child.getPaddingBottom()));
child.setPadding((int)(child.getPaddingLeft()*widthScale), (int)(child.getPaddingTop()*heightScale),
(int)(child.getPaddingRight()*widthScale), (int)(child.getPaddingBottom()*heightScale));
Log.i(TAG,"scaleChild---child.setPadding--"+(int)(child.getPaddingLeft()*widthScale)+","+(int)(child.getPaddingTop()*heightScale)+","
+(int)(child.getPaddingRight()*widthScale)+","+(int)(child.getPaddingBottom()*heightScale));
}
if(strenchTextsize && heightScale!=1 && child instanceof TextView){
if(screenHeight <= 320){
heightScale = widthScale;
}
TextView t = (TextView)child;
float size = t.getTextSize();
t.setTextSize(TypedValue.COMPLEX_UNIT_PX, size*heightScale);
Log.i(TAG,"scaleChild---t.setTextSize--size="+size+"size*heightScale="+size*heightScale);
if(t instanceof AutoCompleteTextView){
AutoCompleteTextView at = (AutoCompleteTextView)t;
// at.setDropDownHeight((int) (at.getDropDownHeight()*heightScale));
}
}
scaledMap.put(child, Boolean.TRUE);
}
if((child instanceof ViewGroup) && !(child instanceof PercentageLayout)){
Log.i(TAG,"(child instanceof ViewGroup) && !(child instanceof PercentageLayout):"+
((child instanceof ViewGroup) && !(child instanceof PercentageLayout)));
scaleDimensions((ViewGroup) child);
}
}
@Override
protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
return p instanceof PercentageLayout.LayoutParams;
}
@Override
protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
return super.generateDefaultLayoutParams();
}
@Override
public RelativeLayout.LayoutParams generateLayoutParams(AttributeSet attrs) {
return new LayoutParams(getContext(),attrs);
}
@Override
protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
return new LayoutParams(p);
}
public float getBaseWidth() {
return baseWidth;
}
public void setBaseWidth(float baseWidth) {
this.baseWidth = baseWidth;
}
public float getBaseHeight() {
return baseHeight;
}
public void setBaseHeight(float baseHeight) {
this.baseHeight = baseHeight;
}
public float getScreenWidth() {
return screenWidth;
}
public void setScreenWidth(float screenWidth) {
this.screenWidth = screenWidth;
}
public float getScreenHeight() {
return screenHeight;
}
public void setScreenHeight(float screenHeight) {
this.screenHeight = screenHeight;
}
public static class LayoutParams extends RelativeLayout.LayoutParams{
/**是否固定长宽比,默认为true*/
private boolean constraitRatio = true;
/**是否根据屏幕重置图片大小,仅用于ImageView并且layout_width=wrap_content的情况,默认为true*/
private boolean strenchImage = true;
/**是否根据屏幕大小重置文字大小,仅用于TextView,默认为true*/
private boolean strenchTextsize = true;
/**是否在固定长宽比时使用屏幕高度作为计算基准,默认为true*/
private boolean resizeByHeight = true;
public LayoutParams(Context c, AttributeSet attrs) {
super(c, attrs);
// TODO PercentageLayout.LayoutParams(Context c, AttributeSet attrs)
TypedArray a = c.obtainStyledAttributes(attrs,R.styleable.PercentageLayout);
constraitRatio = a.getBoolean(R.styleable.PercentageLayout_constraitRatio, constraitRatio);
strenchImage = a.getBoolean(R.styleable.PercentageLayout_strenchImage, strenchImage);
strenchTextsize = a.getBoolean(R.styleable.PercentageLayout_strenchTextsize, strenchTextsize);
resizeByHeight = a.getBoolean(R.styleable.PercentageLayout_resizeByHeight, resizeByHeight);
a.recycle();
Log.i(TAG,"LayoutParams--constraitRatio:"+constraitRatio+"--strenchImage:"+strenchImage
+"--strenchTextsize:"+strenchTextsize+"--resizeByHeight:"+resizeByHeight);
}
public LayoutParams(int arg0, int arg1) {
super(arg0, arg1);
}
public LayoutParams(ViewGroup.LayoutParams arg0) {
super(arg0);
}
public LayoutParams(MarginLayoutParams arg0) {
super(arg0);
}
}
}
(2)res/values/attrs.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="PercentageLayout">
<attr name="base_width" format="dimension" />
<attr name="base_height" format="dimension" />
<attr name="des_width" format="dimension" />
<attr name="des_height" format="dimension" />
<attr name="strenchTextsize" format="boolean" />
<attr name="scaleItSelf" format="boolean" />
<attr name="constraitRatio" format="boolean" />
<attr name="resizeByHeight" format="boolean" />
<attr name="strenchImage" format="boolean" />
</declare-styleable>
</resources>
(3)res/layout/activity_test_percentage_layout_main.xml
<com.example.testpercentagelayout.PercentageLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/PL"
android:layout_width="500px"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:background="@drawable/bj"
tools:context=".TestPercentageLayoutMainActivity" >
<ImageView
android:id="@+id/my_iv_pl"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="100px"
android:layout_marginRight="100px"
android:layout_marginTop="200px"
android:layout_marginBottom="200px"
android:background="@drawable/btn_bg_yellow"
/>
<TextView
android:id="@+id/my_tv_pl"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="50px"
android:layout_below="@id/my_iv_pl"
android:text="@string/hello_world"
android:textSize="35px"/>
</com.example.testpercentagelayout.PercentageLayout>
下载地址:
http://download.csdn.net/detail/hfreeman2008/7509507
参考文献:
http://blog.csdn.net/hfreeman2008/article/details/23749007