Fragement简介:
Fragment是activity的一个片段,依赖于activity而存在,有自己的生命周期,但受activity生命周期的影响。API>=11才可以使用。
Fragment生命周期:
1 onAttach(Activity activity) fragment依附于activity
2 onCreate(Bundle bundle) 创建
3 onCreateView() (XML—>View)
4 onActivityCreated() acitivty创建的时候(初始化数据)
5 onStart() 界面可见
6 onResume() 可交互
7 onPause() 可见不可建湖
8 onStop() 界面不可见
9 onDestroyView() 销毁fragment
10 onDestroy() 销毁activiy
11 onDettach() 去除依附关系
相关的类
FragmentManager:Framgent的管理者(Android.jar 和 v4)
Transation:事务(要么同时成功,要么同事失败)
相关的方法:
1 context.getFragmentManager: 获取Framgent的管理者
2 context.getSupportFragmentManager:获取v4包的Framgent的管理者
3 fm.findFragmentById(): 通过id获取fragment
4 fm.findFragmentByTag(): 通过tag获取fragment
5 fm.benginTransation: 开启事务
6 fm.replace(resId id):替换
7 fm.commit(): 提交
Fragment之间的通讯
是通过fragment的所有者activity实现fragment之间的通讯的。
fragment传值:
传值和取值:
frag.setArguments(bundle);
Bundle bundle = getArguments();
具体代码:
public static DialogFragment04 getInstance(String title, String cancel, ArrayList<String> items) {
Bundle bundle = new Bundle();
bundle.putString("title", title);
bundle.putString("cancel", cancel);
bundle.putStringArrayList("items", items);
DialogFragment04 frag = new DialogFragment04();
frag.setArguments(bundle);
return frag;
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Bundle bundle = getArguments();
title = bundle.getString("title", "标题");
cancel = bundle.getString("cancel", "取消");
items = bundle.getStringArrayList("items");
}
Framgent的3种使用方式
- 直接在activity的布局中使用
- replace()
- add() show() hide()
https://git.oschina.net/AndroidUI/fragmentreplace
使用方式1:直接在activity的布局中使用
注意:使用这种方式,必须在布局中声明fragment的id或tag。
由于是在父布局中引用width=“mathparent”,所以会全部显示。
逻辑:
activity_main.xml水平放置2个fragement。
效果图:
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"
android:orientation="horizontal"
tools:context=".MainActivity" >
<fragment
android:name="com.android.fragmentdemo01.Fragment01"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:tag="fragment01" />
<fragment
android:name="com.android.fragmentdemo01.Fragment02"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:tag="fragment02" />
</LinearLayout>
Fragment01.java(Fragment02一样,只是背景色变成了red.java)
public class Fragment01 extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment01, null);
return view;
}
}
使用方式2:replace()
逻辑:
点击Button,用fragment替换红色Linearlayout。
效果图:
fragment的android:layout_height=”fill_parent”或android:layout_height=”match_parent”都无法占满屏幕。
使用步骤:
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button btn = (Button) findViewById(R.id.btn);
btn.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
FragmentManager fm = getFragmentManager();
fm.beginTransaction().replace(R.id.layout_main,new Fragment01(), "fragment01").commit();
}
});
}
}
Demo:http://download.youkuaiyun.com/detail/ss1168805219/9508352
使用方法三:add() show() hide()
底部导航栏实现页面的切换(一):Fragment + LinearLayout + TextView
除了上面的写法,还可以这么写。
private int oldPosition = 0;//默认上一个Fragment是 frags[0]
private List<Fragment> frags = new ArrayList<>();//Fragment集合
private FragmentManager fm;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
fm = getSupportFragmentManager();
initFrags();
switchFragment(0);
}
private void initFrags() {
frags.add(new Frag1());
frags.add(new Frag2());
frags.add(new Frag3());
frags.add(new Frag4());
}
private void switchFragment(int position) {
if (position >= frags.size()) {
return;
}
Fragment newFrag = frags.get(position);
Fragment oldFrag = frags.get(oldPosition);
oldPosition = position;
FragmentTransaction transaction = fm.beginTransaction();
transaction.hide(oldFrag);
if (!newFrag.isAdded()) {
transaction.add(R.id.frame, newFrag);
}
transaction.show(newFrag);
transaction.commitAllowingStateLoss();
}
Fragment兼容低版本:
1 引用V4里面的Fragment
2 fragment依附的activity继承FragmentActivity
3 引用V4里面的fragmentManager:getSupportFragmentManager()
Fragment之间的通讯
通过他们通过的activity实现的通讯的。
1 获取activity实例
2 通过tag,或id获取fragment
3 调用fragment里面的方法实现通讯
控制Fragment方法onCreateView() 返回的View的显示与隐藏
用法:
//根据点中按钮的索引,从fragment的数据适配器中获取fragment对象,替换顶部帧布局内部内容
//1,替换的帧布局2,获取fragment对象的索引值
BaseFragment baseFragment = (BaseFragment) fragmentPagerAdapter.instantiateItem(layout_content, index);
//3,替换帧布局的fragment对象
fragmentPagerAdapter.setPrimaryItem(null, 0, baseFragment);
//提交替换操作
fragmentPagerAdapter.finishUpdate(null);
需满足2个条件
条件一:
//控制view的显示与隐藏,避免fragment的重叠
@Override
public void setMenuVisibility(boolean menuVisible) {
// super.setMenuVisibility(menuVisible);
//方法一:view
if (view != null) {
view.setVisibility(menuVisible? View.VISIBLE :View.GONE);
}
//方法二:getView()就是onCreateView() 返回的View
// if(getView()!=null){
// getView().setVisibility(menuVisible?View.VISIBLE:View.GONE);
// }
}
条件二:
FragmentPagerAdapter myAdapter = new FragmentPagerAdapter(getSupportFragmentManager()){...}可以
class MyAdapter extends FragmentPagerAdapter(){...} 不可以,fragment仍然会重叠
Fragment重叠
在使用fragment的replace(…)方法时,会导致界面的重叠。我这里说的重叠是:用FragmentA、FragmentB去替换framelayout(里面包含textView:有text),那么A重的内容与framelayout的内容重叠,但A与B的不会重叠。
原因:
我们在用fragment去替换时,比方说用A替换B,那么B里面不应该有内容,B应该这是一个Layout.
注意:
FragmentTransaction和 commit必须在一个方法里面,同一个FragmentTransaction对应同一个replace()+ commit()方法,
也就是每次要替换都要重新创建FragmentTransaction,不能把FragmentTransaction的创建放在成员变量中。不能把FragmentTransaction变成成员变量。
如图:
图一:FrameLayout中的TextView: text = “content”
图二:Fragment2种的TextView: text = “222”,与 content重叠的效果
若想不重叠:需满足2个条件:
第一:替换的内容区域改为FrameLayout 或RelativeLayout
第二:framelayout没有子布局 或子view,如果frameLayout中个textview,那
么这个textview还会存在,还是会重叠的。
效果图:
MainActiivty.java
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private Button btn1;
private Button btn2;
private Button btn3;
private Button btn4;
private Frag1 frag1;
private Frag2 frag2;
private Frag3 frag3;
private Frag4 frag4;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btn1 = (Button) findViewById(R.id.btn1);
btn2 = (Button) findViewById(R.id.btn2);
btn3 = (Button) findViewById(R.id.btn3);
btn4 = (Button) findViewById(R.id.btn4);
btn1.setOnClickListener(this);
btn2.setOnClickListener(this);
btn3.setOnClickListener(this);
btn4.setOnClickListener(this);
initFragment();
}
private void initFragment() {
frag1 = new Frag1();
frag2 = new Frag2();
frag3 = new Frag3();
frag4 = new Frag4();
}
@Override
public void onClick(View view) {
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
switch (view.getId()){
case R.id.btn1:
transaction.replace(R.id.frame,frag1,"Frag1").commit();
break;
case R.id.btn2:
transaction.replace(R.id.frame,frag2,"Frag2").commit();
break;
case R.id.btn3:
transaction.replace(R.id.frame,frag3,"Frag3").commit();
break;
case R.id.btn4:
transaction.replace(R.id.frame,frag4,"Frag4").commit();
break;
}
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<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"
android:orientation="vertical"
tools:context="com.cqc.fragmentreplace.MainActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="50dp"
android:orientation="horizontal">
<Button
android:id="@+id/btn1"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@null"
android:text="btn1"/>
<Button
android:id="@+id/btn2"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@null"
android:text="btn2"/>
<Button
android:id="@+id/btn3"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@null"
android:text="btn3"/>
<Button
android:id="@+id/btn4"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@null"
android:text="btn4"/>
</LinearLayout>
<RelativeLayout
android:id="@+id/frame"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="content"/>
</RelativeLayout>
</LinearLayout>
frag1.xml (frag2.xml frag3.xml frag4.xml都一样,只是text变成了222/333/444 )
<?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">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="111"/>
</LinearLayout>
{replace() } {add()+show()+hide() }{ViewPager +Fragment}的生命周期的方法
Demo:https://git.oschina.net/AndroidUI/fragmentreplace.git
replace()会把所有的生命周期全走一遍,也就是当再次回到该frag1时,会重新创建,而不是从onResume()等开始。
add()+hide()+show(),当再次回到Frag1时,不会重新走任何生命周期的方法,而是会走
@Override
public void onHiddenChanged(boolean hidden) {
super.onHiddenChanged(hidden);
}
ViewPager+Fragment,当从Frag2回到Frag1时,也不会走Fragment的任何生命周期方法,而是走
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
}
在三星手机上遇到的问题。
当Frag1中有个Button,点击该Button会会打开新的Activity(比如Activity_A),当点击Button后,ViewPager中的这几个Fragment都会被销毁,导致的问题就是:如果你在Fragment的OnCreateView()、onActivityCreated()、onResume()、onPause()
做了网络请求的操作,那么当关闭Activity_A的时候,会再走这个几个方法法OnCreateView()、onActivityCreated()、onResume()、onPause()
,也就会再次请求网络。
当Fragment需要展示大量gif图片时,为什么不建议使用 add()/show/hide()方式?
当Fragment里面加载了大量的gif图片,这种方式会让Fragment加载到内存中,而gif会占用大量cpu资源,影响流畅度。
Fragment切换动画
分2种,setTransition()
、setCustomAnimations()
,注意:设置动画要在replace()
+add()
前,不然动画无效。
Demo: http://git.oschina.net/AndroidUI/fragmentanimator01
第一种:setTransition()
fm.beginTransaction()
.setTransition(FragmentTransaction.TRANSIT_EXIT_MASK)
.replace(R.id.frameLayout, frag1)
.commit();
还有
FragmentTransaction.TRANSIT_FRAGMENT_OPEN
FragmentTransaction.TRANSIT_FRAGMENT_CLOSE
FragmentTransaction.TRANSIT_ENTER_MASK
FragmentTransaction.TRANSIT_EXIT_MASK
FragmentTransaction.TRANSIT_FRAGMENT_FADE
FragmentTransaction.TRANSIT_NONE
FragmentTransaction.TRANSIT_UNSET
第二种:setCustomAnimations()
fm.beginTransaction()
.setCustomAnimations(android.R.anim.slide_in_left, android.R.anim.slide_out_right)
.replace(R.id.frameLayout, frag3)
.commit();
还可以使用自定义的动画
fm.beginTransaction()
.setCustomAnimations(R.anim.slide_in_bottom, R.anim.slide_out_bottom)
.replace(R.id.frameLayout, frag4)
.commit();
还有addToBackStack()
,没看出来什么效果。
fm.beginTransaction()
.setCustomAnimations(R.anim.slide_in_bottom, R.anim.slide_out_bottom)
.add(R.id.frameLayout,frag2)
.addToBackStack("Frag2")
.commit();
R.anim.slide_in_bottom
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="@android:integer/config_longAnimTime">
<translate
android:fromYDelta="100%"
android:toYDelta="0"/>
<alpha
android:fromAlpha="0"
android:toAlpha="1"/>
</set>
R.anim.slide_out_bottom
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="@android:integer/config_longAnimTime">
<translate
android:fromYDelta="0"
android:toYDelta="100%"/>
<alpha
android:fromAlpha="1"
android:toAlpha="0"/>
</set>
DialogFragment
自定义类继承
DialogFragment
,重写onCreateDialog()
或者onCreateView()
创建dialog
第一种:onCreateDialog()
中创建dialog
Dialog dialog = super.onCreateDialog(savedInstanceState);
第二种:onCreateDialog()
中创建dialog
AlertDialog dialog = new AlertDialog.Builder(getActivity()).create();
第三种:在onCreateView()
中自定义view
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.frag_dialog_3, container, false);
return rootView;
}
显示dialog
DialogFragment01 frag = new DialogFragment01();
frag.show(getSupportFragmentManager(), "frag");
Demo
DialogFragment01
public class DialogFragment01 extends DialogFragment implements View.OnClickListener {
@NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
Dialog dialog = super.onCreateDialog(savedInstanceState);
dialog.setTitle("Title");
dialog.setContentView(R.layout.frag_dialog_3);
dialog.setCanceledOnTouchOutside(false);
dialog.setCancelable(false);
TextView tv1 = dialog.findViewById(R.id.tv1);
TextView tv2 = dialog.findViewById(R.id.tv2);
tv1.setOnClickListener(this);
tv2.setOnClickListener(this);
return dialog;
}
@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.tv1:
Toast.makeText(getActivity(), "tv1", Toast.LENGTH_SHORT).show();
break;
case R.id.tv2:
Toast.makeText(getActivity(), "tv2", Toast.LENGTH_SHORT).show();
break;
}
}
}
DialogFragment02
public class DialogFragment02 extends DialogFragment {
@NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
AlertDialog dialog = new AlertDialog.Builder(getActivity())
.setTitle("Title")
.setMessage("message")
.setPositiveButton("确定", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
}
})
.setNegativeButton("取消", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
}
})
.setCancelable(false)
.create();
dialog.setCanceledOnTouchOutside(false);
return dialog;
}
}
DialogFragment03
public class DialogFragment03 extends DialogFragment {
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setStyle(DialogFragment.STYLE_NO_TITLE,0);//去掉dialog的Title,也可以使用style
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.frag_dialog_3, container, false);
return rootView;
}
}
Demo:https://git.oschina.net/AndroidUI/dialogfragment01.git
遇到的bug
1.context成员变量的问题
public abstract class BaseFragment extends Fragment {
public Context context = null;
public View view;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
context = getActivity();//如果变成成员变量即:public Context context = getActivity(),子类得不到context,为null
view = initView();
return view;
}
}
2.fragment放在viewpager中重写的方法有哪些?
如果把fragment放在viewpager中,那么viewpager的adapter使用FragmentPagerAdapter,重写方法getItem()+getCount();不需要重写instantiateItem()+ destroyItem()
3 假如MainActivity下面有FragmentA和FragemntB,来给SlidingMenu替换侧拉栏和内容页(替换顺序:A在前,B在后),那么在FragmentA中调用((MainActivity)context).findFragmentByTag(“HOME”)会报错,因为在FragmentA中调用findFragmentByTag(“HOME”),而这时FragmentB还没有替换(没有tag)。
public class MainActivity extends SlidingFragmentActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
....
//用fragment替换frameLayout
//1 侧拉栏
MenuFragment menuFragment = new MenuFragment();
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.fl_menu, menuFragment,"MENU")
.commit();
//2内容页
HomeFragment homeFragment = new HomeFragment();
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.fl_content, homeFragment,"HOME")
.commit();
}
public class MenuFragment extends Fragment {
public void onActivityCreated(Bundle savedInstanceState) {
//此时由于HomeFragment 没有替换,就拿不到homeFragment,值为null。
HomeFragment homeFragment = ((MainActivity)context).getHomeFragment();
}
}
4 Fragment中调用onActivityResult()无效
在Fragment设置startActivityForResult(),完成后,Fragment的onActivityResult没有任何反应。
解决方法;
Fragment中直接使用startActivityForResult(); 不要使用this.getActivity().startActivityForResult();
或者
Fragment中onActivityResult的使用
5 java.lang.IllegalStateException Can not perform this action after onSaveInstanceState
报错信息(onSaveInstanceState()方法后不能响应action)
java.lang.IllegalStateException
Can not perform this action after onSaveInstanceState
原因:
fragment替换中,onSaveInstanceState()方法后不能调用方法commit(),
解决方法:
将commit()换成commitAllowingStateLoss()
参考:
解决IllegalStateException: Can not perform this action after onSaveInstanceState
FragmentManager is already executing transactions
LOG提示:
FragmentManager is already executing transactions
情况:
App顶部是bottombar,上面是framelayout,采用replace(。。)的方式替换主页面,分别对应frag1、frag2、frag3,其中frag2的布局是:上面tablayout,下面还是采用采用replace(。。)的方式替换其余界面。2个FragmentManager都用的是
android.support.v4.app.FragmentManager
,但是包错(上面)。
解决方法:
FragmentManager fm = getChildFragmentManager();
参考:FragmentManager is already executing transactions异常
Activity销毁时,Fragment没有被销毁,导致的页面重叠
方法一:activity销毁时不保存信息
@Override
protected void onSaveInstanceState(Bundle outState) {
//super.onSaveInstanceState(outState);
}
方式二:Activity销毁时,保存Fragment,Activity创建时从新取出Fragment
@Override
protected void onSaveInstanceState(Bundle outState) {
if (homeFrag != null) {
getSupportFragmentManager().putFragment(outState, HomeFrag.class.getSimpleName(), homeFrag);
}
if (userFrag != null) {
getSupportFragmentManager().putFragment(outState, UserFrag.class.getSimpleName(), userFrag);
}
super.onSaveInstanceState(outState);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViews();
if (savedInstanceState != null) {
homeFrag = (HomeFrag) getSupportFragmentManager().getFragment(savedInstanceState, HomeFrag.class.getSimpleName());
userFrag = (UserFrag) getSupportFragmentManager().getFragment(savedInstanceState, UserFrag.class.getSimpleName());
}
initViews();
}
参考:https://blog.youkuaiyun.com/yuzhiqiang_1993/article/details/75014591