0


安卓Fragment使用详解

 Fragment

一、Fragment的概念和用法:

(一)、概念:

    Fragment是在Android 3.0 (API level 11)开始引入新的API技术。

    为了提高代码重用性和改善用户体验,我们将Activity中的UI组件进行分组和模块化管理。这些分组后的UI组件就是Fragment。

    一个Activity页面中可以包含多个Fragment模块,而同一个Fragment模块也可以被多个Activity使用。每个Fragment有自己的布局,有自己的生命周期。虽然Fragment拥有自己的生命周期,但因为Fragment必须被嵌入到Activity中使用,因此Fragment的生命周期是受其Activity宿主的生命周期所控制的。当Activity暂停时,该Activtiy内的所有Fragment都会暂停;当Activity被销毁时,该Activity内的所有Fragment都会被销毁。

(二)、Fragment为什么能改善用户体验,另外Fragment能用于平板电脑,那么普通的手机屏幕适用吗?

   下图是一个“AndroidManual学习手册浏览界面”的效果图:

    如上图所示,“平板电脑”中,在一个Activity布局中放了两个Fragment:左侧是文章标题列表Fragment,右侧是文章内容展示的Fragment。左侧的标题列表不变,点击每条列表项,右侧的文章内容发生变化。点击和浏览内容都在同一个页面中发生,避免了页面切换和点击“返回键”返回的操作。用户感觉自然方便和快捷。

    那么Fragment组件适用于普通屏幕的手机吗?答案是肯定的。Fragment本身是可复用的组件。是否在一个Activity页面中放置多个Fragment取决了屏幕的大小,如果屏幕大小不够,那么就可以在Activity A中只包含Fragment A,在Activity B中只包含Fragment B,点击A中的item跳转到B就可以。

(三)、Fragment要点:【 重点 】

1、Fragment作为Activity界面的一部分组成出现;

2、可以在一个Activity中同时出现多个Fragment,并且一个Fragment亦可在多个Activity中使用;

3、在Activity运行过程中,可以添加、移除或者替换Fragment(add()、remove()、replace());

4、Fragment可以响应自己的输入事件,并且有自己的生命周期,当然,它们的生命周期直接受其所属的宿主Activity的生命周期控制。

二、Fragment生命周期:

(一)、Fragment基本状态:

1、活动状态:Resumed   当前Fragment位于前台,用户可见,可以获得焦点;

2、暂停状态:  Paused   另一个Activity处于前台并拥有焦点, 但是该Fragment所在的Activity仍然可见(前台Activity局部透明或者没有覆盖整个屏幕),不过不能获得焦点;

3、停止状态:Stopped
  • 要么是宿主Activity已经被停止, 要么是Fragment从Activity被移除但被添加到回退栈中;

  • 停止状态的Fragment仍然活着(所有状态和成员信息被系统保持着)。 然而, 它对用户不再可见, 并且如果Activity被销毁,它也会被销毁;

    4、销毁状态:Destroyed 只能等待被回收。

(二)、Fragment生命周期:【重点】

1、onAttach(): 当该Fragment被添加到Activity时被回调。该方法只会被调用一次;

2、onCreate(): 当创建Fragment时被回调。该方法只会被调用一次;

3、onCreateView():每次创建、绘制该Fragment的View组件时回调该方法,Fragment将会显示该方法返回的View 组件;

4、onActivityCreated(): 当Fragment的宿主Activity被启动完成后回调该方法;//单独的

5、onStart(): 启动Fragment时被回调;

6、onResume(): onStart()方法后一定会回调onResume()方法;

7、onPause(): 暂停Fragment时被回调;

8、onStop(): 停止Fragment时被回调;

9、onDestroyView(): 销毁该Fragment所包含的View组件时调用;

10、onDestroy(): 销毁Fragment时被回调。该方法只会被调用一次;

11、onDetach(): 将Fragment从Activity中删除、替换完成时调用该方法。onDestroy()方法后一定会回调onDetach()方法。该方法只会被调用一次。

12、onInflate():

13、onViewCreated():

(三)、示例代码:

1、Fragment的布局文件:

<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:orientation="vertical">
<TextView
android:id="@+id/text_leftfragment_info"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
android:text="这是leftFragment中的textview"/>
</LinearLayout>

2、MainActivity的布局文件:

<RelativeLayoutxmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
 android:layout_width="match_parent"
 android:layout_height="match_parent">
<fragment
android:id="@+id/fragment_left_newstitle"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
android:name="com.steven.android24_fragmentlifecycle.LeftFragment"/>
</RelativeLayout>

