第一行代码(四)Fragment

        当今是移动设备发展非常迅速的时代,不仅手机已经成为了生活必需品,就连平板电脑也变得越来越普及。平板电脑和手机最大的区别就在于屏幕的大小,一般手机屏幕的大小会在 3 英寸到 5 英寸之间,而一般平板电脑屏幕的大小会在 7 英寸到 10 英寸之间。屏幕大小差距过大有可能会让同样的界面在视觉效果上有较大的差异,比如一些界面在手机上看起来非常美观,但在平板上看起来就可能会有控件被过分拉长、元素之间空隙过大等情况。

        作为一名专业的 Android 开发人员,能够同时兼顾到手机和平板的开发是我们必须要做的事情。android 自 3.0 版本开始引入了碎片的概念,它可以让界面在平板上更好地展示。

1.  碎片是什么

        碎片 (Fragment) 是一种可以嵌入在 Activity 当中的 UI 片段,它能让程序更加合理和充分地利用大屏幕的空间,因而在平板上应用的非常广泛。虽然碎片对你来说应该是个全新的概念,但我相信你学习起来应该毫不费力,因为它和 Activity 实在是太像了,同样都能包含布局,同样都有自己的生命周期。你甚至可以将碎片理解成一个迷你型的 Activity,虽然这个迷你型的 Activity 有可能和普通的 Activity 是一样大的。

        那么究竟要如何使用碎片才能充分地利用平板屏幕的空间呢?想象我们正在开发一个新闻英语,其中一个界面使用 ListView 展示了一组新闻的标题,当点击了其中一个标题,就打开另一个界面显示新闻的详细内容。如果是在手机中设计,我们可以将新闻标题列表放在一个 Activity 中,将新闻的详细内容放在另一个 Activity 中,如图 4.1 所示。

        可是如果在平板上也这么设计,那么新闻标题列表将会被拉长至填充慢整个平板的屏幕,而新闻的标题一般都不会太长,这样将会导致界面上有大量的空白区域,如图 4.2 所示。

        因此,更好的设计方案是将新闻标题列表界面和新闻详细内容界面分别放在两个碎片中,然后在同一个 Activity 里引入这两个碎片,这样就可以将屏幕空间充分地利用起来了,如图 4.3 所示。

2.  碎片的使用方式

        介绍了这么多抽象的东西,也是时候应该学习一下碎片的具体用法了。你已经知道,碎片通常都是在平板开发中才会使用的,因此我们首先要做的就是新建一个平板电脑的模拟器。由于 4.0 系统的平板模拟器好像存在 bug,这里就新建一个 4.2 系统的平板模拟器。

2.1  碎片的简单用法

        这里我们准备先写一个最简单的碎片示例来练练手,在一个 Activity 当中添加两个碎片,并让这两个碎片平分 Activity 空间。

       新建一个左侧碎片布局 left_fragment.xml,代码如下所示:

  1. <LinearLayout xmlns:android=“http://schemas.android.com/apk/res/android”  
  2.     android:layout_width=“match_parent”  
  3.     android:layout_height=“match_parent”  
  4.     android:orientation=“vertical” >  
  5.       
  6.     <Button   
  7.         android:id=“@+id/button”  
  8.         android:layout_width=“wrap_content”  
  9.         android:layout_height=“wrap_content”  
  10.         android:layout_gravity=“center_horizontal”  
  11.         android:text=“Button”  
  12.         />  
  13.   
  14. </LinearLayout>  
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <Button 
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:text="Button"
        />

</LinearLayout>

        这个布局非常简单,只放置了一个按钮,并让它水平居中显示。然后新建右侧碎片布局 right_fragment.xml,代码如下所示:

  1. <LinearLayout xmlns:android=“http://schemas.android.com/apk/res/android”  
  2.     android:layout_width=“match_parent”  
  3.     android:layout_height=“match_parent”  
  4.     android:background=“#00ff00”  
  5.     android:orientation=“vertical” >  
  6.       
  7.     <TextView   
  8.         android:layout_width=“wrap_content”  
  9.         android:layout_height=“wrap_content”  
  10.         android:layout_gravity=“center_horizontal”  
  11.         android:textSize=“20sp”  
  12.         android:text=“This is right fragment”  
  13.         />  
  14.       
  15. </LinearLayout>  
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#00ff00"
    android:orientation="vertical" >

    <TextView 
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:textSize="20sp"
        android:text="This is right fragment"
        />

