天天看点

Android Fragment 详解 2016 干货

最近看了很多动画和一些效果很好的自定义控件,发现了一件事,就是Android的View层设计思想和古老的JavaSwing是如此的相似。这是在原来的基础上加入了一些输入移动端的生命周期,使其在使用和性能上更好。但是对核心的理解还是可以借鉴一些的。

如果说Activity就是JavaSwing的JFrame那么Fragment 就是在JPanel

一个是容器顶层控件,一个是显示在容器上的中间层控件。

所以这里插一句话,如果感觉自己android写的不溜那么就多看看java的代码。和设计思想吧。android的东西太多都是继承自java或者说就是java的衍生品。无论是顶层容器还是底层容器都是为了放交互容器的。

Fragment的使用

一、布局

大家完全可以把Fragment看成一个轻量级的Activity.为什么轻量,因为他不用在mainfest.xml文件中添加自己.第二他不能直接启动,必须依附(attach)在activity之上。

所以在这布局这一块,Fragment的使用完全可以和activity的布局划等号,设计图怎么画在这里就怎么设计一个 Fragment .

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
    android:layout_width="match_parent"  
    android:layout_height="match_parent"  
    android:background="#00ff00" >  

    <TextView  
        android:layout_width="wrap_content"  
        android:layout_height="wrap_content"  
        android:text="This is fragment 1"  
        android:textColor="#000000"  
        android:textSize="25sp" />  

</LinearLayout> 
           
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
    android:layout_width="match_parent"  
    android:layout_height="match_parent"  
    android:background="#ffff00" >  

    <TextView  
        android:layout_width="wrap_content"  
        android:layout_height="wrap_content"  
        android:text="This is fragment 2"  
        android:textColor="#000000"  
        android:textSize="25sp" />  

</LinearLayout>  
           

简单的画了两个布局背景不同,文字区分1和2

二、在Activity中使用

使用方法一:静态加载一个Fragment

第一步把这个 fragment实例化

public class Fragment1 extends Fragment {  

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

二和一 ,一样

在onCreateView() 中实现的是

{
    View view=inflater.inflate(R.layout.fragment);//这个View就是父容器在这个布局里的其他控件 好比TextView要依靠他findViewById();
    TextView tv=(TextView)view.findViewById(R.id,textview);
    return view;
}
           

这样一个 Fragment在View层就创建成功了。

然后打开或新建activity_main.xml作为主Activity的布局文件,在里面加入两个Fragment的引用,使用android:name前缀来引用具体的

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
    android:layout_width="match_parent"  
    android:layout_height="match_parent"  
    android:baselineAligned="false" >  

    <fragment  
        android:id="@+id/fragment1"  
        **android:name="com.example.fragmentdemo.Fragment1"**  
        android:layout_width="0dip"  
        android:layout_height="match_parent"  
        android:layout_weight="1" />  

    <fragment  
        android:id="@+id/fragment2"  
        **android:name="com.example.fragmentdemo.Fragment2"**  
        android:layout_width="0dip"  
        android:layout_height="match_parent"  
        android:layout_weight="1" />  

</LinearLayout>  
           

最后打开或新建MainActivity作为程序的主Activity,里面的代码非常简单,都是自动生成的:

public class MainActivity extends Activity {  

    @Override  
    protected void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.activity_main);  
    }  

}  
           
Android Fragment 详解 2016 干货

如我们的设计一样,两个Fragment平分了屏幕

使用方法二:动态添加

这回我们直接在Activity中使用Fragment

public class MainActivity extends Activity {  

    @Override  
    protected void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.activity_main);  
        Display display = getWindowManager().getDefaultDisplay();  
        if (display.getWidth() > display.getHeight()) {  
            Fragment1 fragment1 = new Fragment1(); //新建Fragment
            getFragmentManager().beginTransaction().replace(R.id.main_layout, fragment1).commit(); //添加Fragment提交
        } else {  
            Fragment2 fragment2 = new Fragment2();  
            getFragmentManager().beginTransaction().replace(R.id.main_layout, fragment2).commit();  
        }  
    }   
}  
           

首先,我们要获取屏幕的宽度和高度,然后进行判断,如果屏幕宽度大于高度就添加fragment1,如果高度大于宽度就添加fragment2。动态添加Fragment主要分为4步:

1.获取到FragmentManager,在Activity中可以直接通过getFragmentManager得到。