3、Fragment文件中的java代码:

publicclass LeftFragment extends Fragment {
privatestaticfinal String TAG = "LeftFragment";
 @Override
publicvoid onInflate(Activity activity, AttributeSet attrs, Bundle savedInstanceState) {
 Log.i (TAG, "==onInflate()执行了");
super.onInflate(activity, attrs, savedInstanceState);
 }
 @Override
publicvoid onAttach(Activity activity) {
 Log.i (TAG, "==onAttach()执行了");
super.onAttach(activity);
 }
 @Override
publicvoid onCreate(Bundle savedInstanceState) {
 Log.i (TAG, "==onCreate()执行了");
super.onCreate(savedInstanceState);
 }
 @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {
 Log.i (TAG, "==onCreateView()执行了");
 // inflater.inflate(resource, null);
return inflater.inflate(R.layout.fragment_left, container, false);
 }
 @Override
publicvoid onViewCreated(View view, Bundle savedInstanceState) {
 Log.i (TAG, "==onViewCreated()执行了");
super.onViewCreated(view, savedInstanceState);
 }
 @Override
publicvoid onActivityCreated(Bundle savedInstanceState) {
 Log.i (TAG, "==onActivityCreated()执行了");
super.onActivityCreated(savedInstanceState);
 }
 @Override
publicvoid onStart() {
 Log.i (TAG, "==onStart()执行了");
super.onStart();
 }
 @Override
publicvoid onResume() {
 Log.i (TAG, "==onResume()执行了");
super.onResume();
 }
 @Override
publicvoid onPause() {
 Log.i (TAG, "==onPause()执行了");
super.onPause();
 }
 @Override
publicvoid onStop() {
 Log.i (TAG, "==onStop()执行了");
super.onStop();
 }
 @Override
publicvoid onDestroyView() {
 Log.i (TAG, "==onDestroyView()执行了");
super.onDestroyView();
 }
 @Override
publicvoid onDestroy() {
 Log.i (TAG, "==onDestroy()执行了");
super.onDestroy();
 }
 @Override
publicvoid onDetach() {
 Log.i (TAG, "==onDetach()执行了");
super.onDetach();
 }
}

(四)、运行结果观察:

1、第一次加载Fragment:

2、在MainActivity中,点击“返回键”退出程序:

3、在MainActivity中,点击“HOME键”退出程序:

【备注:】请注意第二种和第三种情况的区别。如果点HOME键退出,则少调用了onDestroyView()、onDestroy()、onDetach()三个回调方法。

4、点击“下一页”,进入NextActivity页面:

5、在NextActivity页面中,点击“HOME键”退出程序:

6、上次HOME键退出后,重新进入程序:【因为上次是从NextActivity页面按HOME退出,所以此时直接进入 NextActivity页面 】

(五)、跟Fragment生命周期相关的其他方法:

1、onInflate():在onCreate()方法之前调用,这时窗体上的控件都没有被创建,所以不能通过getActivity().findViewById(),因为此时getActivity()返回null。

2、onViewCreated():在onCreateView()方法后会立刻调用onViewCreated()方法。通常在该方法中完成创建Fragment的最后工作,然后系统就开始调用onCreate()方法对窗体初始化。

(六)、如果Activity生命周期和Fragment生命周期的Log都开启,那么依次会如何输出信息呢?

1、第一次加载MainActivity页面:

2、在MainActivity页面中,点击“返回键”退出程序:

3、在MainActivity中,点击“HOME键”退出程序:

【备注:】请同学们注意观察Activity回调方法和Fragment回调方法执行的顺序。请设想各种操作步骤,写出生命周期回调方法执行的顺序。

【特别强调:】

当Activity的onCreate()方法中,如果将Log的位置放在setContentView(R.layout.activity_main)之前,那么Activity的onCreate()方法就会在最先执行,如果将日志写在setContentView(R.layout.activity_main)之后,那么Activity的onCreate()方法就会在Fragment的onViewCreated()之后执行。

4、分析生命周期执行过程的案例:

开启一个包含有Fragment的Activity页面,然后点击“返回键”退出,然后再次启动该Activity页面,请描述完整的生命周期回调方法。

