动态加载Fragment的坑
来源:互联网 发布:诅咒淘宝店网址 编辑:程序博客网 时间:2024/06/07 09:46
以前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: main11-06 04:23:43.800: E/AndroidRuntime(8144): Process: com.example.fragment, PID: 814411-06 04:23:43.800: E/AndroidRuntime(8144): java.lang.IllegalStateException: commit already called11-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作为最外层布局。
此坑填完!!
- 动态加载Fragment的坑
- Fragment的动态加载
- Fragment:碎片的动态加载
- Fragment的动态加载问题
- FragMent的静态加载和动态加载
- Fragment自学 动态加载Fragment 以及如何获取动态加载的fragment中的控件
- fragment的静态与动态加载
- android Fragment动态加载的使用详解
- android (二) -fragment 的动态加载
- Android中的Fragment——02(Fragment的动态加载)
- 动态加载fragment和实现fragment 之间的通信
- fragment动态加载
- fragment 动态加载,删除
- Fragment动态加载
- 动态加载Fragment
- 动态加载Fragment
- 仿微信 Fragment 动态加载
- 动态加载fragment
- Java for Web学习笔记(三一):JSTL(7)SQL Tag
- Android内存泄漏相关
- 自定义下拉刷新和上拉加载ListView
- Python实现快速排序
- js中三个容易混淆的东东
- 动态加载Fragment的坑
- poj2449 Remmarguts' Date-k短路+A*(待解决)
- Android单例模式怎么写比较好?
- Docker之Cgroup与接口的使用(二)子系统详解
- 合并k个升序链表(非递归)
- TCP协议与UDP协议的区别
- (7)分块矩阵
- 智能科普:VR、AR、MR的区别
- Java ClassLoader学习总结