Fragment Inside Fragment
I need help regarding working on fragment inside fragment, actually I am facing a problem on pressing back button. Application Main screen has buttons and pressing on each button view replace with new fragment(and that fragment contain inside another fragment), dynamically adding/replacing fragment is working fine, by pressing button1 fragment replaced, same happens when pressing button, but if I press the button again, got an exception:
"Duplicate id 0x7f05000a, tag null, or parent id 0x7f050009 with
another fragment for com........ fragmentname"
means fragment or inner fragments are already added and I am trying to add them again, anybody has idea how to work with fragment inside fragment and moving back and forth without any problem, thanks for the support.
MainActivity, where fragments are dynamical added and replaced.
public class FragmentInsideFragmentTestActivity extends Activity {
private Button button1;
private Button button2;
private B开发者_如何学编程utton 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);
}
}
}
Here is Layout: main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical">
<LinearLayout
android:id="@+id/linearLayout1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="@+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button1" />
<Button
android:id="@+id/button2"
android:text="Button2"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<Button
android:id="@+id/button3"
android:text="Button3"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<Button
android:id="@+id/button4"
android:text="Button4"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
<LinearLayout
android:id="@+id/linearLayout2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" />
</LinearLayout>
Hope I tried to clear my problem.
AFAIK, fragments cannot hold other fragments.
UPDATE
With current versions of the Android Support package -- or native fragments on API Level 17 and higher -- you can nest fragments, by means of getChildFragmentManager()
. Note that this means that you need to use the Android Support package version of fragments on API Levels 11-16, because even though there is a native version of fragments on those devices, that version does not have getChildFragmentManager()
.
I needed some more context, so I made an example to show how this is done. The most helpful thing I read while preparing was this:
- Creating and Using Fragments
Activity
activity_main.xml
Add a FrameLayout
to your activity to hold the parent fragment.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Activity"/>
<FrameLayout
android:id="@+id/parent_fragment_container"
android:layout_width="match_parent"
android:layout_height="200dp"/>
</LinearLayout>
MainActivity.java
Load the parent fragment and implement the fragment listeners. (See 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");
}
}
Parent Fragment
fragment_parent.xml
Add another FrameLayout
container for the child fragment.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="20dp"
android:background="#91d0c2">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Parent fragment"/>
<FrameLayout
android:id="@+id/child_fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent">
</FrameLayout>
</LinearLayout>
ParentFragment.java
Use getChildFragmentManager
in onViewCreated
to set up the child fragment.
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);
}
}
Child Fragment
fragment_child.xml
There is nothing special here.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="20dp"
android:background="#f1ff91">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Child fragment"/>
</LinearLayout>
ChildFragment.java
There is nothing too special here, either.
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);
}
}
Notes
- The support library is being used so that nested fragments can be used before Android 4.2.
Since Android 4.2 (API 17) nested fragments become available http://developer.android.com/about/versions/android-4.2.html#NestedFragments
To place fragment inside other fragment use getChildFragmentManager()
It also available in support library!
you can use getChildFragmentManager()
function.
example:
Parent fragment :
@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 layout (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_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/white">
<FrameLayout
android:id="@+id/FRAGMENT_PLACEHOLDER"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
Child Fragment:
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) {
}
}
Fragments can be added inside other fragments but then you will need to remove it from parent Fragment each time when onDestroyView()
method of parent fragment is called. And again add it in Parent Fragment's onCreateView()
method.
Just do like this :
@Override
public void onDestroyView()
{
FragmentManager mFragmentMgr= getFragmentManager();
FragmentTransaction mTransaction = mFragmentMgr.beginTransaction();
Fragment childFragment =mFragmentMgr.findFragmentByTag("qa_fragment")
mTransaction.remove(childFragment);
mTransaction.commit();
super.onDestroyView();
}
I solved this problem. You can use Support library and ViewPager
. If you don't need swiping by gesture you can disable swiping. So here is some code to improve my solution:
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.It is ugly code for test, but it improves that it is possible.
P.P.S Inside fragment ChildFragmentManager
should be passed to ViewPagerAdapter
It's nothing complicated. We cannot use getFragmentManager()
here. For using Fragments inside a Fragment, we use getChildFragmentManager()
. Rest will be the same.
Use getChildFragmentManager(), follow the link : Nested Fragment
You can add FrameLayout
to the fragment and replace it with another fragment when it initializes.
This way , you could consider the other fragment to be inside the first fragment.
Curently in nested fragment, the nested one(s) are only supported if they are generated programmatically! So at this time no nested fragment layout are supported in xml layout scheme!
That may help those who works on Kotlin you can use extension function so create a kotlin file let's say "util.kt" and add this piece of code
fun Fragment.addChildFragment(fragment: Fragment, frameId: Int) {
val transaction = childFragmentManager.beginTransaction()
transaction.replace(frameId, fragment).commit()
}
Let's say this is the class of the 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)
...
}
...
}
Now you can add the children to the father fragment like this
FatherPresentation:Fragment()
{
...
override fun onViewCreated(view: View?, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val fieldFragment= InputFieldPresentation()
addChildFragment(fieldFragment,R.id.fragmet_field)
}
...
}
where R.id.fragmet_field is the id of the layout which will contain the fragment.This lyout is inside the father fragment of course. Here is an example
father_fragment.xml:
<LinearLayout android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
xmlns:android="http://schemas.android.com/apk/res/android"
>
...
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:id="@+id/fragmet_field"
android:orientation="vertical"
>
</LinearLayout>
...
</LinearLayout>
There is no support for MapFragment
, Android team says is working on it since Android 3.0. Here more information about the issue But what you can do is by creating a Fragment that returns a MapActivity
. Here is a code example. Thanks to inazaruk:
How it works:
- MainFragmentActivity is the activity that extends
FragmentActivity
and hosts two MapFragments. - MyMapActivity extends MapActivity and has
MapView.
LocalActivityManagerFragment
hosts LocalActivityManager.- MyMapFragment extends
LocalActivityManagerFragment
and with help ofTabHost
creates internal instance of MyMapActivity.
If you have any doubt please let me know
Hi I solved this problem by putting per Fragment into distinct layout.And I made just related Layout visible and made the others visibilities gone.
I mean:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<LinearLayout android:id="@+id/linearLayout1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button android:layout_width="wrap_content"
android:id="@+id/button1"
android:layout_height="wrap_content"
android:text="Button1"></Button>
<Button android:text="Button2"
android:id="@+id/button2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"></Button>
<Button android:text="Button3"
android:id="@+id/button3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"></Button>
<Button android:text="Button4"
android:id="@+id/button4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"></Button>
</LinearLayout>
<LinearLayout android:layout_width="full_screen"
android:layout_height="0dp"
android:layout_weight="1"
android:id="action_For_Button1"
android:visibility="visible">
<Fragment android:layout_width="full_screen"
android:layout_height="full_screen"
android:id="fragment1"
.
.
.
/ >
</LinearLayout>
<LinearLayout android:layout_width="full_screen"
android:layout_height="0dp"
android:id="action_For_Button1"
android:layout_weight="1"
android:visibility="gone">
<Fragment android:layout_width="full_screen"
android:layout_height="full_screen"
android:id="fragment2"
.
.
.
/ >
</LinearLayout>
.
.
.
</LinearLayout>
I assumed that you will open your page as button 1 is clicked.You can control your fragment's visibilities on click action.You can make related Layout visible and the others gone and by Fragment Manager you can take your fragment.This approach worked for me.And since view that has visibility:gone is invisible, and it doesn't take any space for layout purposes I think this approach does not cause any space problem.
P.S:I just tried to explain my solution code may have syntax mistakes or uncompleted structure.
精彩评论