以前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作为最外层布局。
此坑填完!!