</LinearLayout>

        可以看到,我们将这个布局的背景色设置成绿色,并放置了一个 TextView 用于显示一段文本。接着新建一个 LeftFragment 类,继承自 Fragment。注意,这里可能会有两个不同包下的 Fragment 供你选择,建议使用 android.app.Fragment,因为我们的程序是面向 Android 4.0 以上系统的,另一个包下的 Fragment 主要是用于兼容低版本的 Android 系统。LeftFragment 的代码如下所示:

  1. public class LeftFragment extends Fragment {  
  2.       
  3.     @Override  
  4.     public View onCreateView(LayoutInflater inflater, ViewGroup container,  
  5.             Bundle savedInstanceState) {  
  6.         View view = inflater.inflate(R.layout.left_fragment, container, false);  
  7.         return view;  
  8.     }  
  9.       
  10. }  
public class LeftFragment extends Fragment {

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.left_fragment, container, false);
        return view;
    }

}

        这里仅仅是重写了 Fragment 的 onCreateView() 方法,然后在这个方法中通过 LayoutInflater 的 inflate() 方法将刚才定义的 left_fragment 布局动态加载进来,整个方法简单明了。接着我们用同样的方法再新建一个 RightFragment,代码如下所示:

  1. public class RightFragment extends Fragment {  
  2.       
  3.     @Override  
  4.     public View onCreateView(LayoutInflater inflater, ViewGroup container,  
  5.             Bundle savedInstanceState) {  
  6.         View view = inflater.inflate(R.layout.right_fragment, container, false);  
  7.         return view;  
  8.     }  
  9.       
  10. }  
public class RightFragment extends Fragment {

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.right_fragment, container, false);
        return view;
    }

}

        基本上代码都是相同的,相信已经没有必要再做什么解释了。接下来修改 activity_main.xml 中的代码,如下所示:

  1. <LinearLayout xmlns:android=“http://schemas.android.com/apk/res/android”  
  2.     android:layout_width=“match_parent”  
  3.     android:layout_height=“match_parent” >  
  4.   
  5.     <fragment  
  6.         android:id=“@+id/left_fragment”  
  7.         android:name=“com.example.fragmenttest.LeftFragment”  
  8.         android:layout_width=“0dp”  
  9.         android:layout_height=“match_parent”   
  10.         android:layout_weight=“1”/>  
  11.       
  12.        <fragment  
  13.         android:id=“@+id/right_fragment”  
  14.         android:name=“com.example.fragmenttest.RightFragment”  
  15.         android:layout_width=“0dp”  
  16.         android:layout_height=“match_parent”   
  17.         android:layout_weight=“1”/>  
  18.   
  19. </LinearLayout>  
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <fragment
        android:id="@+id/left_fragment"
        android:name="com.example.fragmenttest.LeftFragment"
        android:layout_width="0dp"
        android:layout_height="match_parent" 
        android:layout_weight="1"/>

       <fragment
        android:id="@+id/right_fragment"
        android:name="com.example.fragmenttest.RightFragment"
        android:layout_width="0dp"
        android:layout_height="match_parent" 
        android:layout_weight="1"/>

</LinearLayout>

        可以看到,我们使用了 <fragment> 标签在布局中添加碎片,其中指定的大多数属性你都是熟悉的,只不过这里还需要通过 android:name 属性来显示指明要添加的碎片类名,注意一定要将类的包名也加上。

        这样最简单的碎片示例就已经写好了,现在运行一下程序,效果如图 4.6 所示。

图  4.6

        正如我们所期待的一样,两个碎片平分了整个 Activity 的布局。不过这个例子实在是太简单了,在真正的项目中很难有什么实际的作用,因此我们马上来看一看,关于碎片更加高级的使用技巧。