2.开启一个事务,通过调用beginTransaction方法开启。

3.向容器内加入Fragment,一般使用replace方法实现,需要传入容器的id和Fragment的实例。

4.提交事务,调用commit方法提交。

现在运行一下程序,效果如下图所示:

竖屏显示结果

Android Fragment 详解 2016 干货

横屏显示结果

Android Fragment 详解 2016 干货

在实战中主要使用的是第二种方法使用我们的Fragment.

Android Fragment 详解 2016 干货

即底部是三个activity切换按钮上面是三个Fragment这里的,这里对Fragment的使用方法就是使用动态加载的方法。

三、Fragment的生命周期 Fragment 就是一个轻量的Activity那么他的声明周期就和Activity非常像了。

生命周期如下:

1.onAttach() 在Fragment和Activity建立关联的时候调用

2.onCreate() 初始化Fragment本身为inflate view做准备

3.onCreateView() 对布局中的所有控件进行初始化 绑定

4.onActivityCreated

5.onStart

6onResume

在按下home之后

1.onPause 程序挂起

2.onStop 当前activity 停止

在返回程序之后

1.onStart 开始

2.onResume 恢复

点击back退出程序

1onPause

2onStop

3onDestoryView Fragment 的View被移除了。

4onDestory

5onDetach Fragment和Activiy解除关联的时候调用

注:此处并没有onRestart()方法!

三、Fragment通信

这里的通信主要分为

fragment->fragment 通过getActivity()为桥梁进行通信

Activity->fragment

fragment-activity 最简单 所有activity 之间的通信方法这种时候都可以用

如在之前Fragment2中添加一个button那么我们需要调用其他相关的Fragment是如何实现的那。

public class Fragment2 extends Fragment {  

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

    @Override  
    public void onActivityCreated(Bundle savedInstanceState) {  
        super.onActivityCreated(savedInstanceState);  
        Button button = (Button) getActivity().findViewById(R.id.button);  
        button.setOnClickListener(new OnClickListener() {  
            @Override  
            public void onClick(View v) {  
                TextView textView = (TextView) getActivity().findViewById(R.id.fragment1_text);  
                Toast.makeText(getActivity(), textView.getText(), Toast.LENGTH_LONG).show();  
            }  
        });  
    }    
}  
           

当点击按钮的时候就会在Toast中显示Fragment1中的内容。

当然两个Fragment需要使用一个共同的activity父容器。

最后分享一些使用心得

补充知识点:

1、什么是FragmentTransaction?

使用Fragment时,可以通过用户交互来执行一些动作,比如增加、移除、替换等。

所有这些改变构成一个集合,这个集合被叫做一个transaction。

可以调用FragmentTransaction中的方法来处理这个transaction,并且可以将transaction存进由activity管理的back stack中,这样用户就可以进行fragment变化的回退操作。

可以这样得到FragmentTransaction类的实例:

ragmentManager  mFragmentManager = getSupportFragmentManager();
ragmentTransaction  mFragmentTransaction = mFragmentManager.beginTransaction();
           

在多个 Fragment 之间进行切换的时候

replace()和hide的区别

首先replace 是代替,之前的Fragment会被清空掉,在再次切换回来的时候会进行重新加载。而hide是将之前的一个fragment 进行隐藏,将新的fragment 叠在上面进行显示.等在次调用的时候进行显示。不需要重新加载。测试这个最简单的方法就是 设计两个fragment 在同一位置上放一个按钮,一个有监听一个没有,但现实没有监听的按钮点击按钮的时候会响应一号按钮的监听事件。

另外一个对于 FragmentTransaction对象在commit之后就会失效。所以建议直接使用fragmentManager.beginTransaction() 这样就不会产生失效的问题了。

commit 于commitAllowingStateLoss();的区别

简单来说就是避免报错。

他们都调用了commitInternal

public int commit() {

return commitInternal(false);

}

public int commitAllowingStateLoss() {

return commitInternal(true);

}

这个两个方法区别就在传参判断后的处理方法checkStateLoss

当使用commit方法时,系统将进行状态判断,如果状态(mStateSaved)已经保存,将发生”Can not perform this action after onSaveInstanceState”错误。

如果mNoTransactionsBecause已经存在,将发生”Can not perform this action inside of ” + mNoTransactionsBecause错误