动态加载Fragment的坑

以前Fragment虽然也在使用,但基本上都是配合ViewPager使用,对于Fragment的事务等知识点很少接触,最近在使用途中踩到了许多坑,所以记下来,下次遇到的话自己可以看看!

首先,我想要做的是在Activity的ui界面点击一个按钮,然后在点击事件中将Fragment展现出来,然后Fragment处理完业务之后,通过接口调用回到Activity将Fragment关闭,然后将Fragment业务结果显示出来。
如果上面的意思没有表达清楚,就直接看代码吧!
MainActivity:

package com.example.fragment;

import android.app.Activity;
import android.app.ActionBar;
import android.app.Fragment;
import android.app.FragmentTransaction;
import android.graphics.Color;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
import android.os.Build;

public class MainActivity extends Activity implements OnClickListener {


    public interface ClickListener{
        void OnClick();
    }

    private Button Btn;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Btn = (Button)findViewById(R.id.btn_Click);
        Btn.setOnClickListener(this);

    }





    /**
     * A placeholder fragment containing a simple view.
     */
    public static class PlaceholderFragment extends Fragment {

        private ClickListener listener;
        public PlaceholderFragment() {
        }
        public void setListener(ClickListener mListener){
            listener = mListener;
        }

        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                Bundle savedInstanceState) {
            View rootView = inflater.inflate(R.layout.fragment_main, container, false);
            rootView.setOnClickListener(new OnClickListener() {

                @Override
                public void onClick(View arg0) {
                    listener.OnClick();

                }
            });
            return rootView;
        }
    }

    @Override
    public void onClick(View arg0) {
        final PlaceholderFragment fragment = new PlaceholderFragment();
        final FragmentTransaction transaction = getFragmentManager().beginTransaction();
        fragment.setListener(new ClickListener() {

            @Override
            public void OnClick() {
//              MainActivity.this.getFragmentManager().beginTransaction().hide(fragment).commit();
                transaction.hide(fragment).commit();
                Btn.setBackgroundColor(Color.RED);
            }
        });
        transaction.replace(R.id.container, fragment)
      .commit();

    }

}

R.layout.activity_main:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.fragment.MainActivity"

    tools:ignore="MergeRootFrame" >
    <Button 
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:text="切换视图"
        android:id="@+id/btn_Click"
        android:gravity="center"/>
   <!--  <ImageView 
        android:id="@+id/iv"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:src="@drawable/ic_launcher"/> -->



</LinearLayout>

R.layout.fragment_main:

<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"
    android:gravity="center">

    <TextView
        android:id="@+id/fmt_Click"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:text="@string/hello_world" />
    <Button 

        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="退回"
        />

</LinearLayout>

逻辑很简单,但是事实上当我们点击文字的时候,Fragment并没有加载出来,why?

这里写图片描述

那我们将R.layout.activity_main的布局改一下,由线性布局改为相对布局试一下,看看行不行?为了显示效果明显,我们加一下背景以及HelloWorld的文字居中,接下来我们看见内容出来了!

这里写图片描述

这里写图片描述

其实使用FrameLayout也可以实现动态加载Fragment,但是LinerLayout为什么不支持动态加载Fragment呢?是它不支持吗?事实上在一定条件下,它也是支持的,我们对R.layout.activity_main做一个简单的修改,接着看看demo效果。为了演示加载效果,我们对需要加载的Linerlayout加一个蓝色的背景色。
修改后的R.layout.activity_main:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.fragment.MainActivity"

    tools:ignore="MergeRootFrame" >
    <Button 
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="切换视图"
        android:id="@+id/btn_Click"
        android:gravity="center"/>



   <LinearLayout 
       android:id="@+id/ll_main"
       android:layout_width="match_parent"
       android:layout_height="match_parent"
       android:orientation="vertical"
       android:layout_marginTop="40dp"
       android:background="@android:color/holo_blue_light"
       android:layout_below="@+id/btn_Click">
       <ImageView 
        android:id="@+id/iv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/ic_launcher"/> 
        <Button 
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="不要点"
        android:gravity="center"/>
         <ImageView 
        android:id="@+id/iv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/ic_launcher"/> 
        <Button 
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="不要点"
        android:gravity="center"/>
   </LinearLayout>