2.2  动态添加碎片

        在上一节当中,你已经学会了在布局文件中添加碎片的方法,不过碎片真正的强大之处在于,它可以在程序运行时动态地添加到 Activity 当中。根据具体情况来动态地添加碎片,你就可以将程序界面定制得更加多样化。

        我们还是在上一节代码的基础上继续完善,新建 another_right_fragment.xml,代码如下所示:

  1. <LinearLayout xmlns:android=“http://schemas.android.com/apk/res/android”  
  2.     android:layout_width=“match_parent”  
  3.     android:layout_height=“match_parent”  
  4.     android:background=“#ffff00”  
  5.     android:orientation=“vertical” >  
  6.       
  7.     <TextView   
  8.         android:layout_width=“wrap_content”  
  9.         android:layout_height=“wrap_content”  
  10.         android:layout_gravity=“center_horizontal”  
  11.         android:textSize=“20sp”  
  12.         android:text=“This is another right fragment”  
  13.         />  
  14.       
  15. </LinearLayout>  
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#ffff00"
    android:orientation="vertical" >

    <TextView 
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:textSize="20sp"
        android:text="This is another right fragment"
        />

</LinearLayout>

        这个布局文件的代码和 right_fragment.xml 中的代码基本相同,只是将背景色改成了黄色,并将显示的文字改了改。然后新建 AnotherRightFragment 作为另一个右侧碎片,代码如下所示:

  1. public class AnotherRightFragment extends Fragment {  
  2.       
  3.     @Override  
  4.     public View onCreateView(LayoutInflater inflater, ViewGroup container,  
  5.             Bundle savedInstanceState) {  
  6.         View view = inflater.inflate(R.layout.another_right_fragment, container, false);  
  7.         return view;  
  8.     }  
  9.       
  10. }  
public class AnotherRightFragment extends Fragment {

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.another_right_fragment, container, false);
        return view;
    }

}

        代码同样非常简单,在 onCreateView() 方法中加载了刚刚创建的 another_right_fragment 布局。这样我们就准备好了另一个碎片,接下来看一下如何将它动态地添加到活动当中。修改 activity_main.xml,代码如下所示:

  1. <LinearLayout xmlns:android=“http://schemas.android.com/apk/res/android”  
  2.     android:layout_width=“match_parent”  
  3.     android:layout_height=“match_parent” >  
  4.   
  5.     <fragment  
  6.         android:id=“@+id/left_fragment”  
  7.         android:name=“com.example.fragmenttest.LeftFragment”  
  8.         android:layout_width=“0dp”  
  9.         android:layout_height=“match_parent”   
  10.         android:layout_weight=“1”/>  
  11.       
  12.     <FrameLayout   
  13.         android:id=“@+id/right_layout”  
  14.         android:layout_width=“0dp”  
  15.         android:layout_height=“match_parent”  
  16.         android:layout_weight=“1”>  
  17.         
  18.         <fragment  
  19.         android:id=“@+id/right_fragment”  
  20.         android:name=“com.example.fragmenttest.RightFragment”  
  21.         android:layout_width=“match_parent”  
  22.         android:layout_height=“match_parent” />  
  23.           
  24.     </FrameLayout>  
  25.   
  26. </LinearLayout>  
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <fragment
        android:id="@+id/left_fragment"
        android:name="com.example.fragmenttest.LeftFragment"
        android:layout_width="0dp"
        android:layout_height="match_parent" 
        android:layout_weight="1"/>

    <FrameLayout 
        android:id="@+id/right_layout"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1">

        <fragment
        android:id="@+id/right_fragment"
        android:name="com.example.fragmenttest.RightFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

    </FrameLayout>

</LinearLayout>

        可以看到,现在将右侧碎片放在了一个 FrameLayout 中,还记得这个布局吗?在上一章中我们学过,这是 Android 中最简单的一种布局,它没有任何的定位方式,所有的控件都会摆放在布局的左上角。由于这里仅需要在布局里放入一个碎片,因此非常适合使用 FrameLayout。

        之后我们将在代码中替换 FrameLayout 里的内容,从而实现动态添加碎片的功能。修改 MainActivity 中的代码,如下所示:

  1. public class MainActivity extends Activity implements OnClickListener {  
  2.   
  3.     @Override  
  4.     protected void onCreate(Bundle savedInstanceState) {  
  5.         super.onCreate(savedInstanceState);  
  6.         setContentView(R.layout.activity_main);  
  7.         Button button = (Button) findViewById(R.id.button);  
  8.         button.setOnClickListener(this);  
  9.     }  
  10.   
  11.     @Override  
  12.     public void onClick(View v) {  
  13.         switch (v.getId()) {  
  14.         case R.id.button:  
  15.             AnotherRightFragment fragment = new AnotherRightFragment();  
  16.             FragmentManager fragmentManager = getFragmentManager();  
  17.             FragmentTransaction transaction = fragmentManager  
  18.                     .beginTransaction();  
  19.             transaction.replace(R.id.right_layout, fragment);  
  20.             transaction.commit();  
  21.             break;  
  22.         default:  
  23.             break;  
  24.         }  
  25.     }  
  26.   
  27. }  
