最近看了很多动画和一些效果很好的自定义控件,发现了一件事,就是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);
}
}
如我们的设计一样,两个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方法提交。
现在运行一下程序,效果如下图所示:
竖屏显示结果
横屏显示结果
在实战中主要使用的是第二种方法使用我们的Fragment.
即底部是三个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类的实例:
1.FragmentManager mFragmentManager = getSupportFragmentManager();
2.FragmentTransaction 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错误