Android_UI:Fragment

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种使用方式

  1. 直接在activity的布局中使用
  2. replace()
  3. 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还会存在,还是会重叠的。

效果图:
这里写图片描述

源码下载:https://github.com/s1168805219/FragmentReplace

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值