Android自适应屏幕与动态加载不同的子View

最近工作忙,上周感冒三天,注意合理作息。
每周更新一篇技术博文,第七篇:《 Android自适应屏幕与动态加载不同的子View》

此文所涉及内容网上资源比较多,大部分内容都是引用别人的东西做个归类总结,最后简单实现个demo。

一、自适应分辨率
1.google官方提供的自适应分辨率相关文档
一个笑话:IPhone5出来当天,IOS程序员一上班立马找到Android工程师,说:“哥们,你得帮帮我!”Android工程师大惑:平时都趾高气扬的,今天怎么低声下气。问:“啥事?”IOS程序员笑道:“如何适配不同分辨率的屏幕?”Android工程师丢给他一链接,说:“自己看!” http://t.cn/zlvCVz1 “

笑话归笑话,Android系统支持多分辨率,产品泛滥,用户爽了,但开发人员就特别蛋疼。google官方也提供了很多关于多分辨率支持相关文章:
1). 《 Supporting Multiple Screens》 
2). 《 Supporting Different Screens》
3). 《D esigning for Multiple Screens》

《Supporting Multiple Screens》文中提到一些相关的术语,部分译文如下:

术语和概念
屏幕尺寸
屏幕的物理尺寸,以屏幕的对角线长度作为依据(比如2.8寸,3.5寸)。
简而言之,Android把所有的屏幕尺寸简化为四类:超大、大、正常、小。 (small, normal, large, and extra large.)
程序可以针对这四种尺寸的屏幕提供三种不同的布局方案,然后系统会负责把你的布局方案以合适的方式渲染到对应的屏幕上,这个过程是不需要程序员用代码来干预的。

屏幕密度

物理屏幕上的像素总数。与尺寸类似,也有四种: 低、中、高、超高。 ( low, medium, high, and extra high.) 密度较低的屏幕,在长和宽方向都只有比较少的像素,而高密度的屏幕通常则会有很多——甚至会非常非常多——像素排列在同一区域。屏幕的密度是非常重要的,举个例子,长宽以像素为单位定义的界面元素(比如一个按钮),在低密度的屏幕上会显得很大,但在高密度的屏幕上则会显得很小。

屏幕长宽比
屏幕的物理长度与物理宽度的比例。程序可以为制定长宽比的屏幕提供制定的素材,只需要用系统提供的资源分类符longnotlong

分辨率
屏幕上拥有的像素的总数。注意,虽然大部分情况下分辨率都被表示为宽度×长度,但分辨率并不意味着屏幕长宽比。在Android系统中,程序一般并不直接处理分辨率。


密度无关的像素(DIP
指一个抽象意义上的像素,程序用它来定义界面元素。它作为一个与实际密度无关的单位,帮助程序员构建一个布局方案(界面元素的宽度,高度,位置)。
一个与密度无关的像素,在逻辑尺寸上,与一个位于像素密度为160DPI的屏幕上的像素是一致的,这也是Android平台所假定的默认显示设备。在运行的时候,平台会以目标屏幕的密度作为基准,透明地处理所有需要的DIP缩放操作。要把密度无关像素转换为屏幕像素,可以用这样一个简单的公式:pixels = dips * (density / 160)。举个例子,在DPI240的屏幕上,1DIP等于1.5个物理像素。我们强烈推荐你用DIP来定义你程序的界面布局,因为这样可以保证你的UI在各种分辨率的屏幕上都可以正常显示。


支持的屏幕分辨率范围
1.5及更早版本的Android系统,在设计的时候假定系统只会运行在一种分辨率的设备上——HVGA320×480)分辨率,尺寸为3.2寸。由于系统只能工作在一种屏幕上,开发人员就可以针对那个屏幕来编写自己的程序,而无需去考虑程序在其他屏幕上的显示问题。
但自从Android 1.6以来,系统引入了对多种尺寸、多种分辨率屏幕的支持,以此满足拥有各种配置的新平台的运行需求。这就意味着开发人员在针对Android 1.6或更新版系统开发程序的时候,需要为自己的程序在多种分辨率的屏幕上良好显示作出额外的设计。
为了简化程序员面在对各种分辨率时的困扰,也为了具备各种分辨率的平台都可以直接运行这些程序,Android平台将所有的屏幕以密度和分辨率为分类方式,各自分成了四类:
·四种主要的尺寸:超大、大,正常,小;
·四种不同的密度:超高、高(hdpi),中(mdpi)和低(ldpi)。
如果需要的话,程序可以为各种尺寸的屏幕提供不同的资源(主要是布局),也可以为各种密度的屏幕提供不同的资源(主要是位图)。除此以外,程序不需要针对屏幕的尺寸或者密度作出任何额外的处理。在执行的时候,平台会根据屏幕本身的尺寸与密度特性,自动载入对应的资源,并把它们从逻辑像素(DIP,用于定义界面布局)转换成屏幕上的物理像素。
下表列出了Android平台支持的屏幕中一些比较常用的型号,并显示了系统是如何把它们分类到不同的屏幕配置里的。有些屏幕分辨率并不在下面的列表上,但系统仍会把它们归入下列的某一个类型中。



