Android学习笔记十一.fragment(三) 之《兼容多分辨率的应用》实战

来源:互联网 发布:淘宝新店访客多少正常 编辑:程序博客网 时间:2024/05/16 01:47

转载请表明出处:http://blog.csdn.net/u012637501(嵌入式_小J的天空)
    在上一篇博文中介绍了如何使用Android Fragment开发适用于大屏幕应用,现在我们在上一个应用的基础上继续学习如何使用Fragment开发兼容多分辨率的应用。

1.建立/res/values-large/refs.xml引用资源文件
    为了开发兼顾屏幕分辨率的应用,我们需要建立一个引用资源文件,专门用于定义各种引用项。refs.xml引用资源文件中定义了一项引用,其作用就是标明activity_book_list实际引用(@)/res/layout/activity_book_twopane.xml界面布局文件。
    之所以这样使用,目的是为了在Activity加载R.layout.activity_book_list时将会根据运行平台的屏幕大小自动选择界面布局文件:在大屏幕的平板电脑上,R.layout.activity_book_list将会变成/res/layout/目录小的activity_book_twopane界面布局文件;在小屏幕的手机上,R.layout.activity_book_list依然引用/res/layout/目录下的activity_book_list.xml界面布局文件。源码如下:
<?xml version="1.0" encoding="utf-8"?><resources> <!--定义activity_book_list实际引用@layout/activity_book_twopane资源 --> <item type="layout" name="activity_book_list">     @layout/activity_book_twopane</item></resources>
2.建立/res/layout/activity_book_list.xml小屏幕界面布局文件
    该布局文件仅仅是显示BookListFragment组件(android:name="包名.BookListFragment"),即该界面布局文件中只是显示图书列表。源码如下:
<?xml version="1.0" encoding="utf-8"?><fragment xmlns:android="http://schemas.android.com/apk/res/android"    android:name="com.example.android_fragment_1.BookListFragment"    android:id="@+id/book_list"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:layout_marginRight="16dp"    android:layout_marginLeft="16dp"/>
3.建立/res/layout/activity_book_twopane.xml大屏幕界面布局文件
    这个布局文件就定义了Activity的显示界面左边将会显示一个ListFragment,右边只是一个FrameLayout容器。其中FrameLayout容器将会动态更新其中显示的Fragment
<?xml version="1.0" encoding="utf-8"?><!-- 定义一个水平排列的LinearLayout,并指定使用中等分隔条 --><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"                android:layout_width="match_parent"                android:layout_height="match_parent"                android:orientation="horizontal"                android:layout_marginLeft="16dp"                android:layout_marginRight="16dp"                android:divider="?android:attr/dividerHorizontal"                android:showDividers="middle"><!-- 使用资源文件方式:添加一个fragment到Activity中 --><fragmentandroid:name="com.example.android_fragment_1.BookListFragment"android:id="@+id/book_list"android:layout_height="match_parent"android:layout_width="0dp"android:layout_weight="1"/><!-- 添加一个FrameLayout容器,用于显示图书详细信息 --><FrameLayoutandroid:id="@+id/book_detail_container"android:layout_width="0dp"android:layout_height="match_parent"android:layout_weight="3"/></LinearLayout>

4.建立/src/../BookListActivity.java主Activity   
    实现BookListActivity将会针对不同屏幕分辨率来加载不同的界面布局文件,即通过获取界面资源文件"activity_book_list",判定该界面布局文件中是否包含有ID为book_detail_containner的组件。如果有,则说明为大屏幕使用的是activity_book_twopane.xml界面布局文件;否则,为activity_book_list.xml界面布局文件。源码如下:
package com.example.android_fragment_1;import android.app.Activity;import android.content.Intent;import android.os.Bundle;public class BookListActivity extends Activity implements BookListFragment.Callbacks{ //1.定义一个旗标,用于标识是否该应用是否支持大屏幕 private boolean mTwoPane; @Override protected void onCreate(Bundle savedInstanceState) {  super.onCreate(savedInstanceState); //2.指定加载activity_book_list对应的界面布局文件,实际上会根据分辨率选择相应的布局界面  setContentView(R.layout.activity_book_list); //3.如果加载的界面布局文件中包含ID为book_detail_container的组件,则说明支持大屏幕  if(findViewById(R.id.book_detail_container)!=null)  {   mTwoPane=true; //支持大屏幕   ((BookListFragment)getFragmentManager()     .findFragmentById(R.id.book_list))     .setActivateOnItemClick(true);  }  } //4.实现Fragment与Activity交互方法 @Override public void onItemSelected(Integer id) {  /*如果加载的是大屏幕布局界面文件*/  if(mTwoPane)  {   //a.创建Bundle,准备向Fragment传入参数(将键值对绑定到arguments对象中)   Bundle arguments=new Bundle();   arguments.putInt(BookDetailFragment.ITEM_ID,id);   //b.创建BookDetailFragment对象,并向Fragment传入参数   BookDetailFragment fragment=new BookDetailFragment();   fragment.setArguments(arguments);   //c.使用fragment替换book_detail_container容器当前显示的Fragment   getFragmentManager().beginTransaction()      .replace(R.id.book_detail_container, fragment).commit();  }  /*如果加载的是小屏幕布局界面文件*/  else  {   //a.创建启动BookDetailActivity的Intent   Intent detailIntent=new Intent(this,BookDetailActivity.class);   //b.设置传递给BookDetailActivity参数,并启动Activity   detailIntent.putExtra(BookDetailFragment.ITEM_ID, id);   startActivity(detailIntent);     } }}

5.建立/src/../BookDetailActivity.java显示图书详情Activity
    BookDetailActivity主要用于显示小屏幕图书详情,他将直接复用已有的BookDetailFragment。主要完成两个功能:一是当用户当即Activity某个列表项时,会Intent跳转到该Activity并将打包的某图书详情显示在Activity上;二是,该Activity还启用了ActionBar上的应用程序图标,允许用户点击该图标返回到程序的主Activity。
package com.example.android_fragment_1;import android.app.Activity;import android.content.Intent;import android.os.Bundle;import android.view.MenuItem;public class BookDetailActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) {  // TODO Auto-generated method stub  super.onCreate(savedInstanceState); //1.指定加载/res/layout目录下的activity_book_detail.xml布局文件  setContentView(R.layout.activity_book_detail); //2.将ActionBar上应用图标转换成可点击的按钮  getActionBar().setDisplayHomeAsUpEnabled(true);  if(savedInstanceState==null)  {   //a.创建BookDetailFragment对象   BookDetailFragment fragment=new BookDetailFragment();   //b.创建Bundle对象   Bundle arguments=new Bundle();   arguments.putInt(BookDetailFragment.ITEM_ID, getIntent()     .getIntExtra(BookDetailFragment.ITEM_ID,0));   //c.向Fragment传入参数   fragment.setArguments(arguments);   //d.指定fragment添加到book_detail_container容器中   getFragmentManager().beginTransaction()     .add(R.id.book_detail_container, fragment).commit();  } } //3.按下图标响应 @Override public boolean onOptionsItemSelected(MenuItem item) {  // TODO Auto-generated method stub  if(item.getItemId()==android.R.id.home)  {   //a.创建启动BookListActivity的Intent   Intent intent=new Intent(this,BookListActivity.class);   //b.添加额外的Flag,将Activity栈中处于FirstActivity之上的Activity弹出   intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);   //c.启动intent对应的Activity   startActivity(intent);   return true;  }  return super.onOptionsItemSelected(item);  }  /res/layout目录下的activity_book_detail.xml布局文件    该界面布局文件内只定义了一个ID名为book_detail_container的FrameLayout,用于显示图书详情。 <?xml version="1.0" encoding="utf-8"?><FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:id="@+id/book_detail_container" ></FrameLayout>

6.建立/src/../BookDetailFragment.java显示图书详情Fragment
    当为大屏幕时,此时BookListFragment和BookDetailFragment就起作用了,前者为列表Fragment,后者为包含两个文本框组件的Fragment。另外,主Activity加载的是activity_book_
twopane.xml界面布局文件。
     onItemSelected()传入的参数id是列表的被选中的行ID, BookDetailFragment 用这个ID来从程序的ContentProvider中取得标题的内容。  
(1)实现onCreate(Bundle savedInstanceState)方法:
    获取启动该Fragment时传入的ITEM_ID参数并根据该ID获取BookContent的ITEM_MAP中的图书信息
(2)实现onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)方法:
    a.为图书详情Fragment加载一个界面布局文件,为两个文本框;
    b.根据传入的参数ID来更新View容器,使文本框显示不同的内容;
