片段里面的片段
Posted
技术标签:
【中文标题】片段里面的片段【英文标题】:Fragment Inside Fragment 【发布时间】:2011-10-04 01:32:24 【问题描述】:我需要关于在片段中处理片段的帮助,实际上我 我在按下后退按钮时遇到问题。应用程序主屏幕 有按钮并按下每个按钮视图替换为新的 片段(并且该片段包含在另一个片段中), 动态添加/替换片段工作正常,按 button1 片段被替换,按下按钮时也会发生同样的情况,但如果 我再次按下按钮,出现异常:
"Duplicate id 0x7f05000a, tag null, or parent id 0x7f050009 with
another fragment for com........ fragmentname"
表示已经添加了片段或内部片段,我正在尝试 再次添加它们,任何人都知道如何在里面使用片段 碎片和来回移动没有任何问题,谢谢 支持。
MainActivity,其中片段是动态添加的,并且 换了。
public class FragmentInsideFragmentTestActivity extends Activity
private Button button1;
private Button button2;
private Button button3;
private Button button4;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
button1 =(Button) this.findViewById(R.id.button1);
button1.setOnClickListener(new View.OnClickListener()
public void onClick(View view)
onButtonClick(view);
);
button2 =(Button) this.findViewById(R.id.button2);
button2.setOnClickListener(new View.OnClickListener()
public void onClick(View view)
onButtonClick(view);
);
button3 =(Button) this.findViewById(R.id.button3);
button3.setOnClickListener(new View.OnClickListener()
public void onClick(View view)
onButtonClick(view);
);
button4 =(Button) this.findViewById(R.id.button4);
button4.setOnClickListener(new View.OnClickListener()
public void onClick(View view)
onButtonClick(view);
);
public void onButtonClick(View v)
Fragment fg;
switch (v.getId())
case R.id.button1:
fg=FirstFragment.newInstance();
replaceFragment(fg);
break;
case R.id.button2:
fg=SecondFragment.newInstance();
replaceFragment(fg);
break;
case R.id.button3:
fg=FirstFragment.newInstance();
replaceFragment(fg);
break;
case R.id.button4:
fg=SecondFragment.newInstance();
replaceFragment(fg);
break;
private void replaceFragment(Fragment newFragment)
FragmentTransaction trasection = getFragmentManager().beginTransaction();
if(!newFragment.isAdded())
try
//FragmentTransaction trasection =
getFragmentManager().beginTransaction();
trasection.replace(R.id.linearLayout2, newFragment);
trasection.addToBackStack(null);
trasection.commit();
catch (Exception e)
// TODO: handle exception
// AppConstants.printLog(e.getMessage());
else
trasection.show(newFragment);
这是布局:main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_
android:layout_
android:orientation="vertical">
<LinearLayout
android:id="@+id/linearLayout1"
android:layout_
android:layout_
android:orientation="horizontal">
<Button
android:id="@+id/button1"
android:layout_
android:layout_
android:text="Button1" />
<Button
android:id="@+id/button2"
android:text="Button2"
android:layout_
android:layout_ />
<Button
android:id="@+id/button3"
android:text="Button3"
android:layout_
android:layout_ />
<Button
android:id="@+id/button4"
android:text="Button4"
android:layout_
android:layout_ />
</LinearLayout>
<LinearLayout
android:id="@+id/linearLayout2"
android:layout_
android:layout_
android:orientation="horizontal" />
</LinearLayout>
希望我试图解决我的问题。
【问题讨论】:
以上代码对我来说在 android 3.1 上运行得非常好 Fragments within Fragments的可能重复 更多详情请参考***.com/a/11020531/1219456 【参考方案1】:AFAIK,片段不能容纳其他片段。
更新
使用当前版本的 Android 支持包(或 API 级别 17 及更高级别的原生片段),您可以通过 getChildFragmentManager()
嵌套片段。请注意,这意味着您需要在 API 级别 11-16 上使用 Fragment 的 Android 支持包版本,因为即使这些设备上有原生版本的 Fragment,该版本也没有 getChildFragmentManager()
。
【讨论】:
这个平台真的很烂。我花了三个小时调试它,因为内部片段第一次渲染得很好,但在屏幕方向改变后会消失。没有例外,日志信息,什么都没有。切换到getChildFragmentManager()
并从内部片段中删除setRetainInstance(true)
(可惜)修复了它。感谢您再次保存我的培根,@CommonsWare。【参考方案2】:
我需要更多的上下文,所以我做了一个例子来说明这是如何完成的。我在准备时读到的最有帮助的是:
Creating and Using Fragments活动
activity_main.xml
在您的活动中添加FrameLayout
以保存父片段。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_
android:layout_>
<TextView
android:layout_
android:layout_
android:text="Activity"/>
<FrameLayout
android:id="@+id/parent_fragment_container"
android:layout_
android:layout_/>
</LinearLayout>
MainActivity.java
加载父片段并实现片段侦听器。 (见fragment communication。)
import android.support.v4.app.FragmentTransaction;
import android.support.v7.app.AppCompatActivity;
public class MainActivity extends AppCompatActivity implements ParentFragment.OnFragmentInteractionListener, ChildFragment.OnFragmentInteractionListener
@Override
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Begin the transaction
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.replace(R.id.parent_fragment_container, new ParentFragment());
ft.commit();
@Override
public void messageFromParentFragment(Uri uri)
Log.i("TAG", "received communication from parent fragment");
@Override
public void messageFromChildFragment(Uri uri)
Log.i("TAG", "received communication from child fragment");
父片段
fragment_parent.xml
为子片段添加另一个FrameLayout
容器。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_
android:layout_
android:layout_margin="20dp"
android:background="#91d0c2">
<TextView
android:layout_
android:layout_
android:text="Parent fragment"/>
<FrameLayout
android:id="@+id/child_fragment_container"
android:layout_
android:layout_>
</FrameLayout>
</LinearLayout>
ParentFragment.java
在onViewCreated
中使用getChildFragmentManager
设置子片段。
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentTransaction;
public class ParentFragment extends Fragment
private OnFragmentInteractionListener mListener;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState)
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_parent, container, false);
@Override
public void onViewCreated(View view, Bundle savedInstanceState)
Fragment childFragment = new ChildFragment();
FragmentTransaction transaction = getChildFragmentManager().beginTransaction();
transaction.replace(R.id.child_fragment_container, childFragment).commit();
@Override
public void onAttach(Context context)
super.onAttach(context);
if (context instanceof OnFragmentInteractionListener)
mListener = (OnFragmentInteractionListener) context;
else
throw new RuntimeException(context.toString()
+ " must implement OnFragmentInteractionListener");
@Override
public void onDetach()
super.onDetach();
mListener = null;
public interface OnFragmentInteractionListener
// TODO: Update argument type and name
void messageFromParentFragment(Uri uri);
子片段
fragment_child.xml
这里没有什么特别的。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_
android:layout_
android:layout_margin="20dp"
android:background="#f1ff91">
<TextView
android:layout_
android:layout_
android:text="Child fragment"/>
</LinearLayout>
ChildFragment.java
这里也没有什么特别的。
import android.support.v4.app.Fragment;
public class ChildFragment extends Fragment
private OnFragmentInteractionListener mListener;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState)
return inflater.inflate(R.layout.fragment_child, container, false);
@Override
public void onAttach(Context context)
super.onAttach(context);
if (context instanceof OnFragmentInteractionListener)
mListener = (OnFragmentInteractionListener) context;
else
throw new RuntimeException(context.toString()
+ " must implement OnFragmentInteractionListener");
@Override
public void onDetach()
super.onDetach();
mListener = null;
public interface OnFragmentInteractionListener
// TODO: Update argument type and name
void messageFromChildFragment(Uri uri);
注意事项
正在使用支持库,以便在 Android 4.2 之前可以使用 nested fragments。【讨论】:
当我尝试从 ParentFragment 导航到不同的片段时,这很好,然后我导航回 ParentFragment,我得到一个异常:java.lang.IllegalStateException:指定的孩子已经有一个父母。您必须先在孩子的父母上调用 removeView()。【参考方案3】:自从 Android 4.2 (API 17) 嵌套片段可用http://developer.android.com/about/versions/android-4.2.html#NestedFragments
要将片段放置在其他片段中,请使用 getChildFragmentManager()
它也可以在支持库中找到!
【讨论】:
请更新您的答案以提及支持库已经支持来自 android 1.6+ 的嵌套片段【参考方案4】:片段可以添加到其他片段中,但是每次调用父片段的onDestroyView()
方法时,您都需要将其从父片段中删除。并再次将其添加到 Parent Fragment 的 onCreateView()
方法中。
就这样吧:
@Override
public void onDestroyView()
FragmentManager mFragmentMgr= getFragmentManager();
FragmentTransaction mTransaction = mFragmentMgr.beginTransaction();
Fragment childFragment =mFragmentMgr.findFragmentByTag("qa_fragment")
mTransaction.remove(childFragment);
mTransaction.commit();
super.onDestroyView();
【讨论】:
这对我不起作用。我有一个片段,它膨胀了一个包含两个子视图的视图。在onActivityCreated()
中,它使用getFragmentManager()
添加了两个片段,一个到每个视图中。这是第一次工作,但在旋转和恢复时,只有一个片段可见。 getChildFragmentManager()
的行为是正确的。由于活动的onSaveInstanceState()
已被调用,因此此处建议的方法引发了异常。使用commitAllowingStateLoss()
提交事务避免了异常但没有解决原来的问题。
关于这个***.com/questions/63052712/…的任何想法【参考方案5】:
你可以使用getChildFragmentManager()
函数。
示例:
父片段:
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState)
rootView = inflater.inflate(R.layout.parent_fragment, container,
false);
//child fragment
FragmentManager childFragMan = getChildFragmentManager();
FragmentTransaction childFragTrans = childFragMan.beginTransaction();
ChildFragment fragB = new ChildFragment ();
childFragTrans.add(R.id.FRAGMENT_PLACEHOLDER, fragB);
childFragTrans.addToBackStack("B");
childFragTrans.commit();
return rootView;
父布局 (parent_fragment.xml
):
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_
android:layout_
android:background="@android:color/white">
<FrameLayout
android:id="@+id/FRAGMENT_PLACEHOLDER"
android:layout_
android:layout_/>
</LinearLayout>
子片段:
public class ChildFragment extends Fragment implements View.OnClickListener
View v ;
@Override
public View onCreateView(LayoutInflater inflater,
@Nullable ViewGroup container, @Nullable Bundle savedInstanceState)
// TODO Auto-generated method stub
View rootView = inflater.inflate(R.layout.child_fragment, container, false);
v = rootView;
return rootView;
@Override
public void onClick(View view)
【讨论】:
【参考方案6】:我解决了这个问题。您可以使用支持库和ViewPager
。如果您不需要通过手势滑动,您可以禁用滑动。所以这里有一些代码来改进我的解决方案:
public class TestFragment extends Fragment
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
View v = inflater.inflate(R.layout.frag, container, false);
final ArrayList<Fragment> list = new ArrayList<Fragment>();
list.add(new TrFrag());
list.add(new TrFrag());
list.add(new TrFrag());
ViewPager pager = (ViewPager) v.findViewById(R.id.pager);
pager.setAdapter(new FragmentPagerAdapter(getChildFragmentManager())
@Override
public Fragment getItem(int i)
return list.get(i);
@Override
public int getCount()
return list.size();
);
return v;
P.S.这是用于测试的丑陋代码,但它改进了它是可能的。
P.P.S 内部片段ChildFragmentManager
应传递给ViewPagerAdapter
【讨论】:
【参考方案7】:这并不复杂。我们不能在这里使用getFragmentManager()
。对于在片段内使用片段,我们使用getChildFragmentManager()
。其余的都是一样的。
【讨论】:
【参考方案8】:使用 getChildFragmentManager(),点击链接: Nested Fragment
【讨论】:
【参考方案9】:您可以在片段中添加FrameLayout
,并在它初始化时将其替换为另一个片段。
这样,您可以认为另一个片段位于第一个片段中。
【讨论】:
【参考方案10】:目前在嵌套片段中,只有以编程方式生成嵌套片段才受支持!所以目前xml布局方案不支持嵌套片段布局!
【讨论】:
【参考方案11】:这可能对那些使用 Kotlin 的人有所帮助,您可以使用扩展功能 所以创建一个 kotlin 文件让我们说 "util.kt" 并添加这段代码
fun Fragment.addChildFragment(fragment: Fragment, frameId: Int)
val transaction = childFragmentManager.beginTransaction()
transaction.replace(frameId, fragment).commit()
假设这是 child 的类
class InputFieldPresentation: Fragment()
var views: View? = null
override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?,
savedInstanceState: Bundle?): View?
views = inflater!!.inflate(R.layout.input_field_frag, container, false)
return views
override fun onViewCreated(view: View?, savedInstanceState: Bundle?)
super.onViewCreated(view, savedInstanceState)
...
...
现在您可以像这样将孩子添加到父亲片段
FatherPresentation:Fragment()
...
override fun onViewCreated(view: View?, savedInstanceState: Bundle?)
super.onViewCreated(view, savedInstanceState)
val fieldFragment= InputFieldPresentation()
addChildFragment(fieldFragment,R.id.fragmet_field)
...
其中 R.id.fragmet_field 是包含片段的布局的 id。这个 lyout 当然是在父片段中。 这是一个例子
father_fragment.xml:
<LinearLayout android:layout_
android:layout_
android:orientation="vertical"
xmlns:android="http://schemas.android.com/apk/res/android"
>
...
<LinearLayout
android:layout_
android:layout_
android:gravity="center"
android:id="@+id/fragmet_field"
android:orientation="vertical"
>
</LinearLayout>
...
</LinearLayout>
【讨论】:
【参考方案12】:不支持MapFragment
,Android 团队表示自 Android 3.0 起正在开发它。 Here 有关该问题的更多信息但是您可以通过创建一个返回 MapActivity
的片段来做到这一点。 Here 是一个代码示例。感谢 inazaruk:
工作原理:
MainFragmentActivity 是扩展FragmentActivity
并托管两个 MapFragment 的 Activity。
MyMapActivity 扩展 MapActivity 并具有 MapView.
LocalActivityManagerFragment
主持 LocalActivityManager。
MyMapFragment 扩展 LocalActivityManagerFragment
并在 TabHost
的帮助下创建 MyMapActivity 的内部实例。
如果您有任何疑问,请告诉我
【讨论】:
【参考方案13】:您好,我通过将每个 Fragment 放入不同的布局中解决了这个问题。并且我使相关的布局可见,而其他可见性消失了。
我的意思是:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_
android:layout_>
<LinearLayout android:id="@+id/linearLayout1"
android:layout_
android:layout_
android:orientation="horizontal">
<Button android:layout_
android:id="@+id/button1"
android:layout_
android:text="Button1"></Button>
<Button android:text="Button2"
android:id="@+id/button2"
android:layout_
android:layout_></Button>
<Button android:text="Button3"
android:id="@+id/button3"
android:layout_
android:layout_></Button>
<Button android:text="Button4"
android:id="@+id/button4"
android:layout_
android:layout_></Button>
</LinearLayout>
<LinearLayout android:layout_
android:layout_
android:layout_weight="1"
android:id="action_For_Button1"
android:visibility="visible">
<Fragment android:layout_
android:layout_
android:id="fragment1"
.
.
.
/ >
</LinearLayout>
<LinearLayout android:layout_
android:layout_
android:id="action_For_Button1"
android:layout_weight="1"
android:visibility="gone">
<Fragment android:layout_
android:layout_
android:id="fragment2"
.
.
.
/ >
</LinearLayout>
.
.
.
</LinearLayout>
我假设您将在单击按钮 1 时打开您的页面。您可以控制片段在点击操作时的可见性。您可以使相关布局可见而其他布局消失,并且通过片段管理器您可以获取片段。这种方法有效对我来说。而且由于 具有可见性的视图:已消失是不可见的,并且它不占用任何空间用于布局目的,我认为这种方法不会导致任何空间问题。
P.S:我只是想解释一下我的解决方案代码可能有语法错误或结构不完整。
【讨论】:
以上是关于片段里面的片段的主要内容,如果未能解决你的问题,请参考以下文章