Figure 1. Illustration of how Android roughly maps actual sizes and densities to generalized sizes and densities (figures are not exact).


Low density (120), ldpi Medium density (160), mdpi High density (240), hdpi Extra high density (320), xhdpi
Smallscreen QVGA (240x320)   480x640  
Normalscreen WQVGA400 (240x400)
WQVGA432 (240x432)
HVGA (320x480) WVGA800 (480x800) 
WVGA854 (480x854) 
600x1024
640x960
Largescreen WVGA800** (480x800)
WVGA854** (480x854)
WVGA800* (480x800) 
WVGA854* (480x854) 
600x1024
   
Extra Largescreen 1024x600 WXGA (1280x800)
1024x768
1280x768
1536x1152
1920x1152 
1920x1200
2048x1536
2560x1536
2560x1600
 Various screen configurations available from emulator skins in the Android SDK (indicated in bold) and other representative resolutions.

2.自适应分辨率常用方法
 在《 android:屏幕自适应》一文中介绍了几种技巧,几种方法归纳如下:
1) 权重法:利用layout_weight来控制各个结构的权重
2) 自定义尺寸法:根据values-WxH的自适应方式,创建不同分辨率的dimens.xml(此方法使用defaults.xml文档也是可行的,defaults.xml文件中可以定义int,String等基本配置信息)
3) 代码设置法:在java中动态获取屏幕宽高,再对布局控件进行设置位置和宽高
4) 多布局:此法是最常用的方式也是最笨的方法,为多个布局适配不一样的布局文件,在资源文件夹中可以有各种自动适配的命名方式,如:drawable-hdpi、drawable-ldpi、drawable-800x480、drawable-hdpi-854x480、values-land(横屏)、values-port(竖屏)、values-1024x768等等,包括上边提到的values-WxH

3.其它布局技术
1)采用HTML设计软件界面,此种方式需要用HTML设计好界面和相关界面上的Javascript响应逻辑操作,如果需要反馈信息就比较麻烦了。这种方法比较适合有网页开发经验的程序员使用,详细见《 android软件开发之webView.addJavascriptInterface循环渐进》一文。

2)jQuery Mobile是 jQuery 在手机和平板设备上的版本,用于创建针对智能手机和平板电脑的跨设备Web应用。详细见《 jQuery Mobile 1.1.0发布》

二、动态加载子View
有时候需要重用同一个View或者动态切换子View的话,比较好的解决方式是使用LayoutInflater来实例化XML布局为View对象,然后通过ViewGroup的addView方式加载新的View。简单用法如下:
private LayoutInflater mLayoutInflater = null;
private FrameLayout dynamicChangesView = null;
private View newViewOne = null;

……

mLayoutInflater = (LayoutInflater) mContext.getSystemService(LAYOUT_INFLATER_SERVICE);
newViewOne = mLayoutInflater.inflate(R.layout.view_one, null,false);
dynamicChangesView.addView(newViewOne);

关于LayoutInflater,比较常见的地方是在1)ListView的Adapter中、 2)Dialog或 PopupWindow自定义布局、3)动态加载子View 。

相关用法见google提供APIs。

三、本文例子

1.例子部分界面







2.布局文件及资源文件
1)defaults.xml



    8
    17

    1
    2
    3
    4


2)styles.xml

        
    
        
    

    
    

    
    

    
    
 

3)activity_view_group.xml


    

        

4)view_one.xml



    

    

    

    




5)view_three.xml



    

        
    

    

        
    

    
        