参考答案:

 04:36:31.282: MainActivity: ==onCreate()
 04:36:31.502: LeftFragment: ==onInflate()执行了
 04:36:31.502: LeftFragment: ==onAttach()执行了
 04:36:31.513: LeftFragment: ==onCreate()执行了
 04:36:31.513: LeftFragment: ==onCreateView()执行了
 04:36:31.533: LeftFragment: ==onViewCreated()执行了
 04:36:31.542: LeftFragment: ==onActivityCreated()执行了
 04:36:31.542: MainActivity: ==onStart()
 04:36:31.552: LeftFragment: ==onStart()执行了
 04:36:31.563: MainActivity: ==onResume()
 04:36:31.563: LeftFragment: ==onResume()执行了
 04:37:20.133: LeftFragment: ==onPause()执行了
 04:37:20.142: MainActivity: ==onPause()
 04:37:21.932: LeftFragment: ==onStop()执行了
 04:37:21.932: MainActivity: ==onStop()
 04:37:21.942: LeftFragment: ==onDestroyView()执行了
 04:37:21.952: LeftFragment: ==onDestroy()执行了
 04:37:21.952: LeftFragment: ==onDetach()执行了
 04:37:21.952: MainActivity: ==onDestroy()
 04:39:20.763: MainActivity: ==onCreate()
 04:39:21.552: LeftFragment: ==onInflate()执行了
 04:39:21.635: LeftFragment: ==onAttach()执行了
 04:39:21.635: LeftFragment: ==onCreate()执行了
 04:39:21.652: LeftFragment: ==onCreateView()执行了
 04:39:21.652: LeftFragment: ==onViewCreated()执行了
 04:39:21.842: LeftFragment: ==onActivityCreated()执行了
 04:39:21.842: MainActivity: ==onStart()
 04:39:21.908: LeftFragment: ==onStart()执行了
 04:39:21.912: MainActivity: ==onResume()
 04:39:21.932: LeftFragment: ==onResume()执行了

三、创建Fragment:

(一)、创建Fragment的步骤:【只需要两步】

1、创建一个Fragment,必须继承Fragment 这个基类或其子类;

    【备注:】除了继承基类 Fragment , 还有一些子类你可能会继承,是哪些子类呢?
  • DialogFragment
  • 显示一个浮动的对话框. 用这个类来创建一个对话框,是使用在Activity类的对话框工具方法之外的一个好的选择,因为你可以将一个fragment对话框合并到activity管理的fragment back stack中,允许用户返回到一个之前曾被摒弃的fragment.
  • ListFragment
  • 显示一个由一个adapter(例如 SimpleCursorAdapter)管理的项目的列表, 类似于ListActivity。它提供一些方法来管理一个list view, 例如 onListItemClick()回调来处理点击事件.
  • PreferenceFragment
  • 显示一个 Preference对象的层次结构的列表, 类似于PreferenceActivity。这在为你的应用创建一个"设置"activity时有用处.

2、实现回调方法:

    Fragment类的代码看起来很像 Activity 。它包含了和activity类似的回调方法, 例如onCreate()、 onStart()、onPause()以及 onStop()。事实上,,如果你准备将一个现成的Android应用转换到使用Fragment,可能只需要将代码从你的Activity的回调方法中分别移动到你的Fragment的回调方法即可。

    通常,应当至少实现如下的生命周期方法:
  • onCreate() 当创建fragment时, 系统调用该方法. 在实现代码中,应当初始化想要在fragment中保持的必要组件, 当fragment被暂停或者停止后可以恢复.

  • onCreateView() fragment第一次绘制它的用户界面的时候, 系统会调用此方法. 为了绘制fragment的UI,此方法必须返回一个View, 这个view是你的fragment布局的根view. 如果fragment不提供UI, 可以返回null.

  • onPause() 用户将要离开fragment时,系统调用这个方法作为第一个指示(然而它不总是意味着fragment将被销毁.) 在当前用户会话结束之前,通常应当在这里提交任何应该持久化的变化(因为用户有可能不会返回).

    【备注:】对于大部分Fragment而言,通常实现三个回调方法即可。但是实际开发中可以根据需要重写Fragment生命周期中的任意回调方法。

(二)、Fragment实例:

1、描述:以“ 影视介绍 ”为例,无需页面切换,点击左侧影视名称,在右侧区域显示影视内容介绍。

2、主页面布局及Fragment布局文件代码:

//activity_main.xml布局文件:
<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">
<fragment
android:id="@+id/fragment_left_title"
         android:layout_width="0dp"
         android:layout_height="fill_parent"
         android:layout_weight="1"
class="com.steven.android24_fragmentmovie.LeftFragment"/>
<fragment
android:id="@+id/fragment_right_detail"
       android:layout_width="0dp"
               android:layout_height="fill_parent"
       android:layout_weight="3"
