碎片(Fragment)是一种可以嵌入在活动当中的UI片段,它能让程序更加合理和充分地利用屏幕的空间。首先建立一个平板的模拟器1034*600,环境使用android4.2.2.在建立一个android的项目,项目名为FragmentTest。
碎片的简单使用
新建一个左侧碎片布局left_fragment.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" >
<Button
android:id="@+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="button 1"
/>
</LinearLayout>
这个布局值放置了一个按钮,并让它水平居中显示。然后在新建一个右侧布局right_fragment.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"
android:background="#00ff00"
>
<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。注意,这里可能会有2个不同的包下的Fragment供我们选择,建议使用android.app.Fragment,因为我们的程序是面向Android4.0以上系统的,另一个包下的Fragment主要是用于兼容低版本的Android系统。LeftFragment的代码如下所示:
package com.wj.fragmenttest;
import android.app.Fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
public class LeftFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// TODO Auto-generated method stub
View view=inflater.inflate(R.layout.left_fragment,container, false);
return view;
}
}
在新建一个RightFragment代码如下所示:
package com.wj.fragmenttest;
import android.app.Fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
public class RightFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// TODO Auto-generated method stub
View view=inflater.inflate(R.layout.right_fragment,container, false);
return view;
}
}
以上的代码比较的简单就不在多说了额。
接下来修改activity_main.xml,代码如下:
<LinearLayout xmlns: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/left_fragment"
android:name="com.wj.fragmenttest.LeftFragment"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
/>
<fragment
android:id="@+id/right_fragment"
android:name="com.wj.fragmenttest.RightFragment"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
/>
</LinearLayout>
上面的代码我们使用了fragment标签在布局中添加碎片。
运行程序,结果如下所示:
上面添加碎片是静态添加的,那么接下来就让我们来试试动态添加碎片吧。
新建一个布局文件,文件名为another_right_fragment.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"
android:background="#ffff00"
>
<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>
然后新建AnotherRightFragment作为另一个右侧碎片,代码如下:
package com.wj.fragmenttest;
import android.app.Fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
public class AnotherRightFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// TODO Auto-generated method stub
View view=inflater.inflate(R.layout.another_right_fragment,container, false);
return view;
}
}
接下来修改activity_main.xml文件,代码如下:
<LinearLayout xmlns: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/left_fragment"
android:name="com.wj.fragmenttest.LeftFragment"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
/>
<FrameLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:id="@+id/right_layout"
>
<fragment
android:id="@+id/right_fragment"
android:name="com.wj.fragmenttest.RightFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</FrameLayout>
</LinearLayout>
这里使用了FrameLayout布局方式,在之后的代码中我们将替换FrameLayout里的内容,从而实现动态添加碎片的功能。修改MainActivity中的代码:
package com.wj.fragmenttest;
import android.app.Activity;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.os.Bundle;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
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.button1);
button.setOnClickListener(this);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
switch(v.getId()){
case R.id.button1:
//创建碎片
AnotherRightFragment fragment=new AnotherRightFragment();
//获得布局管理器
FragmentManager fragmentManager=getFragmentManager();
//获得碎片的事物
FragmentTransaction transaction=fragmentManager.beginTransaction();
//替换id为R.id.right_layout里面的内容
transaction.replace(R.id.right_layout, fragment);
transaction.commit();//提交事务
break;
default:
break;
}
}
}
运行程序:
点击左边的按钮,结果如下:
结合代码可以看出,动态添加碎片主要分为5步。
1.创建待添加的碎片实例。
2.获取到FragmentManager,在活动中可以直接调用getFragmentManager()方法得到。
3.开启一个事物,通过调用beginTransaction()方法开启。
4.向容器内加入碎片,一般使用replace()方法实现,需要传入容器的id和待添加的碎片实例。
5.提交事务,调用commit()方法来完成。
在碎片中模拟返回栈
通过点击按钮添加了一个碎片之后,这时按下back键程序就会直接退出。如果这里我们想模仿类似返回栈的效果,按下back键可以返回到上一个碎片,该如何实现了,看如下操作:
其实比较的简单,在FragmentTransaction中提供了一个addToBackStack()方法,可以用于将一个事务添加到返回栈中,修改MainActivity中的代码如下:
package com.wj.fragmenttest;
import android.app.Activity;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.os.Bundle;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
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.button1);
button.setOnClickListener(this);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
switch(v.getId()){
case R.id.button1:
//创建碎片
AnotherRightFragment fragment=new AnotherRightFragment();
//获得布局管理器
FragmentManager fragmentManager=getFragmentManager();
//获得碎片的事物
FragmentTransaction transaction=fragmentManager.beginTransaction();
//替换id为R.id.right_layout里面的内容
transaction.replace(R.id.right_layout, fragment);
transaction.addToBackStack(null);
transaction.commit();//提交事务
break;
default:
break;
}
}
}
这里在事务提交之前调用了FragmentManager的addToBackStack()方法,它可以接收一个名字用于描述返回栈的状态,一般传入null即可。现在重写运行程序,并点击按钮将AnotherRightFragment添加到活动中,然后按下Back键,你会发现回到了RightFragment界面,再次按下Back键程序才会退出。
碎片和活动直接进行通信
为了方便碎片和活动直接进行通信,FragmentManager提供了一个类似与findViewById()的方法,专门用于从布局文件中获取碎片的实例,代码如下:
RightFragment rightFragment=(RightFragment) getFragmentManager().findFragmentById(R.id.right_fragment);
调用FragmentManager的findFragmentById方法,可以在活动中得到相应碎片的实例,然后就能轻松地调用碎片里的方法了。
掌握了如何在活动中调用碎片里的方法,那么在碎片中又该如何调用活动中的方法呢?
在每个碎片中都可以通过调用getActivity()方法来得到和当前碎片相关联的活动实例,代码如下:
MainActivity mainActivity=(MainActivity) getActivity();
有了活动的实例之后,在碎片中调用活动里的方法就变得轻而易举了。另外当碎片中需要使用Context对象时,也可以使用getActivity()方法,因为活动本身就是一个Context对象了。
碎片的生命周期
和活动一样,碎片也有自己的生命周期
运行状态:当一个碎片是可见的,并且它所关联的活动正处于运行状态,该碎片也处于运行状态。
暂停状态:当一个活动进入暂停状态时(由于另一个未占满屏幕的活动被添加到了栈顶),与它相关联的可见碎片就会进入到暂停状态。
停止状态:当一个活动进入停止状态时,与它相关联的碎片就会进入到停止状态。或者通过用FragmentTransaction的remove,replace方法将碎片从活动中移除,但有在事务提交之前调用addToBackStack()方法,这时的碎片也会进入到停止状态。总的来说停止状态的碎片对用户来说是完全不可见的,有可能会被回收。
销毁状态:碎片总是依附与活动而存在,因此当活动被撤销时,与它关联的碎片就会进入到销毁状态。或者通过调用FragmentTransaction的remove,replace方法将碎片从活动中移除,但在事务提交之前并没有调用addToBackStack()方法,这时的碎片也会进入到销毁状态。
几个重要的碎片回到方法:
1.onAttach():当碎片和活动建立关联的时候调用
2.onCreateView():为碎片创建视图(加载布局)时调用
3.onActivityCreated():确保与碎片相关联的活动一定已经创建完毕的时候调用
4.onDestroyView():当与碎片关联的视图被移除的时候调用。
5.onDetach():当碎片和活动解除关联的时候调用
下图是碎片的生命周期图:
修改RightFragment中的代码,代码如下:
package com.wj.fragmenttest;
import android.app.Activity;
import android.app.Fragment;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
public class RightFragment extends Fragment {
public static final String TAG="RightFragment";
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// TODO Auto-generated method stub
Log.d(TAG, "onCreateView");
View view=inflater.inflate(R.layout.right_fragment,container, false);
return view;
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onActivityCreated(savedInstanceState);
Log.d(TAG, "onActivityCreated");
}
@Override
public void onAttach(Activity activity) {
// TODO Auto-generated method stub
super.onAttach(activity);
Log.d(TAG, "onAttach");
}
@Override
public void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
Log.d(TAG, "onCreate");
}
@Override
public void onDestroy() {
// TODO Auto-generated method stub
super.onDestroy();
Log.d(TAG, "onDestroy");
}
@Override
public void onDestroyView() {
// TODO Auto-generated method stub
super.onDestroyView();
Log.d(TAG, "onDestroyView");
}
@Override
public void onDetach() {
// TODO Auto-generated method stub
super.onDetach();
Log.d(TAG, "onDetach()");
}
@Override
public void onPause() {
// TODO Auto-generated method stub
super.onPause();
Log.d(TAG, "onPause");
}
@Override
public void onResume() {
// TODO Auto-generated method stub
super.onResume();
Log.d(TAG, "onResume");
}
@Override
public void onStart() {
// TODO Auto-generated method stub
super.onStart();
Log.d(TAG, "onStart");
}
@Override
public void onStop() {
// TODO Auto-generated method stub
super.onStop();
Log.d(TAG, "onStop");
}
}
在回调方法中打印了日志,运行程序,当RightFragment第一次被加载到屏幕的时候,方法调用如下所示:
当第一被加载的时候,会依次调用onAttach,onCreate,onCreateView,onActivityCreated,onStart,onResume方法。
然后点击左边的按钮,输出如下所示的日志:
由于AnotherRightFragment替换了RightFragment,此时的RightFragment进入了停止状态,因此onPause,onStop,onDestroyView方法会得到执行。当然如果在替换的时候没有调用addToBackStack方法,此时的RightFragment就会进入到销毁状态,onDestroy,onDetach方法就会得到执行。
按下Back键,RightFragment会重写回到屏幕,打印如下信息:
由于RightFragment重新回到了运行状态,因此onActivityCreated,onStart,onResume方法会得到执行。注意,此时onCreate和onCreateView方法并不会执行,因为我们借助了addToBackStack方法使得RightFragment和它的视图并没有销毁,在按back键退出程序,打印信息如下:
依次执行了onPause,onStop,onDestroyView,onDestroy,onDetach方法。
另外,在碎片中也可以通过onSaveInstanceState方法保存数据的。因为进入停止状态的碎片有可能在系统内存不足的时候被回收。保存下来的数据在onCreat,onCreateView和onActivityCreate这三个方法中重写得到,他们都包含了一个Bundle类型的参数。
动态加载布局的技巧
动态加载布局的功能很强大,可以解决很多实践中的问题,但是它毕竟只是在一个布局文件中进行一些添加和替换操作。如果程序能够根据设备的分辨率或屏幕的大小在运行时决定加载哪个布局,那么我们就可以发挥更多的空间了。
1.使用限定符
修改FragmentTest项目中的activity_main.xml文件,代码如下:
<LinearLayout xmlns: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/left_fragment"
android:name="com.wj.fragmenttest.LeftFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</LinearLayout>
这里将多余的代码都删掉,值留下一个左侧碎片,并让它充满整个父布局。接着在res目录下新建layout-large文件夹,在这个文件夹下面新建一个布局,也叫activity_main.xml代码如下所示:
<LinearLayout xmlns: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/left_fragment"
android:name="com.wj.fragmenttest.LeftFragment"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
/>
<fragment
android:id="@+id/right_fragment"
android:name="com.wj.fragmenttest.RightFragment"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="3"
/>
</LinearLayout>
可以看到layout/activity_main.xml布局只包含了一个碎片,即单页模式,而layout-large/activity_main.xml布局包含了2个碎片,即双页模式。其中large就是一个限定符,那些屏幕被认定为是large的设备就会自动加载layout-large文件夹下的布局,而小屏幕的设备则还会加载layout文件夹下的布局。
将MainActivity中的按钮点击事件屏蔽掉,代码如下:
package com.wj.fragmenttest;
import android.app.Activity;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.os.Bundle;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
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.button1);
button.setOnClickListener(this);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
/*switch(v.getId()){
case R.id.button1:
//创建碎片
AnotherRightFragment fragment=new AnotherRightFragment();
//获得布局管理器
FragmentManager fragmentManager=getFragmentManager();
//获得碎片的事物
FragmentTransaction transaction=fragmentManager.beginTransaction();
//替换id为R.id.right_layout里面的内容
transaction.replace(R.id.right_layout, fragment);
transaction.addToBackStack(null);
transaction.commit();//提交事务
break;
default:
break;
}*/
}
}
在平板模拟器上运行程序效果如下:
在启动一个手机模拟器,并在这个模拟器上运行程序,效果如下:
这样就实现了动态加载布局的功能。
android中常见的限定符
大小:
small提供给小屏幕设备的资源
normal提供给中等屏幕设备的资源
large提供给大屏幕设备的资源
xlarge提供给超大屏幕设备的资源
分辨率:
ldpi提供给低分辨率设备的资料(120dpi以下)
mdpi提供给中等分辨率设备的资源(120dpi到160dpi)
hdpi提供给高分辨率设备的资源(160dpi到240dpi)
xhdpi提供给高分辨率设备的资源(240dpi到320dpi)
方向:
land提供给横屏设备的资源
port提供给竖屏设备的资源
2.使用最小宽度限定符
最小宽度限定符允许我们对屏幕的宽度指定一个最小值(以dp为单位),然后以这个最小值为临界点,屏幕宽度大于这个值的设备就加载这个布局,屏幕宽度小于这个值的设备就加载另一个布局。
在res目录下面新建layout-sw600dp文件夹,然后在这个文件夹下新建activity_main.xml布局,代码如下:
<LinearLayout xmlns: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/left_fragment"
android:name="com.wj.fragmenttest.LeftFragment"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
/>
<fragment
android:id="@+id/right_fragment"
android:name="com.wj.fragmenttest.RightFragment"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="3"
/>
</LinearLayout>
当程序运行在屏幕宽度大于600dp的设备上时,就会加载layout-sw600dp/activity_main.xml布局,当程序运行在屏幕宽度小于600dp的设备时,则仍然加载默认的layout/activity_main.xml布局。
注意:最小宽度限定符是在android3.2版本引入的,由于我使用的好似最低兼容系统版本4.0,所以可以放心地使用它。
转载请注明:http://blog.csdn.net/j903829182/article/details/40707273