</RelativeLayout>

接着对MainActivity里接口实现的部分将id修改一下:

@Override
    public void onClick(View arg0) {
        final PlaceholderFragment fragment = new PlaceholderFragment();
        final FragmentTransaction transaction = getFragmentManager().beginTransaction();
        fragment.setListener(new ClickListener() {

            @Override
            public void OnClick() {
//              MainActivity.this.getFragmentManager().beginTransaction().hide(fragment).commit();
                transaction.hide(fragment).commit();
                Btn.setBackgroundColor(Color.RED);
            }
        });
        transaction.replace(R.id.ll_main, fragment)
      .commit();

    }

接下来看看加载效果:

这里写图片描述

这里写图片描述

这里说明LinerLayout还是可以动态加载Fragment的,接着我们可以来思考一下为什么刚刚直接替换整个LinerLayout会没有展示效果呢?

这里写图片描述

经过上面使用LinerLayout加载Fragment,我们肯定了一点,LinerLayout是可以动态加载Fragment的,但是也发现了一个问题,就是Fragment并没有覆盖LinerLayout,而是在其下方展示出来的。所以我们推断出Fragment其实是已经加载,只是其碍于界面太小,展示不出来而已(之前的View都已经占用了整个父布局了)。那么这种想法是不是正确的呢?

这里写图片描述

好吧,我们根据Fragment的生命周期图,在onResume方法中输出一个toast。为什么Fragment展示在前台的方法也是onResume呢?

这里写图片描述

不要想了,直接看图:
这里写图片描述

接着我们继续修改代码,Fragment增加一个onResume方法,然后对接口部分修改替换的id:

/**
     * A placeholder fragment containing a simple view.
     */
    public static class PlaceholderFragment extends Fragment {

        private ClickListener listener;
        public PlaceholderFragment() {
        }
        public void setListener(ClickListener mListener){
            listener = mListener;
        }

        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                Bundle savedInstanceState) {
            View rootView = inflater.inflate(R.layout.fragment_main, container, false);
            rootView.setOnClickListener(new OnClickListener() {

                @Override
                public void onClick(View arg0) {
                    listener.OnClick();

                }
            });
            return rootView;
        }
        @Override
        public void onResume() {

            super.onResume();
            Toast.makeText(getActivity(), "加载完成了", Toast.LENGTH_LONG).show();
        }
    }

    @Override
    public void onClick(View arg0) {
        final PlaceholderFragment fragment = new PlaceholderFragment();
        final FragmentTransaction transaction = getFragmentManager().beginTransaction();
        fragment.setListener(new ClickListener() {

            @Override
            public void OnClick() {
//              MainActivity.this.getFragmentManager().beginTransaction().hide(fragment).commit();
                transaction.hide(fragment).commit();
                Btn.setBackgroundColor(Color.RED);
            }
        });
        transaction.replace(R.id.container, fragment)
      .commit();

    }

然后我们继续点击,通过toast来判断Fragment是否有加载?

这里写图片描述

这下知道了LinerLayout加载Fragment的坑了,下次可以注意了!这里我们也完成了Fragment的加载,我已经使尽了我的洪荒之力了。接着我们点击Fragment,完成MainActivity的UI修改,这次记得要将MainActivity的布局修改为Farmelayout或者RelativeLayout,然后将需要替换的id修改为整个布局的id,然后我们点击Fragment将切换画面的Button背景色改为红色就可以洗碗了!!

11-06 04:23:43.800: E/AndroidRuntime(8144): FATAL EXCEPTION: main
11-06 04:23:43.800: E/AndroidRuntime(8144): Process: com.example.fragment, PID: 8144
11-06 04:23:43.800: E/AndroidRuntime(8144): java.lang.IllegalStateException: commit already called
11-06 04:23:43.800: E/AndroidRuntime(8144):     at android.app.BackStackRecord.commitInternal(BackStackRecord.java:583)
11-06 04:23:43.800: E/AndroidRuntime(8144):     at android.app.BackStackRecord.commit(BackStackRecord.java:575)
11-06 04:23:43.800: E/AndroidRuntime(8144):     at com.example.fragment.MainActivity$1.OnClick(MainActivity.java:80)
11-06 04:23:43.800: E/AndroidRuntime(8144):     at com.example.fragment.MainActivity$PlaceholderFragment$1.onClick(MainActivity.java:57)