class="com.steven.android24_fragmentmovie.RightFragment"/>
</LinearLayout>
//LeftFragment的布局文件:
<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">
<ListView
android:id="@+id/listView_fragment_left"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
</ListView>
</LinearLayout>
//RightFragment的布局文件:
<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">
<TextView
android:id="@+id/text_fragment_right"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="请点击左侧列表"/>
</LinearLayout>

【备注:】

  • <fragment>标签的android:id 和 android:tag属性必须至少有一个,同一个xml文件中id或tag必须唯一;
  • <fragment>标签的class 和 android:name属性功能完全相同,使用其中任何一个即可。

3、LeftFragment核心代码:

publicclass LeftFragment extends Fragment {
private ListView listView_fragment_left;
private String[] arr_data = new String[] { "扫毒", " 功夫战斗机", "顶楼的大象", "狂爱恶徒", "兽性窥视", "新
金瓶梅" };
private TextView text_fragment_right;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
 View view = inflater.inflate(R.layout.fragment_left, null);
 listView_fragment_left = (ListView) view .findViewById(R.id.listView_fragment_left);
 ArrayAdapter<String> adapter = new ArrayAdapter<String>(getActivity(),
android.R.layout.simple_list_item_1, arr_data);
listView_fragment_left.setAdapter(adapter);
 listView_fragment_left.setOnItemClickListener(new OnItemClickListener() {
 @Override
publicvoid onItemClick(AdapterView<?> parent, View view,int position, long id) {
                                            InputStream is;

                                              ByteArrayOutputStream baos =  new  ByteArrayOutputStream();

                                              String data =  "" ;
  try  {
 
                                             is = getActivity().getResources().getAssets() .open( "movie"  + position +  ".txt" );
   byte [] buffer =  new byte [1024];
  
   int  c = 0;
  
   while  ((c = is.read(buffer)) != -1) {
  
                                                       baos.write(buffer, 0, c);
                                                       baos.flush(); 

                                               }
                                             data =  new  String(baos.toByteArray(),  "utf-8" );
                                       }  catch  (IOException e) {
                                                  e.printStackTrace();
                                       }


                                        TextView text_fragment_right = (TextView) 
   getActivity() .findViewById(R.id. text_fragment_right );
  
   if  (text_fragment_right !=  null ) {
 
    text_fragment_right.setText(data);
  
                                              }  else  {
                                                     Intent intent =  new  Intent(getActivity(), DetailActivity. class );
                                                     intent.putExtra( "detail" , data);
                                                     startActivity(intent); 

                                              } 

                                      } 
 });
return view;
 }
}

【备注:】本例子中的数据源是assets目录下的文本文件。通过本例,也复习了assets资产文件的访问方式。

4、平板和手机适配:

如果想在平板和手机中显示不同的效果。则可以新建布局目录:layout-sw600dp.

三、动态创建Fragment:

(一)、概念:

如果将Fragment写在布局文件中,那么就是静态创建fragment;如果没有在布局文件中写<fragment>标签,而是在java文件中通过实例化Fragment创建Fragment的方式就是动态创建Fragment。

(二)、动态创建Fragment的步骤:

1、步骤:

2、Activity布局文件代码:

<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical">
<Button
android:id="@+id/button_main_submit"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="clickButton"
android:text="点击发送消息"/>
    <!--  fragment
        android:id="@+id/fragment_content"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:name="com.steven.android24_fragmentarguments.DetailFragment" /-->
<LinearLayout
android:id="@+id/layout_container_fragment"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">
</LinearLayout>
</LinearLayout>

3、MainActivity文件代码:

FragmentManager manager = getFragmentManager();
DetailFragment fragment = new DetailFragment();
FragmentTransaction transaction = manager.beginTransaction();
transaction.add(R.id.layout_container_fragment, fragment, "content_fragment");
transaction.commit();

四、Activity与Fragment之间的数据交互:

(一)、Activity向Fragment传递数据:Arguments

1、做法:

    可以通过Fragment.setArguments()方法向Fragment传递数据,并且通过getArguments()方法获取传递的数据。

2、MainActivity文件代码:

publicclass MainActivity extends Activity {
privatestaticfinal String TAG = "MainActivity";
  @Override
protectedvoid onCreate(Bundle savedInstanceState) {
             super .onCreate(savedInstanceState); 

            setContentView(R.layout. activity_main );

            Log. i ( TAG , "==onCreate()执行");

            FragmentManager manager = getFragmentManager();

            DetailFragment fragment =  new  DetailFragment();
                Bundle bundle = new Bundle();
        bundle.putString("msg", "Hello Fragment!!!");
            fragment.setArguments(bundle);

            FragmentTransaction transaction = manager.beginTransaction();

            transaction.add(R.id.layout_container_fragment, fragment,  "content_fragment");

            transaction.commit();
     }
}