public class MainActivity extends Activity implements OnClickListener {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button button = (Button) findViewById(R.id.button);
        button.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
        case R.id.button:
            AnotherRightFragment fragment = new AnotherRightFragment();
            FragmentManager fragmentManager = getFragmentManager();
            FragmentTransaction transaction = fragmentManager
                    .beginTransaction();
            transaction.replace(R.id.right_layout, fragment);
            transaction.commit();
            break;
        default:
            break;
        }
    }

}

        可以看到,首先我们给左侧碎片中的按钮注册了一个点击事件,然后动态添加碎片的逻辑都放在了点击事件里进行。结合代码可以看出,动态添加碎片主要分为 5 步。

  1.   创建待添加的碎片示例。
  2.   获取到 FragmentManager,在 Activity 中可以直接调用 getFragmentManager() 方法得到。
  3.   开启一个事务,通过调用 beginTransaction() 方法开启。
  4.   向容器内加入碎片,一般使用 replace() 方法实现,需要传入容器的 id 和待添加的碎片示例。
  5.   提交事务,调用 commit() 方法来完成。

        这样就完成了在 Activity 中动态添加碎片的功能,重新运行程序,可以看到和之前相同的界面,然后点击一下按钮,效果如图 4.7 所示。

图 4.7

2.3  在碎片中模拟返回栈

        在上一小节中,我们成功实现了向 Activity 中动态添加碎片的功能,不过你尝试一下就会发现,通过点击按钮添加一个碎片之后,这是按下 Back 键就会直接退出。如果这里我们想模仿类似于返回栈的效果,按下 Back 键可以回到上一个碎片,该如何实现呢?

        其实很简单,FragmentTransaction 中提供了一个 addToBackStack() 方法,可以用于将一个事务添加到返回栈中,修改 MainActivity 中的代码,如下所示:

  1. public class MainActivity extends Activity implements OnClickListener {  
  2.     ……  
  3.   
  4.     @Override  
  5.     public void onClick(View v) {  
  6.         switch (v.getId()) {  
  7.         case R.id.button:  
  8.             AnotherRightFragment fragment = new AnotherRightFragment();  
  9.             FragmentManager fragmentManager = getFragmentManager();  
  10.             FragmentTransaction transaction = fragmentManager  
  11.                     .beginTransaction();  
  12.             transaction.replace(R.id.right_layout, fragment);  
  13.             transaction.addToBackStack(null);  
  14.             transaction.commit();  
  15.             break;  
  16.         default:  
  17.             break;  
  18.         }  
  19.     }  
  20.   
  21. }  
public class MainActivity extends Activity implements OnClickListener {
    ......

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
        case R.id.button:
            AnotherRightFragment fragment = new AnotherRightFragment();
            FragmentManager fragmentManager = getFragmentManager();
            FragmentTransaction transaction = fragmentManager
                    .beginTransaction();
            transaction.replace(R.id.right_layout, fragment);
            transaction.addToBackStack(null);
            transaction.commit();
            break;
        default:
            break;
        }
    }

}

        这里我们在事务提交之前调用了 FragmentTransaction 的 addToBackStack() 方法,它可以接收一个名字用于描述返回栈的状态,一般传入 null 即可。现在重新运行程序,并点击按钮将 AnotherRightFragment 添加到 Activity 中,然后按下 Back 键,你会发现程序并没有退出,而是回到了 RightFragment 界面,再次按下 Back 键程序才会退出。