package com.example.android_fragment_1;import android.app.Fragment;import android.os.Bundle;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.TextView;/*功能: * 使用Fragment将会显示加载一份简单的界面布局文件,并根据传入的参数来更新 * 界面组件*/public class BookDetailFragment extends Fragment { public static final String ITEM_ID="item_id"; BookContent.Book book; //保存该Fragment显示的book对象  @Override public void onCreate(Bundle savedInstanceState) {  super.onCreate(savedInstanceState);  //如果启动该Fragment时包含了ITEM_ID参数,其中 Map containsKey(String Key) 判断key有没有对应的value值; 有,则返回true 没有,则返回false  //从Bundle对象中获取传入的参数(键值对)  if(getArguments().containsKey(ITEM_ID))  {       book=BookContent.ITEM_MAP.get(getArguments().getInt(ITEM_ID));   //获取启动该Fragment时传入的ITEM_ID参数并根据该ID获取BookContent的ITEM_MAP中的图书信息?  } } //2.重写该方法:该方法返回的View将作为Fragment显示的组件 @Override public View onCreateView(LayoutInflater inflater, ViewGroup container,   Bundle savedInstanceState) {  //a.加载/res/layout/目录下的fragment_book_detail.xml布局文件,返回一个view目的使该fragment的布局显示在Activity中  View view=inflater.inflate(R.layout.fragment_book_detail, //指明当前fragment的资源文件         container, //父容器控件         false); //表明是否连接该布局和其父容器控件(这里系统已经插入了布局到父容器中)  /*将图书信息中的标题、属性显示在容器的两个文本框中*/  if(book!=null)  {  //b.让book_title文本框显示book对象的title属性   ((TextView) view.findViewById(R.id.book_title)).setText(book.title);  //c.让book_desc文本框显示book对象的desc属性   ((TextView) view.findViewById(R.id.book_desc)).setText(book.desc);  }  return view; } }

其中界面资源布局文件为/res/layout/fragment_book_detail.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >
 <!-- 定义一个TextView来显示图书标题 -->
 <TextView
     style="?android:attr/textAppearanceLarge"
     android:id="@+id/book_title"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
     android:padding="16dp"/>
 <!-- 定义一个TextView来显示图书描述 -->
 <TextView
     style="?android:attr/textAppearanceLarge"
     android:id="@+id/book_desc"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:padding="16dp"/>
</LinearLayout>
7.建立/src/../BookListFragment.java显示图书详情Fragment
  自定义类,继承于ListFragment,无需实现OnCreateView()方法,用于Activity右边显示列表fragment。
(1)定义Callbacks接口:定义一个回调接口Callback,用于实现该Fragment与它所在的Activity交互;
(2)实现onCreate(Bundle savedInstanceState)方法:通过Adapter所提供的多个列表项,设置Fragment列表显示的列表项;
(3)实现onAttach(Activity activity)方法:将Fragment添加并显示到Acitvity中,并将传入的activity对象强制类型转化为 Callbacks接口对象,以便调用接口公共方法onItemSelected(Integer id)响应用户单击的某列表项;
(4)实现ListFragment的onListItemClick(ListView l, View v, int position, long id)方法:
    当用户点击Acitivity中的某项列表时,onListItemClick方法被激发。在这个方法中调用接口的onItemSelected来与activity共享事件。onItemSelected()传入的参数id是列表的被选中的行ID,另一个fragment(B)( BookDetailFragment )用这个ID来从程序的ContentProvider中取得标题的内容。
package com.example.android_fragment_1;import android.app.Activity;import android.app.ListFragment;import android.os.Bundle;import android.view.View;import android.widget.ArrayAdapter;import android.widget.ListView;public class BookListFragment extends ListFragment {  //1.定义一个回调接口Callback,用于实现该Fragment与它所在的Activity交互(注意:该接口的实现需要在Activit中) private Callbacks mCallbacks;//Callbacks接口对象 public interface Callbacks {  public void onItemSelected(Integer id);//参数为Map集合的键 }  //2.onCreate方法中为该ListFragment设置Adapter,让该ListFragment显示该Adapter所提供的多个列表项 @Override public void onCreate(Bundle savedInstanceState) {  // TODO Auto-generated method stub  super.onCreate(savedInstanceState);  setListAdapter(new ArrayAdapter<BookContent.Book>(getActivity(),//    android.R.layout.simple_list_item_activated_1,    android.R.id.text1,    BookContent.ITEMS)); // }  //3.调用该方法将Fragment添加并显示到Acitvity中 @Override public void onAttach(Activity activity) {  // TODO Auto-generated method stub  super.onAttach(activity);  //a.如果Activity中没有实现Callbacks接口,抛出异常  if(!(activity instanceof Callbacks))  {   throw new IllegalStateException("异常:BookListFragment所在的Activity必须实现Callback接口!");  }  //把该Activity当成Callbacks对象(就是这一句导致出现NullPointerException错误)        mCallbacks=(Callbacks)activity; }  //4.当该Fragment从他所属的Acitivity中被删除时,调用该方法 @Override public void onDetach() {  super.onDetach();  mCallbacks=null;//将mCallbacks赋值为null }  //5.当用户单击某列表项时激发该回调方法 @Override public void onListItemClick(ListView l, View v, int position, long id) {  super.onListItemClick(l, v, position, id);  mCallbacks.onItemSelected(BookContent.ITEMS.get(position).id);//激发mCallbacks接口的onItemSelected方法 }public void setActivateOnItemClick(boolean activateOnItemClick) {getListView().setChoiceMode(activateOnItemClick ? ListView.CHOICE_MODE_SINGLE:ListView.CHOICE_MODE_NONE); }}
需要注意的是,不像BookDetailFragment,由于继承于ListFragment,无需实现OnCreateView()方法。因此,无需定义界面布局文件。
8.res/../BookContent.java:
    用于模拟系统的数据模型
 (1)List集合为左边fragment提供图书(标题)列表项数据
    public static List<Book> ITEMS=new ArrayList<Book>();
对于List集合来说,book为(1,"疯狂Android讲义","个人评价:这本书很好,就是有点厚!"2...3...)
 (2)Map集合为右边fragment提供图书详情(标题、属性)数据
    public static Map<Integer,Book> ITEM_MAP =new HashMap<Integer,Book>();
对应Map集合来说,键book.id<--->值(1.......2.......3......)
package com.example.android_fragment_1;import java.util.ArrayList;import java.util.HashMap;import java.util.List;import java.util.Map;public class BookContent { //1.定义一个内部类,作为系统的业务对象 public static class Book {  public Integer id; //Map键  public String title;  public String desc;  public Book(Integer id,String title,String desc)//构造函数,初始化图书详情的"map键,标题,内容"  {   this.id=id;   this.title=title;   this.desc=desc;  }  public String toString()  {   return title;  } } //2.使用List集合记录系统所包含的标题字符串的ITEMS,利用它调用其add(对象)方法项List集合中添加列表项 public static List<String> ITEMS=new ArrayList<String>(); //3.使用Map集合记录系统ITEM_MAP,利用它调用put(Key,Value)方法向Map集合中添加列表项 public static Map<Integer,String> ITEM_MAP =new HashMap<Integer,String>(); static{   //使用静态初始化代码,将Book对象添加到List集合、Map集合中   addItem(new Book(1,"疯狂Android讲义","个人评价:这本书很好,就是有点厚!"));   addItem(new Book(2,"数学之美","个人评价:来自自然语音的使者"));   addItem(new Book(3,"大话数据结构","个人评价:不知道怎样,听说很不错")); } private static void addItem(Book book) {  // TODO Auto-generated method stub  ITEMS.add(title); //添加List集合中列表项  ITEM_MAP.put(id,desc);//添加Map集合中列表项 }}

/*注释:
 * static{},称为static代码块,也叫静态代码块。是在类中独立于类成员的static语句块,可以有多个且位置可以随便放。
 * 它不属于任何的方法体内,JVM加载类时会执行这些静态的代码块,如果有多个则按先后顺序执行且每个代码块智会被执行依次。
 * 比如,我们可以利用静态代码块可以对一些statci变量进行赋值*/
9.修改AndroidManifest.xml
    由于在源码中实现了一个BookDetailActivity,所以我们需要在工程文件中声明它。
  
  <application         .......>            <activity            android:name=".BookListActivity"            android:label="@string/app_name" >                <intent-filter>                <action android:name="android.intent.action.MAIN" />                <category android:name="android.intent.category.LAUNCHER" />                </intent-filter>            </activity>             <activity            android:name=".BookDetailActivity"            android:label="@string/app_name" >            </activity>    </application></manifest>

小屏幕显示效果:

大屏幕显示效果:
1 0