3、DetailFragment文件代码:

publicclass DetailFragment extends Fragment {
private TextView text_fragment_detail;
private Button button_fragment_get;
      @Override
publicvoid onAttach(Activity activity) {
super.onAttach(activity);
            Log.i("DetailFragment", "==onAttach()");
    }
      @Override
publicvoid onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
    }
      @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {
            View view = inflater.inflate(R.layout.fragment_detail, container, false);
text_fragment_detail = (TextView) view.findViewById(R.id.text_fragment_detail);
            button_fragment_get = (Button) view.findViewById(R.id.button_fragment_get);
            Bundle bundle = getArguments();
if (bundle != null) {
text_fragment_detail.setText(bundle.getString("msg"));
            } else {
                 text_fragment_detail.setText("没有收到信息!");
            }
return view;
    }
}

4、注意事项:

  • Supply the construction arguments for this fragment. This can only be called before the fragment has been attached to its activity; that is, you should call it immediately after constructing the fragment. The arguments supplied here will be retained across fragment destroy and creation.
  • 翻译:setArguments(bundle)为fragment提供构造的参数。它只能在fragment回调onAttach()方法之前调用。就是说,你应该在构造完fragment之后立刻调用它。提供的这些参数将在Fragment被销毁前一直保留。
  • 在布局文件中使用<fragment>标签声明的Fragment(也就是静态生成的Fragment),都不能使用setArguments方法设置Bundle对象。

(二)、Fragment向宿主Activity回传信息:(Fragment回调机制)

1、原则:

Fragment类要尽量保证其独立性,Fragment类中不应该有访问其他Fragment和Activity中资源的代码,否则这个Fragment就不能在不改动代码的情况下用在其他地方。

如何让多个Fragment之间可以独立多次使用,而不是紧密地绑定到一起?通常的做法就是在Fragment类中编写一个接口,然后在该Fragment的宿主窗口类中实现该接口。这样Fragment与其宿主就实现了信息交互。

2、Fragment接口回调的步骤:(五步曲)

  • 在Fragment文件中定义接口:OnItemClickedListener,定义抽象方法onClick(String info);
  • 在Fragment文件中定义属性:private OnItemClickedListener mylistener;
  • 在Fragment文件中的onAttach()方法中执行:mylistener = (OnItemClickedListener) getActivity();
  • 在Fragment文件中,给某个控件增加监听器,在监听器中执行:mylistener.onClick(“需要传递的数据”);
  • 将MainActivity实现OnItemClickedListener,重写onClick(String info)方法。参数就是Fragment传递的数据信息,在该方法中执行希望的逻辑操作。

3、DetailFragment的核心代码:

publicclass DetailFragment extends Fragment {
private TextView text_fragment_detail;
private Button button_fragment_get;
private Button button_fragment_send;
private OnItemClickedListener mylistener;
publicinterface OnItemClickedListener {
publicvoid onClick(String info);
  }
 @Override
publicvoid onAttach(Activity activity) {
super.onAttach(activity);
          Log.i ("DetailFragment", "==onAttach()");
if (getActivity() instanceof OnItemClickedListener) {
mylistener = (OnItemClickedListener) getActivity();
          }
  }
 @Override
publicvoid onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
  }
 @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {
          View view = inflater.inflate(R.layout.fragment_detail, container, false);
text_fragment_detail = (TextView) view .findViewById(R.id.text_fragment_detail);
          button_fragment_get = (Button) view.findViewById(R.id.button_fragment_get);
          button_fragment_send = (Button) view.findViewById(R.id.button_fragment_send);
button_fragment_get.setOnClickListener(new OnClickListener() {
                 @Override
publicvoid onClick(View v) {
                          Bundle bundle = getArguments();
if (bundle != null) {
text_fragment_detail.setText(bundle.getString("msg"));
                          } else {
                                    text_fragment_detail.setText("没有收到信息!");
                          }
                  }
          });
button_fragment_send.setOnClickListener(new OnClickListener() {
                 @Override
publicvoid onClick(View v) {
mylistener.onClick(“我是fragment传递给宿主的信息!");
                  }
          });
return view;
  }
}

4、MainActivity的核心代码:

publicclass MainActivity extends Activity implements OnItemClickedListener {
privatestaticfinal String TAG = "MainActivity";
 @Override
protectedvoid onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_main);
 Log.i (TAG, "==onCreate()执行");
 FragmentManager manager = getFragmentManager();
 DetailFragment fragment = new DetailFragment();
 Bundle bundle = new Bundle();
  bundle.putString("msg", "Hello Fragment!!!");
 fragment.setArguments(bundle);
 FragmentTransaction transaction = manager.beginTransaction();
 transaction.add(R.id.layout_container_fragment, fragment,"content_fragment");
 transaction.commit();
 }
 @Override
publicvoid onClick(String info) {
 // 拿到Fragment传递过来的数据,显示在Title上。
 // 实际项目中,我们是将Fragment_A传给宿主的信息拿到后,再通过setArguments()传递给Fragment_B。
    //这样就实现了两个Fragment之间的数据传递。
 setTitle(info);
 }
}

五、FragmentManager与Fragment事务: 【重要】

(一)、概念:

    Activity管理Fragment主要依靠FragmentManager。FragmentManager可以完成以下几方面的功    能:
    1. 使用findFragmentById()或findFragmentByTag()方法来获取指定Fragment;2. 调用popBackStack()方法将Fragment从回退栈中弹出(如同用户按下“返回键”的效果);3. 调用addOnBackStackChangeListener()注册一个监听器,用于监听回退栈的变化情况。

      在Activity中使用Fragment,一个很明显的特性是:根据用户的交互情况,可以对Fragment进行添加、移除、替换,以及执行其他动作,提交给Activity的每一套变化被称为一个事务。Fragment事务代表了Activity对Fragment执行的多个改变操作。实现事务借助于FragmentTransaction对象。我们可以保存每一个事务到一个Activity管理的backstack,这样用户就能由Fragment的变化往回导航。

【思考:】

    不加事务会怎么样呢?

    如果不加事务,那么当点击“返回键”,页面会返回上一个页面。对于本例来说,只有一个MainActivity页面,所以一旦点击“返回键”则立刻退出程序,而用户实际上希望返回上次查看的文章。如果加上事务,则用户可以返回到每次查看过的文章。

(二)、Fragment与回退栈(导航):

1、回退栈的概念:BackStack

在Activity中已经探讨过回退栈,这种数据结构用来存放创建的窗口对象,并根据一定的规则决定哪些窗口对象应该出栈,凡是出栈的窗口对象将被销毁,也就是说窗口对象从入栈到出栈完成了窗口的整个生命周期。回退栈不仅能存储窗口对象,还可以存储Fragment对象。

(三)、FragmentManager与FragmentTransaction示例代码:

Fragment的布局文件:

// 构建Bundle对象,将文章id放在其中。
Bundle bundle = new Bundle();
bundle.putString("titleId", id);
// 构建文章内容的Fragment
RightFragment fragment = new RightFragment();
// 通过fragment对象的setArguments(bundle)方法将数据传输给其他Fragment。
fragment.setArguments(bundle);
// 通过getFragmentManager()方法构建FragmentManager对象
FragmentManager fragManager = getFragmentManager();
// 通过FragmentManager对象的beginTransaction()方法构建FragmentTransaction对象
FragmentTransaction trans = fragManager.beginTransaction();
// 将主页面布局文件中的ViewGroup容器替换成文章内容Fragment
trans.replace(R.id.fragment_content, fragment);
trans.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
// 将事务添加到回退栈,这样用户就可以通过“返回键”回到替换Fragment的上一个状态
trans.addToBackStack(null);
// 提交事务
trans.commit();

六、完善后的“影视信息展示”:

(一)、核心代码:

1、LeftFragment核心代码:

publicclass LeftFragment extends Fragment {
private String[] arrMovieNames = new String[] { "扫毒", " 功夫战斗机", "顶楼的大象",
"狂爱恶徒", "兽性窥视", "新金瓶梅" };
private ArrayAdapter<String> adapter = null;
private ListView listView_leftfragment_moviename;
private OnItemClickedListener mylistener = null;
publicinterface OnItemClickedListener {
publicvoid onClick(String info);
   }
   @Override
publicvoid onAttach(Activity activity) {
super.onAttach(activity);
if (getActivity() instanceof OnItemClickedListener) {
mylistener = (OnItemClickedListener) getActivity();
           }
   }
   @Override
publicvoid onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
adapter = new ArrayAdapter<String>(getActivity(), android.R.layout.simple_list_item_1, arrMovie
   Names);
   }
   @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {
          View view = inflater.inflate(R.layout.fragment_left, container, false);
          listView_leftfragment_moviename = (ListView) view.findViewById(R.id.listView_leftfragment_moviename);
          listView_leftfragment_moviename.setAdapter(adapter);
          listView_leftfragment_moviename.setOnItemClickListener(new OnItemClickListener() {
          @Override
publicvoid onItemClick(AdapterView<?> parent, View view,int position, long id) {
try {
                       ByteArrayOutputStream baos =  new  ByteArrayOutputStream();
                       BufferedInputStream bis =  new  BufferedInputStream(
                       getActivity().getResources().getAssets() .open( "movie"  + position +  ".txt" ));
  byte [] buffer =  new byte [1024 * 8];
  int  c = 0;
  while  ((c = bis.read(buffer)) != -1) {
                           baos.write(buffer, 0, c);
                           baos.flush();
                }
                String data =  new  String(baos.toByteArray(), "utf-8" );
                mylistener .onClick(data);
        }  catch  (IOException e) {
                   e.printStackTrace();
         }
} });
return view;
   }
}