2.4  碎片和 Activity 之间进行通信

        虽然碎片都是嵌入在 Activity 中显示的,可是实际上它们的关系并没有那么亲密。你可以看出,碎片和 Activity 都是各自存在于一个独立的类当中的,它们之间并没有那么明显的方式来直接进行通信。如果想要在 Activity 中调用碎片的方法,或者再碎片中调用 Activity 里的方法,应该如何实现呢?

        为了方便碎片和 Activity 之间进行通信,FragmentManager 提供了一个类似于 findViewById() 的方法,专门用于从布局文件中获取碎片的实例,代码如下所示:

  1. RightFragment rightFragment = (RightFragment)getFragmentManager().findFragmentById(R.id.right_fragment);  
RightFragment rightFragment = (RightFragment)getFragmentManager().findFragmentById(R.id.right_fragment);

        调用 FragmentManager 的 findFragmentById() 方法,可以在 Activity 中得到相应碎片的实例,然后就能轻松地调用碎片里的方法了。

        掌握了如何在 Activity 中调用碎片里的方法,那在碎片中又该怎样调用 Activity 里的方法呢?其实这就更简单了,在每个碎片中都可以通过调用 getActivity() 方法来得到和当前碎片相关联的 Activity 实例,代码如下所示:

  1. MainActivity activity = (MainActivity) getActivity();  
MainActivity activity = (MainActivity) getActivity();

        有了 Activity 实例之后,在碎片中调用 Activity 里的方法就变得轻而易举了。另外当碎片中需要使用 Context 对象时,也可以使用 getActivity() 方法,因为获取到的 Activity 本身就是一个 Context 对象了。

        这是不知道你心中会不会产生一个疑问,既然碎片和 Activity 之间的通信问题已经解决了,那么碎片和碎片之间可不可以进行通信呢?

        说实在的,这个问题并没有看上去的复杂,它的基本思路非常简单,首先在一个碎片中可以得到与它相关联的 Activity,然后再通过这个 Activity 去获取另外一个碎片的实例,这样也就实现了不同碎片之间的通信功能,因此这里我们的答案是肯定的。

3.  动态加载布局的技巧

        虽然动态添加碎片的功能很强大,可以解决很多实际开发中的问题,但是它毕竟只是在一个布局文件中进行一些添加和替换操作。如果程序能够根据设备的分辨率或屏幕大小在运行时来决定加载哪个布局,那我们可发挥的空间就更多了。

3.1  使用限定符

        如果你经常使用平板电脑,应该会发现很多的平板应用现在都采用的是双页模式(程序会在左侧的面板上显示一个包含子项的列表,在坐车的面板上显示内容),因为平板电脑的屏幕足够大,完全可以同时显示下两页的内容,但手机的屏幕一次就只能显示一页的内容,因此两个页面需要分开显示。

        那么怎样才能在运行时判断程序应该是使用双页模式还是单页模式呢?这就需要借助限定符(Qualifiers)来实现了。我们通过一个例子来学习一下它的用法,修改 FragmentTest 项目中的 activity_main.xml 文件,代码如下所示:

  1. <LinearLayout xmlns:android=“http://schemas.android.com/apk/res/android”  
  2.     android:layout_width=“match_parent”  
  3.     android:layout_height=“match_parent” >  
  4.   
  5.     <fragment  
  6.         android:id=“@+id/left_fragment”  
  7.         android:name=“com.example.fragmenttest.LeftFragment”  
  8.         android:layout_width=“match_parent”  
  9.         android:layout_height=“match_parent” />  
  10.   
  11. </LinearLayout>  
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <fragment
        android:id="@+id/left_fragment"
        android:name="com.example.fragmenttest.LeftFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</LinearLayout>

        这里将多余的代码都删掉,只留下一个左侧碎片,并让它充满整个父布局。接着在 res 目录下新建 layout-large 文件夹,在这个文件夹下新建一个布局,也叫做 activity_main.xml,代码如下所示:

  1. <LinearLayout xmlns:android=“http://schemas.android.com/apk/res/android”  
  2.     android:layout_width=“match_parent”  
  3.     android:layout_height=“match_parent” >  
  4.   
  5.     <fragment  
  6.         android:id=“@+id/left_fragment”  
  7.         android:name=“com.example.fragmenttest.LeftFragment”  
  8.         android:layout_width=“0dp”  
  9.         android:layout_height=“match_parent”  
  10.         android:layout_weight=“1” />  
  11.   
  12.     <fragment  
  13.         android:id=“@+id/right_fragment”  
  14.         android:name=“com.example.fragmenttest.RightFragment”  
  15.         android:layout_width=“0dp”  
  16.         android:layout_height=“match_parent”  
  17.         android:layout_weight=“3” />  
  18.   
  19. </LinearLayout>  
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <fragment
        android:id="@+id/left_fragment"
        android:name="com.example.fragmenttest.LeftFragment"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1" />

    <fragment
        android:id="@+id/right_fragment"
        android:name="com.example.fragmenttest.RightFragment"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="3" />