3.java实现代码
1)DynamicChangesView.java
public class DynamicChangesView extends Activity {
    private Button selectViewOneBtn = null;
    private Button selectViewTwoBtn = null;
    private Button selectViewThreeBtn = null;
    
    private LayoutInflater mLayoutInflater = null;
    public LinearLayout optionsView = null;
    private FrameLayout dynamicChangesView = null;
    private View newViewOne = null;
    private View newViewTwo = null;
    private View newViewThree = null;
    private Context mContext = null;
    
    
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        initData();
    }    
    private void initData() {
        mContext = this;
        setContentView(R.layout.activity_view_group);
        
        selectViewOneBtn = (Button) findViewById(R.id.selectViewOneBtn);
        selectViewTwoBtn = (Button) findViewById(R.id.selectViewTwoBtn);
        selectViewThreeBtn = (Button) findViewById(R.id.selectViewThreeBtn);
        optionsView = (LinearLayout) findViewById(R.id.optionsView);
        dynamicChangesView = (FrameLayout) findViewById(R.id.dynamicChangesView);
        
        selectViewOneBtn.setOnClickListener(mOnClickListener);
        selectViewTwoBtn.setOnClickListener(mOnClickListener);
        selectViewThreeBtn.setOnClickListener(mOnClickListener);
        dynamicChangesView.setOnClickListener(mOnClickListener);
        
        mLayoutInflater = (LayoutInflater) mContext     
                .getSystemService(LAYOUT_INFLATER_SERVICE);
        
        newViewOne =  mLayoutInflater.inflate(R.layout.view_one, null,false);
        newViewTwo =  mLayoutInflater.inflate(R.layout.view_two, null,false);
        // newViewThree  =  mLayoutInflater.inflate(R.layout.view_three, null,false);
        newViewThree = new ViewThree(mContext);
        
        dynamicChangesView.addView(newViewOne);       
    }

    
    private OnClickListener mOnClickListener = new OnClickListener() {
        @Override
        public void onClick(View v) {
            
            if(v == selectViewOneBtn){
                dynamicChangesView.removeAllViews();
                Log.v("", "-----------> selectViewOneBtn");
                dynamicChangesView.addView(newViewOne);
                
            }else if(v == selectViewTwoBtn){
                dynamicChangesView.removeAllViews();
                Log.v("", "-----------> selectViewTwoBtn");                
                dynamicChangesView.addView(newViewTwo);
                
            }else if(v == selectViewThreeBtn){
                dynamicChangesView.removeAllViews();
                Log.v("", "-----------> selectViewThreeBtn");                
                dynamicChangesView.addView(newViewThree);
                
            }else if(v == dynamicChangesView){
                if(optionsView.getVisibility() == View.GONE)
                    optionsView.setVisibility(View.VISIBLE);
                else
                    optionsView.setVisibility(View.GONE);                
            }
        }
    };

}

2).ViewThree.java (此处实现耦合不好)
public class ViewThree extends FrameLayout{
    private LayoutInflater mLayoutInflater = null;
    private Button displayButton = null; 
    private Button hideButton = null;
    private DynamicChangesView mActivity = null;

    public ViewThree(Context context) {
        super(context); 

        mActivity = (DynamicChangesView)context;

        mLayoutInflater = (LayoutInflater) context     
                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        this.addView(mLayoutInflater.inflate(R.layout.view_three, null,false));

        displayButton = (Button) findViewById(R.id.display_button);
        hideButton = (Button) findViewById(R.id.hide_button);

        displayButton.setOnClickListener(mOnClickListener);
        hideButton.setOnClickListener(mOnClickListener);
    }

    public ViewThree(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);        
    }

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

    private OnClickListener mOnClickListener = new OnClickListener() {

        @Override
        public void onClick(View v) {
            if(v == displayButton){
                mActivity.optionsView.setVisibility(View.VISIBLE);
            }else if(v == hideButton){
                mActivity.optionsView.setVisibility(View.GONE);
            }
        }
    }; 

}

参考引用:

1.《android多分辨率支持[最新版,包括Extra Large screen]》
2.《 android:屏幕自适应
3.《   android软件开发之webView.addJavascriptInterface循环渐进
4.《 jQuery Mobile 1.1.0发布
5. Google APIs LayoutInflater
6. Google APIs ViewGroup
7. Google APIs FrameLayout
8.《 Android杂谈--LayoutInflater和MenuInflater用法

你可能感兴趣的:(原创博文,学习笔记)