2、RightFragment核心代码:

publicclass RightFragment extends Fragment {
private TextView text_rightfragment_detail;
           @Override
publicvoid onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
   }
          @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceSt
   ate) {
           View view = inflater.inflate(R.layout.fragment_right, container, false);
           text_rightfragment_detail = (TextView) view .findViewById(R.id.text_rightfragment_detail);
           Bundle bundle = getArguments();
if (bundle != null) {
text_rightfragment_detail.setText(bundle.getString("detail"));
           } else {
                  text_rightfragment_detail.setText("没有数据!");
           }
return view;
   }
}
    3、MainActivity核心代码:
publicclass MainActivity extends FragmentActivity implements OnItemClickedListener {
   @Override
protectedvoid onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
           setContentView(R.layout.activity_main);
   }
       @Override
publicboolean onCreateOptionsMenu(Menu menu) {
           getMenuInflater().inflate(R.menu.main, menu);
returntrue;
   }
  @Override
publicvoid onClick(String info) {
           FragmentManager manager = getSupportFragmentManager();
           RightFragment fragment = new RightFragment();
           Bundle bundle = new Bundle();
           bundle.putString("detail", info);
           fragment.setArguments(bundle);
           FragmentTransaction transaction = manager.beginTransaction();
           transaction.replace(R.id.layout_container_detail, fragment,"detailFragment");
           transaction.addToBackStack(null);
           transaction.commit();
   }
   @Override
protectedvoid onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
   }
}
4、MainActivity主窗体布局的核心代码:
<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"
 android:layout_width="fill_parent"
 android:layout_height="fill_parent">
<fragment
android:id="@+id/fragment_left"
 android:layout_width="0dp"
 android:layout_weight="1"
 android:layout_height="wrap_content"
android:name="com.steven.android22.fragmentmoviev2.LeftFragment"/>
<LinearLayout
android:id="@+id/layout_container_detail"
 android:layout_width="0dp"
 android:layout_weight="3"
 android:layout_height="wrap_content"
 android:orientation="horizontal"
/>
</LinearLayout>

七、利用Fragment来实现自适应屏幕的例子:

(一)、实现效果:

    • 当竖屏的时候,展示为普通列表,点击进入下一个页面查看详细内容;- 当横屏的时候,显示为左右结构。点击左侧列表,直接在右侧显示详细内容。

(二)、Android屏幕适配之——sw<n>dp:

    1. 在android3.2以前,所有的资源文件都有相应的xhdpi,hdpi,mdpi,ldpi四种文件来对应,android3.2以后,为了提供更精准的对布局文件的控制,可以通过为资源文件(res目录下文件)增加后缀来指定该文件夹里的xml布局文件或color.xml,string.xml是为哪种大小的屏幕使用。 2. 第一种后缀:sw<N>dp,如layout-sw600dp, values-sw600dp

第一种,smallwidth:

这里的sw代表 smallwidth 的意思,当你所有屏幕的最小宽度都大于600dp时,屏幕就会自动到带sw600dp后缀的资源文件里去寻找相关资源文件,这里的最小宽度是指屏幕宽高的较小值,每个屏幕都是固定的,不会随着屏幕横向纵向改变而改变。

第二种后缀w<N>dp 如layout-w600dp, values-w600dp :

带这样后缀的资源文件的资源文件制定了屏幕宽度的大于Ndp的情况下使用该资源文件,但它和sw<N>dp不同的是,当屏幕横向纵向切换时,屏幕的宽度是变化的,以变化后的宽度来与N相比,看是否使用此资源文件下的资源。

第三种后缀h<N>dp 如layout-h600dp, values-h600dp :