好不容易到这一步了,居然挂了!有没有一种天亮了还尿床了的感觉??
不着急,我们看看问题是什么?不知道的话我们直接将问题丢给度娘,看看这个“java.lang.IllegalStateException: commit already called”是什么东西?反正我以前也没有遇到过?

网上有文章说是事务的问题:

这里写图片描述

于是修改代码,再次尝试:

@Override
            public void OnClick() {
                MainActivity.this.getFragmentManager().beginTransaction().hide(fragment).commit();
                Btn.setBackgroundColor(Color.RED);
            }

这里写图片描述

OK,动态加载算是完成了,至于FragmentTransaction等知识点这里不做深究,大家可以自行找度娘,网上已经说得很详细了,这里就不再赘述了!

既然动态的我们实现了,那我们为什么不把静态的加载也实现呢?好,说干就干!!
R.layout.activity_main:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.fragment.view.MainActivity"
    android:orientation="vertical"
    tools:ignore="MergeRootFrame" >
    <Button 
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="切换视图"
        android:id="@+id/btn_Click"
        android:gravity="center"/>



   <LinearLayout 
       android:id="@+id/ll_main"
       android:layout_width="match_parent"
       android:layout_height="match_parent"
       android:orientation="vertical"
       android:layout_marginTop="40dp"
       android:background="@android:color/holo_blue_light"
       android:layout_below="@+id/btn_Click">
       <ImageView 
        android:id="@+id/iv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/ic_launcher"/> 
        <Button 
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="不要点"
        android:gravity="center"/>
         <ImageView 
        android:id="@+id/iv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/ic_launcher"/> 
        <Button 
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="不要点"
        android:gravity="center"/>
   </LinearLayout>
   <fragment class="com.example.fragment.MainActivity$PlaceholderFragment"
       android:layout_width="match_parent"
       android:layout_height="match_parent"
       android:id="@+id/fragment"/>
</RelativeLayout>

R.layout.fragment_main没有电变化就补贴代码了,看看MainActivity吧:

package com.example.fragment;

import android.app.Activity;
import android.app.Fragment;
import android.app.FragmentTransaction;
import android.graphics.Color;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.Toast;

public class MainActivity extends Activity implements OnClickListener {


    public interface ClickListener{
        void OnClick();
    }

    private Button Btn;
    private PlaceholderFragment fragment;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Btn = (Button)findViewById(R.id.btn_Click);
        Btn.setOnClickListener(this);
        fragment = (PlaceholderFragment) getFragmentManager().findFragmentById(R.id.fragment);
        getFragmentManager().beginTransaction().hide(fragment).commit();
    }

    /**
     * A placeholder fragment containing a simple view.
     */
    public static class PlaceholderFragment extends Fragment {

        private ClickListener listener;
        public PlaceholderFragment() {
        }
        public void setListener(ClickListener mListener){
            listener = mListener;
        }

        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                Bundle savedInstanceState) {
            View rootView = inflater.inflate(R.layout.fragment_main, container, false);
            rootView.setOnClickListener(new OnClickListener() {

                @Override
                public void onClick(View arg0) {
                    listener.OnClick();

                }
            });
            return rootView;
        }
        @Override
        public void onResume() {

            super.onResume();
            Toast.makeText(getActivity(), "加载完成了", Toast.LENGTH_LONG).show();
        }
    }

    @Override
    public void onClick(View arg0) {

        final FragmentTransaction transaction = getFragmentManager().beginTransaction();
        fragment.setListener(new ClickListener() {

            @Override
            public void OnClick() {
                MainActivity.this.getFragmentManager().beginTransaction().hide(fragment).commit();
//              transaction.hide(fragment).commit();
                Btn.setBackgroundColor(Color.RED);
            }
        });
//      transaction.replace(R.id.container, fragment).commit();
//      findViewById(R.id.container).setVisibility(View.GONE);
        transaction.show(fragment).commit();

    }

}

经过实践操作,发现即使LinerLayout静态加载Fragment也会踩到上面的坑。避坑措施为不使用LinerLayout作为最外层布局。

此坑填完!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值