</LinearLayout>

        可以看到,layout/activity_main 布局只包含了一个碎片,即单页模式,而 layout-large/activity_main 布局包含了两个碎片,即双页模式。其中 large 就是一个限定符,那些屏幕被认为是 large 的设备就会自动加载 layout-large 文件夹下的布局,而小屏幕的设备则还是会加载 layout 文件夹下的布局。

        然后将 MainActivity 中按钮点击事件的代码屏蔽掉,并在平板模拟器上重新运行程序,效果如图 4.13 所示。

图 4.13

        再启动一个手机模拟器,并在这个模拟器上重新运行程序,效果如图 4.14 所示。

图 4.14

        这样我们就实现了在程序运行时动态加载布局的功能。

        Android 中一些常见的限定符可以参考下表。


屏幕特征限定符描述
大小 small 提供给小屏幕设备的资源
 normal 提供给中等屏幕设备的资源
 large 提供给大屏幕设备的资源
 xlarge 提供给超大屏幕设备的资源
分辨率 ldpi 提供给低分辨率设备的资源 ( 120 dpi 以下)
 mdpi 提供给中等分辨率设备的资源 ( 120 dpi 到 160 dpi )
 hdpi 提供给高分辨率设备的资源 ( 160 dpi 到 40 dpi )
 xhdpi 提供给超高分辨率设备的资源 ( 240 dpi 到 320 dpi )
方向 land 提供给横屏设备的资源
 port 提供给竖屏设备的资源

3.2  使用最小宽度限定符

        在上一小节中我们使用 large 限定符成功解决了单页双页的判断问题,不过很快又有一个新的问题出现了,large 到底是指多大呢?有的时候我们希望可以更加灵活地位不同设备加载布局,不管它们是不是被系统认定为 “large”,这时就可以使用最小宽度限定符(Smallest-width Qualifier)了。

        最小宽度限定符允许我们队屏幕的宽度指定一个最小值(以 dp 为单位),然后以这个最小值为临界点,屏幕宽度大于这个值的设备就加载一个布局,屏幕小于这个值的设备就加载另一个布局。

        在 res 目录下新建 layout-sw600dp 文件夹,然后在这个文件夹新建 activity_main.xml 布局,代码如下所示:

  1. <LinearLayout xmlns:android=“http://schemas.android.com/apk/res/android”  
  2.     android:layout_width=“match_parent”  
  3.     android:layout_height=“match_parent” >  
  4.   
  5.     <fragment  
  6.         android:id=“@+id/left_fragment”  
  7.         android:name=“com.example.fragmenttest.LeftFragment”  
  8.         android:layout_width=“0dp”  
  9.         android:layout_height=“match_parent”  
  10.         android:layout_weight=“1” />  
  11.   
  12.     <fragment  
  13.         android:id=“@+id/right_fragment”  
  14.         android:name=“com.example.fragmenttest.RightFragment”  
  15.         android:layout_width=“0dp”  
  16.         android:layout_height=“match_parent”  
  17.         android:layout_weight=“3” />  
  18.   
  19. </LinearLayout>  
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <fragment
        android:id="@+id/left_fragment"
        android:name="com.example.fragmenttest.LeftFragment"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1" />

    <fragment
        android:id="@+id/right_fragment"
        android:name="com.example.fragmenttest.RightFragment"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="3" />

</LinearLayout>

        这就意味着,当程序运行在屏幕宽度大于 600 dp 的设备上时,会加载 layout-sw600dp/activity_main 布局,当程序运行在屏幕宽度小于 600 dp 的设备上时,则仍然加载默认的 layout/activity_main 布局。

        需要注意一点,最小宽度限定符是在 Android 3.2 版本引入的,由于这里我们最低兼容的系统版本是 4.0,所以可以放心地使用它。

摘自《第一行代码》

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值