这个后缀的使用方式和w<N>dp一样,随着屏幕横纵向的变化,屏幕高度也会变化,根据变化后的高度值来判断是否使用h<N>dp ,但这种方式很少使用,因为屏幕在纵向上通常能够滚动导致长度变化,不像宽度那样基本固定,因为这个方法灵活性不是很好,google官方文档建议尽量少使用这种方式。

多语言目录:

    values-en    英文

values-zh 中文

values-zh-rCN 简体中文

values-zh-rTW 繁体中文

values-ja 日文

(三)、知识点回顾:sp、dp、dip、pt、px等单位的区别?

1. dpi    dpi指像素密度。dots per inch  ,即每英寸内像素点的个数。它不是表示长度的单位。

2.  在android中认为:低(120dpi),中( 160dpi ),高(240dpi),超高(320dpi)。随着技术的增长,实际dpi已经超出这个定义范围。

3.  dip    device independent pixels  ,即与设备无关的像素。目前这个单位已经被dp所取代,而不建议使用dip。

4.  dp     与dip的概念一样。不过dp已经取代了dip。在Android中用来表示非文字大小的尺寸。例如:外边距、内填充等。

5.

    1.  px = dp * (dpi / 160)

    2.  3.7寸屏幕,分辨率320*480手机上,正好1px = 1dp。

6.  sp      scale  independent  pixel  ,即与缩放比例无关的像素。在android中常用来表示文字大小。

7.  px      表示像素。因为同样是200px,但是在不同手机下显示的大小是不同的。

8.  pt      point磅。1磅=1/74英寸
1.  xlarge 屏幕至少:960dp x 720dp 
2.  large 屏幕至少 :640dp x 480dp 
3.  normal 屏幕至少 :470dp x 320dp 
4.  small 屏幕至少 :426dp x 320dp

(四)、核心代码:

publicclass LeftFragment extends Fragment {
private String[] arrTitle = new String[] { "TOP1:扫毒", "TOP2:功夫战斗机",
 "TOP3:顶楼的大象", "TOP4:狂爱恶徒", "TOP5:兽性窥视", "TOP6:新金瓶梅" };
private ListView listView_fragment_titlelist;
private OnItemClickedListener listener = null;
privateintwidth = 0;
publicinterface OnItemClickedListener {
publicvoid onClick(String data);
 }
 @Override
publicvoid onAttach(Activity activity) {
super.onAttach(activity);
listener = (OnItemClickedListener) getActivity();
 }
 @Override
publicvoid onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
 // int width = getActivity().getWindowManager().getDefaultDisplay().getWidth();
 // int height = getActivity().getWindowManager().getDefaultDisplay().getHeight();
 Point point = new Point();
 getActivity().getWindowManager().getDefaultDisplay().getSize(point);
width = point.x;
int height = point.y;
 getActivity().setTitle(width + ":" + height);
 }
 @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
 View view = inflater.inflate(R.layout.fragment_left, container, false);
 // inflater.inflate(resource, null);
 listView_fragment_titlelist = (ListView) view.findViewById(R.id.listView_fragment_left_titlelist);
 ArrayAdapter<String> adapter = new ArrayAdapter<String>(this.getActivity(), android.R.layout.simpl
 e_list_item_1,arrTitle);
          listView_fragment_titlelist.setAdapter(adapter);
          listView_fragment_titlelist.setOnItemClickListener(new OnItemClickListener() {
                     @SuppressWarnings("deprecation")
                     @Override
publicvoid onItemClick(AdapterView<?> parent, View view,int position, long id) {
                              String result = "";
try {
                                    InputStream is = getActivity().getResources().getAssets()
   .open("movie" + position + ".txt");
                                    ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = newbyte[8 * 1024];
int c = 0;
while ((c = is.read(buffer)) != -1) {
                                              baos.write(buffer, 0, c);
                                              baos.flush();
                                     }
                                    result = baos.toString();
                               } catch (IOException e) {
                                     e.printStackTrace();
                               }
if (width >= 600) {
listener.onClick(result);
                                   } else {
                                             Intent intent = new Intent();
                                             intent.setClass(getActivity(),ContentActivity.class);
                                             Bundle bundle = new Bundle();
                                             bundle.putString("content", result);
                                             intent.putExtras(bundle);
                                             startActivity(intent);
                                           }
                                   }
                           });
return view;
   }
}
标签: android ui

本文转载自: https://blog.csdn.net/liuliuhelingdao/article/details/127210550
版权归原作者 我是你的春哥! 所有, 如有侵权,请联系我们删除。

“安卓Fragment使用详解”的评论:

还没有评论