传送门 ☞ 轮子的专栏 ☞ 转载请注明 ☞ http://blog.csdn.net/leverage_1229
为了在Android创建一个动态的多面的用户界面,你需要封装UI组件和activity的行为到一种可以相互交换的act的模块中。我们能使用Fragment类创建这些模块,这行为有点像一个嵌套的act,它可以定义自己的布局和管理自己的生命周期。Fragment的好处已经越发明显,它是Android3.0新增的API。当一个fragment指定它的布局,它能以不同的组合配置到act中,为不同的屏幕大小修改你的布局配置,一个小屏幕可能只显示一个fragment,而在大屏幕中可能显示2个或2个以上的fragment。本章说明怎样使用fragment创建动态的用户体验并为不同屏幕大小的设备优化用户体验,同时继续支持例如Android1.6这样的老版本。
使用Android支持的库
通过绑定Android支持库,学习怎样使用近期新版本的APIs;
创建一个Fragment
学习怎样创建fragment并实现一些基本行为;
构建一个灵活的UI
学习怎样为不同的屏幕提供不同的fragment配置布局;
与其他Fragment通信
学习如何为fragment设置通信路径来与启动fragment或Activity通信。
<sdk>/extras/android/support/v4/android-support-v4.jar(4)更新你的manifest文件设置最小API Level为4,目标API Level为15(截止此刻最新的系统版本4.0.3)
<uses-sdk android:minSdkVersion="4" android:targetSdkVersion="15" />
import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; ...
import android.os.Bundle; import android.support.v4.app.Fragment; import android.view.LayoutInflater; import android.view.ViewGroup; public class ArticleFragment extends Fragment { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { // 为这个fragment填充布局 return inflater.inflate(R.layout.article_view, container, false); } }就像activity一样,一个fragment也应该实现其他声明周期的回调方法,以允许你管理状态(例如从activity中添加或删除),例如,当act调用onPause()时,在act中的任意fragments也将接收到onPause()回调。关于fragment更详细的使用方法,会在API框架中讲解。
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal" android:layout_width="fill_parent" android:layout_height="fill_parent"> <fragment android:name="com.example.android.fragments.HeadlinesFragment" android:id="@+id/headlines_fragment" android:layout_weight="1" android:layout_width="0dp" android:layout_height="match_parent" /> <fragment android:name="com.example.android.fragments.ArticleFragment" android:id="@+id/article_fragment" android:layout_weight="2" android:layout_width="0dp" android:layout_height="match_parent" /> </LinearLayout>
以下是在代码中如何使用以上的layout xml文件:
import android.os.Bundle; import android.support.v4.app.FragmentActivity; public class MainActivity extends FragmentActivity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.news_articles); } }
注意:当你通过xml来定义fargment时,在运行时是不能移除的。下一小节我们会讲解如何动态与用户交互。
当你为了支持广泛的屏幕大小而设计你的App时,你能在不同的布局配置中重用你的fragments以优化各种屏幕大小的用户体验。例如,某个时刻在手机上显示一个fragment,相反的在平板中由于屏幕更大可能显示更多的fragment。
FragmentManager类提供添加、删除、替换fragment的方法,可以在Activity运行时的使用,以创建一个动态的用户体验。<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/fragment_container" android:layout_width="match_parent" android:layout_height="match_parent" />在我们的activity中,使用支持库APIs调用getSupportFragmentManager()来获得一个FragmentManger。然后调用beginTransaction()创建一个FragmentTransaction以通过它的add()方法来添加一个fragment。你能在act中使用同一个FragmentTransaction执行多个fragment处理事务。当你确定改变时,需要调用commit()来提交我们的操作。
import android.os.Bundle; import android.support.v4.app.FragmentActivity; public class MainActivity extends FragmentActivity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.news_articles); //检查FrameLayout是否为空 if (findViewById(R.id.fragment_container) != null) { // 如果我们从先前的状态恢复,那么我们不需要做任何事,直接返回 if (savedInstanceState != null) { return; } //创建一个HeadlinesFragment对象,光盘资源中有此类的实现 HeadlinesFragment firstFragment = new HeadlinesFragment(); // 给fragment设置一个Intent's extras参数 firstFragment.setArguments(getIntent().getExtras()); //添加fragment到'fragment_container'(FrameLayout) getSupportFragmentManager().beginTransaction() .add(R.id.fragment_container, firstFragment).commit(); } } }因为fragment在运行时被添加到FrameLayout,而不是我们6.2中使用layout XML文件把fragment写到<fragment>中,所以我们可以动态添加,移除,替换fragment。
// 创建fragment并给他一个参数用于确定选择文章的位置 ArticleFragment newFragment = new ArticleFragment(); Bundle args = new Bundle(); args.putInt(ArticleFragment.ARG_POSITION, position); newFragment.setArguments(args); FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); //我们需要把文章标题Fragment替换为文章内容Fragment // 我们添加了addToBackStack(),表示用户向后导航的时候fragment不会被destroyed transaction.replace(R.id.fragment_container, newFragment); transaction.addToBackStack(null); // 提交 transaction.commit();如果在addToBackStack()中传入null,表示你可能更高级的操作,例如使用FragmentManager.BackStackEntry。
public class HeadlinesFragment extends ListFragment { OnHeadlineSelectedListener mCallback; // 容器Activity必须实现这个接口,用来传递消息 public interface OnHeadlineSelectedListener { public void onArticleSelected(int position); } @Override public void onAttach(Activity activity) { super.onAttach(activity); // 确保容器Activity已经实现回调接口,如果没有则会抛出一个异常 try { mCallback = (OnHeadlineSelectedListener) activity; } catch (ClassCastException e) { throw new ClassCastException(activity.toString() + " must implement OnHeadlineSelectedListener"); } } ... }现在fragment能通过调用Activity中的onArticleSelected() 方法来传递消息了。等会我们将展示实现的代码。
@Override public void onListItemClick(ListView l, View v, int position, long id) { //通知父类activity选择的item mCallback.onArticleSelected(position); }
public static class MainActivity extends Activity implements HeadlinesFragment.OnHeadlineSelectedListener{ ... public void onArticleSelected(Uri articleUri) { //用户从HeadlinesFragment选择文章的标题 // do something } }
public static class MainActivity extends Activity implements HeadlinesFragment.OnHeadlineSelectedListener{ ... public void onArticleSelected(int position) { //用户从HeadlinesFragment选择文章的标题 //从 res/layout-large/获得文章 fragment ArticleFragment articleFrag = (ArticleFragment) getSupportFragmentManager().findFragmentById(R.id.article_fragment); if (articleFrag != null) { //如果articleFrag不为空,我们正在使用的是双面板布局(平板设备) //调用 ArticleFragment的这个方法来更新内容 articleFrag.updateArticleView(position); } else { //否则,我们使用的是单面板(手机设备) //创建fragment并给他传入一个参数用于确定选择文章的位置 ArticleFragment newFragment = new ArticleFragment(); Bundle args = new Bundle(); args.putInt(ArticleFragment.ARG_POSITION, position); newFragment.setArguments(args); FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); //我们需要把文章标题Fragment替换为文章内容Fragment //表示用户可以后退导航 transaction.replace(R.id.fragment_container, newFragment); transaction.addToBackStack(null); //最后需要使用transaction提交 transaction.commit(); } } }