前言
fragment自动3.0推出以后,在碎片化处理方面一直很受欢迎,平时在项目中也在频繁使用,自从看了CSDN上郭大神和鸿洋大神对fragment的详细总结后,自己学习做个笔记。
郭大神:http://blog.csdn.net/guolin_blog/article/details/8881711
鸿洋大神:http://blog.csdn.net/lmj623565791/article/details/42628537
fragment的常用方法
(1) 静态使用
在布局文件xml中使用
<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/fragmentOne"
android:name="com.ydscience.FragmentOne"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1" />
<fragment
android:id="@+id/fragmentTwo"
android:name="com.ydscience.FragmentTwo"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1" />
</LinearLayout>
具体的fragmentOne和fragmentTwo的代码就不粘贴啦,都是继承Fragment去实现的。
(2)动态使用
动态使用分为四个步骤
(a)获取FragmentManager,通过方法getSupportFragmentManager()获取。
(b)开启一个事物,通过beginTransaction方法开启。
(c)向容器内加入Fragment,默认第一个是通过add()方法添加,后面再替换时通过replace方法实现,需要传入容器的id和Fragment的实例。
(d)提交事务,调用commit方法提交。
xml中的代码为
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.ydsciernce.studytest.MainActivity">
<FrameLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/content_fragment"/>
</RelativeLayout>
Activity中的代码为
public class MainActivity extends AppCompatActivity implements FragementOne.FOneListener{
FragmentTwo mFragmentTwo;
FragementOne mFragmentOne;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (savedInstanceState == null){
Log.i("TAG","saveInstanece数据为空");
FragmentManager manager = getSupportFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
mFragmentOne = new FragementOne();
transaction.add(R.id.content_fragment,mFragmentOne,"ONE");
transaction.commit();
}else {
Log.i("TAG","saveInstanece数据为"+savedInstanceState.toString());
}
}
@Override
public void onOneClick() {
if (mFragmentTwo == null){
mFragmentTwo = new FragmentTwo();
}
FragmentManager manager = getSupportFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
//transaction.hide(mFragmentOne);
//transaction.add(R.id.content_fragment,mFragmentTwo,"TWO");
transaction.replace(R.id.content_fragment,mFragmentTwo,"TWO");
transaction.addToBackStack(null);
transaction.commit();
}
}
FragmentOne的代码为
public class FragementOne extends Fragment implements View.OnClickListener{
private Button mButton;
EditText test;
public interface FOneListener{
void onOneClick();
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
Log.i("TAG"," on createview");
View view = inflater.inflate(R.layout.content_one_fragment,null);
mButton = (Button) view.findViewById(R.id.buttonOne);
mButton.setOnClickListener(this);
test = (EditText) view.findViewById(R.id.editText);
Log.i("TAG","edit datda"+test.getText().toString());
return view;
}
@Override
public void onClick(View v) {
if(getActivity() instanceof FOneListener){
((FOneListener)getActivity()).onOneClick();
}
}
}
注意事项:
a.在设置默认的fragment时,不能调用addToBackStack(null)方法,如果添加了在退出应用时会在Activity页面出现一个空白页面。
b.在代码中有两个Fragment,分别为FrangmentOne和FragmentTwo,FragmentOne为默认的,从FragmentOne中的Button 去启动FragmentTwo,在鸿洋大神的分析中当我们从FragmentOne启动到FragmentTwo时使用的是replace方法,replace方法的实现过程是remove方法和add方法的合体,当我们在调用该方法时,如果不添加事务到回退栈,前一个Fragment实例会被销毁,调用了addToBackStack(null);将当前的事务添加到了回退栈,所以FragmentOne实例不会被销毁,但是视图层次依然会被销毁,即会调用onDestoryView和onCreateView,在这种情况下,再次返回到FragmentOne时等于新建了一个FragmentOne实例对象,在之前Fragment中保存的数据将不会存在,而在真实的案例中当我们从FragmentTwo返回到FragmentOne时希望数据还在,因此此时就不能使用replace方法,使用hide方法将FragmentOne隐藏起来,当FragmengTwo返回时会调用show方法显示,相应的数据也会被保存。
c.当fragment遇到屏幕自动旋转时,会导致Activity和Fragment的重建在这种情况下可能会导致两个FragmentOne的实例对象存在,界面上也会存在重叠效果,导致这种现象的原因是当Activity重建时我们的fragment会被保存下来,但是会创建新的FragmentManager,新的FragmentManager会首先会去获取保存下来的fragment队列,重建fragment队列,从而恢复之前的状态。解决方法是在创建实例和获取FragmentManager时判断saveInstanceState状态,当我们第一次启动Activity时,saveInstanceState为空的,当因异常情况导致Activity重建时,自动会调用saveInstanceState会保存一些信息,因此通过判断saveInstanceState状态可以避免这种情况。
d.在参考了大神的博客后自己尝试技能满足保存数据又可以避免在屏幕旋转后避免创建多个Fragment实例对象,实现方法为创建实例前对saveInstanceState状态进行判断,同时在启动其他的Fragment时不调用replace方法使用hide方法,但是出现nullPointerException,目前还没解决。
(3)Activity与Fragment以及Fragment与Fragment之间的通信。
a.Activity与Fragment
实现方式有以下几种
①.handler通信
②.接口回调 例如上面代码中在FragmentTwo 实现方式,在
FragmentTwo中的点击事件通过接口回调交给Activity去处理。
声明接口
public interface FOneListener{
void onOneClick();
}
点击事件传递
public void onClick(View v) {
if(getActivity() instanceof FOneListener){
((FOneListener)getActivity()).onOneClick();
}
}
父类继承去实现
public class MainActivity extends AppCompatActivity implements FragementOne.FOneListener
@Override
public void onOneClick() {
//处理点击事件
}
③.使用EventBus去实现。
④.使用广播BroadcastReceiver去实现。
b.同时存在同一个Activity中的页面中的两个Fragment之间的通信
在查看郭大神的博客后发现这种情况适用于存在于同一个页面之间FragmentA 去获取FragmentB(或者FragmentB 去获取FragmentA)中某个组件的信息。主要都是通过getActivity这个方法实现的,getActivity方法可以让Fragment获取到关联的Activity,然后再调用Activity的findViewById方法,就可以获取到和这个Activity关联的其它Fragment的视图,从而可以获取这个视图的数据。
@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();
}
});
}
(4)Fragment的复用以及与Activity之间的解耦
在我们项目想法中肯定会经常遇到ViewPager与Fragment之间的结合使用,使用Viewpager来切换不同的fragment,当第一次使用时可能会针对每一个主题都会创建一个Fragment去实现,这种实现方式在网上有很多案例,但是这种实现方式创建了很多的fragment,代码有太多的耦合性,自从Google官网给出了Tablayout这个组件时,它也是结合fragmen和viewpager去实现的。在这个具体实现时很好的复用了fragment。具体实现见代码吧,口头叙述态度都是扯淡,只有代码才是王道。
布局代码(content_history_record.xml)
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_centerInParent="true">
<android.support.design.widget.TabLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/tabLayout"
app:tabBackground="@color/historyColorAccent"
app:tabSelectedTextColor="@color/tabSelected"
app:tabTextColor="@color/tabUnSelected"
app:tabMode="scrollable"/>
<android.support.v4.view.ViewPager
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:background="@color/label"
android:id="@+id/viewpager_history"
/>
</LinearLayout>
MainActivity的代码
public class HistoryRecordActivity extends AppCompatActivity{
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.content_history_record);
init();
}
private void init(){
HistoryFragmentPreferenceAdapter adapter = new HistoryFragmentPreferenceAdapter(getSupportFragmentManager(),this);
ViewPager historyPager = (ViewPager) findViewById(R.id.viewpager_history);
historyPager.setAdapter(adapter);
TabLayout historyTabLayout = (TabLayout) findViewById(R.id.tabLayout);
historyTabLayout.setupWithViewPager(historyPager);
historyTabLayout.setTabGravity(TabLayout.GRAVITY_FILL);
historyTabLayout.setTabMode(TabLayout.MODE_FIXED);
}
}
适配器的代码(HistoryFragmentPreferenceAdapter.java)
public class HistoryFragmentPreferenceAdapter extends FragmentPagerAdapter {
private Context mContext;
private final int coutnt = 5;
private String [] titles ;
public HistoryFragmentPreferenceAdapter(FragmentManager fm, Context context) {
super(fm);
this.mContext = context;
titles = context.getResources().getStringArray(R.array.history_title);
}
@Override
public Fragment getItem(int position) {
return PageFragment.getIntance(position+1);
}
@Override
public int getCount() {
return coutnt;
}
@Override
public CharSequence getPageTitle(int position) {
return titles[position];
}
}
Fragment的代码(PageFragment.java)
public class PageFragment extends Fragment {
public static final String TRANSFER_PAGE = "page";
private int mPage;
public static Fragment getIntance(int page){
Bundle bundle = new Bundle();
bundle.putInt(TRANSFER_PAGE,page);
PageFragment pageFragment = new PageFragment();
pageFragment.setArguments(bundle);
return pageFragment;
}
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
Bundle bundle = getArguments();
if (bundle != null)
mArgument = bundle.getString(TRANSFER_PAGE);
}
给Fragment添加newInstance方法,将需要的参数传入,设置到bundle中,然后setArguments(bundle),最后在onCreate中进行获取通过getArguments()去获取一个fragment的实例,通过这种方式可以实现Fragment的复用。