I am trying to implement transitions between fragments which have "shared elements" as described in the new material design specs.
The only method I can find is the ActivityOptionsCompat.makeSceneTransitionAnimation, which I believe works on Activity only.
I've been searching for this same functionality but with/for fragments.
I had the same problem but had it working by adding a new fragment from another fragment.
The following link is very helpful in getting started on this: https://developer.android.com/training/material/animations.html#Transitions
Following is my code that works. I'm animating an ImageView from one fragment to the other.
Make sure the View you want to animate has the same android:transitionName in both fragments.
The other content doesn't really matter.
As a test, you could copy this to both your layout xml files. Make sure the image exists.
<ImageView
android:transitionName="MyTransition"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
android:src="#drawable/test_image" />
Then I have 1 file in my res/transition folder, named change_image_transform.xml.
<?xml version="1.0" encoding="utf-8"?>
<transitionSet xmlns:android="http://schemas.android.com/apk/res/android">
<changeImageTransform />
</transitionSet>
Now you can get started. Lets say you have Fragment A containing the image and want to add Fragment B.
Run this in Fragment A:
#Override
public void onClick(View v) {
switch(v.getId()) {
case R.id.product_detail_image_click_area:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
setSharedElementReturnTransition(TransitionInflater.from(getActivity()).inflateTransition(R.transition.change_image_transform));
setExitTransition(TransitionInflater.from(getActivity()).inflateTransition(android.R.transition.explode));
// Create new fragment to add (Fragment B)
Fragment fragment = new ImageFragment();
fragment.setSharedElementEnterTransition(TransitionInflater.from(getActivity()).inflateTransition(R.transition.change_image_transform));
fragment.setEnterTransition(TransitionInflater.from(getActivity()).inflateTransition(android.R.transition.explode));
// Our shared element (in Fragment A)
mProductImage = (ImageView) mLayout.findViewById(R.id.product_detail_image);
// Add Fragment B
FragmentTransaction ft = getFragmentManager().beginTransaction()
.replace(R.id.container, fragment)
.addToBackStack("transaction")
.addSharedElement(mProductImage, "MyTransition");
ft.commit();
}
else {
// Code to run on older devices
}
break;
}
}
The shared element fragment transitions do work with ListViews, as long as the source and target views have the same (and unique) transitionName.
If you make your list view adapter to set unique transitionNames to the views you want (e.g. some constant + specific item id) and also change your detail fragment to set the same transitionNames to the target views at runtime (onCreateView), the transitions actually work!
Shared elements do work with Fragments but there are some things to keep in mind:
Don't try to set the sharedElementsTransition in the onCreateView of your Fragment. You have to define them when creating an instance of your Fragment or in onCreate.
Take note of the official documentation on the possible animations for enter/exit transitions & sharedElementTransition. They are not the same.
Trial and error :)
This should be a comment to the accepted answer, as I am unable to comment on it.
The accepted answer (by WindsurferOak and ar34z) works, except for a "minor" problem which caused a null pointer exception when navigating up with the backStack. It seems that setSharedElementReturnTransition() should be called on the target fragment instead of the original fragment.
So instead of:
setSharedElementReturnTransition(TransitionInflater.from(getActivity()).inflateTransition(R.transition.change_image_transform));
it should be
fragment.setSharedElementReturnTransition(TransitionInflater.from(getActivity()).inflateTransition(R.transition.change_image_transform));
https://github.com/tevjef/Rutgers-Course-Tracker/issues/8
Following are some helpful resources:
https://github.com/lgvalle/Material-Animations
http://www.androiddesignpatterns.com/2014/12/activity-fragment-transitions-in-android-lollipop-part1.html
https://www.youtube.com/watch?v=5e1Yh0fSZhQ
The key is to use a custom transaction with
transaction.addSharedElement(sharedElement, "sharedImage");
Shared Element Transition Between Two Fragments
In this example, one of two different ImageViews should be translated from the ChooserFragment to the DetailFragment.
In the ChooserFragment layout we need the unique transitionName attributes:
<ImageView
android:id="#+id/image_first"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/ic_first"
android:transitionName="fistImage" />
<ImageView
android:id="#+id/image_second"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/ic_second"
android:transitionName="secondImage" />
In the ChooserFragments class, we need to pass the View which was clicked and an ID to the parent Activity wich is handling the replacement of the fragments (we need the ID to know which image resource to show in the DetailFragment). How to pass information to a parent activity in detail is surely covered in another documentation.
view.findViewById(R.id.image_first).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if (mCallback != null) {
mCallback.showDetailFragment(view, 1);
}
}
});
view.findViewById(R.id.image_second).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if (mCallback != null) {
mCallback.showDetailFragment(view, 2);
}
}
});
In the DetailFragment, the ImageView of the shared element also needs the unique transitionName attribute.
<ImageView
android:id="#+id/image_shared"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:transitionName="sharedImage" />
In the onCreateView() method of the DetailFragment, we have to decide which image resource should be shown (if we don't do that, the shared element will disappear after the transition).
public static DetailFragment newInstance(Bundle args) {
DetailFragment fragment = new DetailFragment();
fragment.setArguments(args);
return fragment;
}
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
View view = inflater.inflate(R.layout.fragment_detail, container, false);
ImageView sharedImage = (ImageView) view.findViewById(R.id.image_shared);
// Check which resource should be shown.
int type = getArguments().getInt("type");
// Show image based on the type.
switch (type) {
case 1:
sharedImage.setBackgroundResource(R.drawable.ic_first);
break;
case 2:
sharedImage.setBackgroundResource(R.drawable.ic_second);
break;
}
return view;
}
The parent Activity is receiving the callbacks and handles the replacement of the fragments.
#Override
public void showDetailFragment(View sharedElement, int type) {
// Get the chooser fragment, which is shown in the moment.
Fragment chooserFragment = getFragmentManager().findFragmentById(R.id.fragment_container);
// Set up the DetailFragment and put the type as argument.
Bundle args = new Bundle();
args.putInt("type", type);
Fragment fragment = DetailFragment.newInstance(args);
// Set up the transaction.
FragmentTransaction transaction = getFragmentManager().beginTransaction();
// Define the shared element transition.
fragment.setSharedElementEnterTransition(new DetailsTransition());
fragment.setSharedElementReturnTransition(new DetailsTransition());
// The rest of the views are just fading in/out.
fragment.setEnterTransition(new Fade());
chooserFragment.setExitTransition(new Fade());
// Now use the image's view and the target transitionName to define the shared element.
transaction.addSharedElement(sharedElement, "sharedImage");
// Replace the fragment.
transaction.replace(R.id.fragment_container, fragment, fragment.getClass().getSimpleName());
// Enable back navigation with shared element transitions.
transaction.addToBackStack(fragment.getClass().getSimpleName());
// Finally press play.
transaction.commit();
}
Not to forget - the Transition itself. This example moves and scales the shared element.
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
public class DetailsTransition extends TransitionSet {
public DetailsTransition() {
setOrdering(ORDERING_TOGETHER);
addTransition(new ChangeBounds()).
addTransition(new ChangeTransform()).
addTransition(new ChangeImageTransform());
}
}
I searched for SharedElement in fragments and I find very useful source code on GitHub.
1.first you should define transitionName for your Objects(Like ImageView) in both Fragments layout(We add a button in fragment A for handling click event):
fragment A:
<ImageView
android:id="#+id/fragment_a_imageView"
android:layout_width="128dp"
android:layout_height="96dp"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:layout_marginBottom="80dp"
android:scaleType="centerCrop"
android:src="#drawable/gorilla"
android:transitionName="#string/simple_fragment_transition />
<Button
android:id="#+id/fragment_a_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:layout_marginBottom="24dp"
android:text="#string/gorilla" />
fragment B:
<ImageView
android:id="#+id/fragment_b_image"
android:layout_width="match_parent"
android:layout_height="250dp"
android:scaleType="centerCrop"
android:src="#drawable/gorilla"
android:transitionName="#string/simple_fragment_transition" />
Then you should write this code in your transition file in transition Directory(if you haven't this Directory so create One: res > new > Android Resource Directory > Resource Type = transition > name = change_image_transform ):
change_image_transform.xml:
<?xml version="1.0" encoding="utf-8"?>
<transitionSet xmlns:android="http://schemas.android.com/apk/res/android">
<changeBounds/>
<changeTransform/>
<changeClipBounds/>
<changeImageTransform/>
</transitionSet>
In the last step you should complete codes in java:
fragment A:
public class FragmentA extends Fragment {
public static final String TAG = FragmentA.class.getSimpleName();
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_a, container, false);
}
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
final ImageView imageView = (ImageView) view.findViewById(R.id.fragment_a_imageView);
Button button = (Button) view.findViewById(R.id.fragment_a_btn);
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
getFragmentManager()
.beginTransaction()
.addSharedElement(imageView, ViewCompat.getTransitionName(imageView))
.addToBackStack(TAG)
.replace(R.id.content, new FragmentB())
.commit();
}
});
}
}
fragment B:
public class FragmentB extends Fragment {
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setSharedElementEnterTransition(TransitionInflater.from(getContext()).inflateTransition(android.R.transition.move));
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_b, container, false);
}
}
don't forget to show your "A" fragment in your activity:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getSupportFragmentManager()
.beginTransaction()
.add(R.id.content, new SimpleFragmentA())
.commit();
}
source : https://github.com/mikescamell/shared-element-transitions
How to start shared element transition using Fragments?
I assume you want to transition of your Image using Fragment (instead of Activity)
it wont work perfectly if you have already set AppTheme
keep the transition name of source and destination same
You have to do three things for transition:
1.Set transitionName to the source View(xml or programatically) -> before calling makeFragmentTransition
private void setImageZoom(boolean isImageZoom) {
ImageView imageView = this.findViewById(R.id.image);
if (isImageZoom) {
imageView.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
ViewCompat.setTransitionName(imageView, "imageTransition");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
makeFragmentTransition(imageView);
}
}
});
}
}
2.Fragment Transition
Set TransitionSet for the specicific Transition animation
apply them on Fragment
call addSharedElement(View, transitionName) while fragmentTransition
#RequiresApi(Build.VERSION_CODES.LOLLIPOP)
public void makeFragmentTransition(ImageView sourceTransitionView) {
//transtionName for sourceView
//MUST set transitionName before calling this method(programattically or give ->transitionName to the view in xml)
String sourceTransitionName = ViewCompat.getTransitionName(sourceTransitionView);
TransitionSet transitionSet = new TransitionSet();
transitionSet.setDuration(500);
transitionSet.addTransition(new ChangeBounds()); //to expand boundaries
transitionSet.addTransition(new ChangeTransform()); //for transtion vertically
transitionSet.addTransition(new ChangeImageTransform()); // image transform work
transitionSet.setOrdering(TransitionSet.ORDERING_TOGETHER);
ImageTransitionFragment fragment = new ImageTransitionFragment();
fragment.setSharedElementEnterTransition(transitionSet);
fragment.setSharedElementReturnTransition(transitionSet);
fragment.setAllowReturnTransitionOverlap(false);
try {
getHostActivity().getSupportFragmentManager()
.beginTransaction()
//sharedElement is set here for fragment
//it will throw exception if transitionName is not same for source and destionationView
.addSharedElement(sourceTransitionView, sourceTransitionName)
//R.id.fragmentView is the View in activity on which fragment will load...
.replace(R.id.fragmentView, fragment)
.addToBackStack(null)
.commit();
} catch (Exception e) {
//
String string = e.toString();
}
}
3.set desitionNation transitionName in ImageView
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/destionationTransitionPage"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:transitionName="#string/pageTransition"
android:background="#color/black_color">
<com.android.foundation.ui.component.FNImageView
android:id="#+id/destinationImageView"
android:layout_width="#dimen/_400dp"
android:layout_gravity="center"
android:transitionName="imageTransition"
android:layout_height="#dimen/_400dp" />
</FrameLayout>
Please respond if anything is not clear or it need more improvement
Related
I created an application in which i had 1 ACTIVITY and 8 Fragment in which i am inflating every fragment into the activity. but my problem is that every fragment is overlapping each other. I change the background color of every fragment but when i am clicking the vacant spaces button of my previous fragment get clicked.
please suggest me some thing to get out of this situation.
Main_Activity.class
public class ActRegistration extends AppCompatActivity {
FragmentManager fm;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
setContentView(R.layout.activity_registration);
fm = getSupportFragmentManager();
loadFragment(new FragmentRegLogSkip(), FragmentRegLogSkip.FRAGMENT_KEY);
}
public void loadFragment(Fragment fragmentName, String fragmentKey) {
FragmentTransaction fragmentTransaction = fm.beginTransaction();
fragmentTransaction.add(R.id.id_registration_layout_frame, fragmentName);
fragmentTransaction.addToBackStack(fragmentKey);
fragmentTransaction.commit();
}
}
Frag_1.class
public class FragmentRegLogSkip extends Fragment implements View.OnClickListener {
private Activity activity;
private Context context;
public static final String FRAGMENT_KEY = "fragment_reg_log_skip";
#Override
public View onCreateView(#NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
context = getContext();
LanguageChanger.changeLanguage(context);
View view = inflater.inflate(R.layout.act_registration_screen_login, container, false);
return view;
}
}
Don't forget to add android:clickable="true" and android:focusable="true" to the parent view of your second fragment so it catches the clicks and they don't get propagated to the fragment below. Something like this:
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clickable="true"
android:focusable="true">
You're using fragmentTransaction.add(), which won't remove the previous Fragment - use fragmentTransaction.replace() if you want the other Fragment's view to be removed.
Note FragmentManager automatically restores any previously added Fragments when your Activity is recreated (i.e., when savedInstanceState != null). Therefore you'll want to ensure that you only add your initial Fragment once - when the savedInstanceState is null.
if (savedInstanceState == null) {
loadFragment(new FragmentRegLogSkip(), FragmentRegLogSkip.FRAGMENT_KEY);
}
Inside your second fragment layout file's Parent Tag , add this :-
android:clickable="true"
android:focusable="true"
then when you click on any view , it will not click previous fragment.
I am using the new Lollipop api to setEnterTransition on a fragment and then add a shared element transition for an image in the fragment. The desired behavior is first, the image should move to its position, after which the rest of the views in the fragment should fade in.
However, the enterTransition is being applied to the shared element so it is fading in with the rest of the views. If I don't set the enterTransition, then the image moves properly but it while it is moving the other content is already visible.
How do I get it to not apply the enterTransition to the shared view?
I found this commit in the AOSP that seems like it should address this issue, but it doesn't seem to be working.
Here is sample code:
public class Fragment1 extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.main_fragment, container, false);
final ImageView imageView = (ImageView) rootView.findViewById(R.id.image);
final Button button = (Button) rootView.findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
TransitionSet transitionSet = new TransitionSet();
transitionSet.addTransition(new ChangeImageTransform());
transitionSet.addTransition(new ChangeBounds());
transitionSet.setDuration(300);
Fragment fragment2 = new Fragment2();
fragment2.setSharedElementEnterTransition(transitionSet);
fragment2.setSharedElementReturnTransition(transitionSet);
Fade fade = new Fade();
fade.setStartDelay(300);
fragment2.setEnterTransition(fade);
getFragmentManager().beginTransaction()
.replace(R.id.container, fragment2)
.addSharedElement(imageView, "SharedImage")
.commit();
}
});
return rootView;
}
}
The enter transition should not apply the the shared element views. The most likely scenario is that your shared element is within another view with a background, making that view affected by the enter transition. That's a situation like this:
<FrameLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#FFF"
>
<ImageView android:src="#drawable/pretty_picture"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:transitionName="picture"
android:padding="20dp"/>
</FrameLayout>
Here, the ImageView is the shared element.
If that happens, you'll want to add a magic transition: ChangeTransform. If it detects that the parent changes, it will pull out the shared element from the parent and transition it separately.
I asked a question about how to add a Fragment that contained something drawn using OpenGL ES
here. Someone was kind enough to answer that for me, but unfortunately today I encountered another problem. As I mentioned in my other question, my purpose is to add other Fragments next to the one that contains OpenGL and because I am a beginner in Android development I don't seem to understand how this is done.
Here's what I want: right now, my code is exactly the one from my other question. I also have this Fragment:
public class TextFragment extends Fragment
{
private TextView textview;
#Override
public View onCreateView(LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState)
{
View view = inflater.inflate(R.layout.text_fragment,
container, false);
textview = (TextView) view.findViewById(R.id.textView1);
return view;
}
}
together with its layout:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/frag2">
<TextView
android:id="#+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:text="Fragment Two"
android:textAppearance="?android:attr/textAppearanceLarge" />
</RelativeLayout>
and I want to add this to my main activity, where right now I only have the OpenGL Fragment. Here's my main activity:
public class FragmentExampleActivity extends FragmentActivity implements ToolbarFragment.ToolbarListener
{
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getSupportFragmentManager().addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener()
{
public void onBackStackChanged()
{
int backCount = getSupportFragmentManager().getBackStackEntryCount();
if (backCount == 0)
{
finish();
}
}
});
if (savedInstanceState == null)
{
getSupportFragmentManager()
.beginTransaction()
.add(R.id.main_container, new OpenGLES20ActivityFrag())
.addToBackStack(null)
.commit();
}
}
}
and the Fragment that has OpenGL in it and that I have already added to the main activity:
public class OpenGLES20ActivityFrag extends Fragment
{
private GLSurfaceView mGLView;
public OpenGLES20ActivityFrag()
{
super();
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
mGLView = new MyGLSurfaceView(this.getActivity());
return mGLView;
}
}
What I tried and failed: using another call to the .add method inside getSupportFragmentManager() or adapting this bit of code for my second Fragment
getSupportFragmentManager()
.beginTransaction()
.add(R.id.frag2, TextFragment)
.addToBackStack(null)
.commit();
that gave me an 'expression expected' error in the add method. I tried adding this constructor to my second Fragment
public TextFragment()
{
super();
}
and then inside the add method I put .add(R.id.frag2, new TextFragment())
which still didn't work.
In order to dynamically add a Fragment to a layout, what you need is a container (like in your case, it was R.id.main_container). Thus, if you want to add multiple fragments, what you need is multiple containers, like so:
<LinearLayout android:orientation="vertical" android:layout_height="fill_parent" android:layout_width="fill_parent">
<FrameLayout android:id="#+id/main_container_1" android:layout_weight="1" android:layout_height="fill_parent" android:layout_width="fill_parent"/>
<FrameLayout android:id="#+id/main_container_2" android:layout_weight="1" android:layout_height="fill_parent" android:layout_width="fill_parent"/>
</LinearLayout>
(this snippet is from How to split the screen with two equal LinearLayouts? )
And then you would need to add the two Fragments:
if (savedInstanceState == null)
{
getSupportFragmentManager()
.beginTransaction()
.add(R.id.main_container_1, new OpenGLES20ActivityFrag())
.commit();
getSupportFragmentManager()
.beginTransaction()
.add(R.id.main_container_2, new TextFragment())
.commit();
}
Please note that with multiple Fragments on a single Activity, it's better not to add them to the backstack, because then you'd have to press Back as many times as there are Fragments, and in this case it's more reasonable to navigate between the "views" or states of the application with Activities, and not by replacing the Fragments.
(considering the backstack doesn't change, I don't think the backstack listener needs to be removed, but that's done so that if you press Back, you don't end the Activity, but the Fragments within it first if you have them added to the backstack. But the Activity doesn't end when it contains no fragments, and you'd have an "empty view", hence why that was added.)
Please also check if the rotation works and data is maintained even after the activity reconstruction, because there's a chance you need to set the retain instance state to true explicitly on the Fragments for that to work.
I have 2 tabs in my app.Each have single fragment initially.
Scenario 1(Tab 1):
I 'm adding a second fragment in the tab on Button Click from the first fragment & again a third fragment on button click from second fragment.
Scenario 2(Tab 2):
Similar as tab1
Scenario 2(Switch between the Tab):
As I switch between the tab.I'm unable to get same tab fragment which I have added in the respective tab.
Below is my code:(First Fragment Tab1)
public class ArticleFragment extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
ViewGroup view=(ViewGroup) inflater.inflate(R.layout.article_fragment, container,false);
fragmentTabHost.setup(getActivity(), getChildFragmentManager() , R.id.frag_container);
Button article_button = (Button) view.findViewById(R.id.article_btn);
article_button.setOnClickListener( new OnClickListener() {
#Override
public void onClick(View v) {
FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();
article_txt.setText("Changed Value");
ArticleDetails articleFragment = new ArticleDetails();
fragmentTransaction.add(ScreenFragmentActivity.getContainertId(), articleFragment, AppConstants.ARTICLE_FRAGMENT_DETAILS);
fragmentTransaction.hide(ArticleFragment.this);
fragmentTransaction.addToBackStack(ArticleFragment.class.getName());
fragmentTransaction.commit();
}
});
return view;
}
}
Second Fragment Tab1:
public class ArticleDetails extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
ViewGroup view=(ViewGroup) inflater.inflate(R.layout.article_details, container,false);
Button article_button = (Button) view.findViewById(R.id.article_details_btn);
article_button.setOnClickListener( new OnClickListener() {
#Override
public void onClick(View v) {
FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();
ArticleDetailsWithIn articleFragment = new ArticleDetailsWithIn();
fragmentTransaction.add(ScreenFragmentActivity.getContainertId(), articleFragment,
AppConstants.ARTICLE_FRAGMENT_DETAILS_WITHIN);
fragmentTransaction.hide(ArticleDetails.this);
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();
}
});
return view;
}
My Layout:
<android.support.v4.app.FragmentTabHost
android:id="#+id/tabhost"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<TabWidget
android:id="#+id/tabs"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="0"
android:orientation="horizontal" />
<FrameLayout
android:id="#+id/tabcontent"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
</LinearLayout>
</android.support.v4.app.FragmentTabHost>
So Basically I'm trying to add the new Fragment within the tab .But on Switch I'm not able to retain my respective fragment
You can try this - tag your fragments with some name and store this in any static helper class using getter/setter method. Make your first fragment tag name as default (this is for first time launch). Then at place where you have logic to add new fragments, put the required if-else statements. Same goes for other tab also. (you can even use shared preferences, if you want to remember last seen fragment even when app is closed and restarted again.)
Create a static helper class.
public class HelperClass {
private static string TabOneFragmentOnScreen = "";
public static String getTabOneFragmentOnScreen () {
return TabOneFragmentOnScreen ;
}
public static void setTabOneFragmentOnScreen(
String TabOneFragmentOnScreen) {
HelperClass.TabOneFragmentOnScreen = TabOneFragmentOnScreen;
}
}
When you click one TabOne
if(HelperClass.getTabOneFragmentOnScreen.equals("")){
HelperClass.setTabOneFragmentOnScreen("FragmentOne");
//logic to add fragment one goes here
}
if(HelperClass.getTabOneFragmentOnScreen.equals("FragmentTwo")){
HelperClass.setTabOneFragmentOnScreen("FragmentTwo");
//logic to add fragment two goes here
}
This way it will always remember the last fragment on screen under tab one.
This code is not complete, but you can develop further on this logic.
I am trying to use Android fragments in a very simple way, similar to the tutorial on the Android developer website.
I have an Activity (MediaInfoActivity) with the following code:
public class MediaInfoActivity extends FragmentActivity {
private final String TAG = "MediaInfoActivity";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.i(TAG, "onCreate()");
setContentView(R.layout.media_info_activity_layout);
}
}
Here is the code for the media_info_activity_layout.xml file:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<fragment class="com.hawkforce.test.MediaInfoFragment"
android:id="#+id/mediaInfoFragment"
android:layout_weight="1"
android:layout_width="match_parent"
android:layout_height="0dp" />
<FrameLayout android:id="#+id/mediaPlayerBarPanel"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<fragment class="com.hawkforce.test.MediaPlayerBarFragment"
android:id="#+id/mediaPlayerBar"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</FrameLayout>
And finally here is the code for MediaInfoFragment:
public class MediaInfoFragment extends Fragment {
private final static String TAG = "MediaInfoFragment";
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.i(TAG, "onCreate()");
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
Log.i(TAG, "onCreateView()");
if (container == null) {
Log.i(TAG, "onCreateView(): container = null");
}
return inflater.inflate(R.layout.media_info_fragment_layout, container, false);
}
}
Here is my problem : the container passed in the onCreateView() method of the MediaInfoFragment is null. As I understood, this should only be the case for non-UI Fragments. However, my Fragment has a UI, which is displayed OK on the screen when I launch MediaInfoActivity. It causes problems because no style declared in the xml layout file of the fragment is applied.
Here is my Log:
I/MediaInfoActivity: onCreate()
I/MediaInfoFragment: onCreate()
I/MediaInfoFragment: onCreateView()
I/MediaInfoFragment: onCreateView(): container = null
Am I missing anything obvious here ?
You just have to create a inflater like bellow in your fragment.
View rootView;
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
if (rootView == null) {
rootView = inflater.inflate(R.layout.activity_my_cart, null);
} else {
((ViewGroup) container.getParent()).removeView(rootView);
}
return rootView;
}
I hope it will work as per your question.
I am not sure since I don't have the code of the SDK in front of me but I think that the life-cycle of calling Fragment "onCreateView" function is different between the two cases:
1. Static settings of fragment in layout
2. Loading pragmatically with FragmentManager.
In the first case the debugger get into Fragment.onCreateView() method immediately upon adding the content view to the parent activity as part of onCreate() code:
When calling: setContentView(R.layout.some_layoue);
You will see the debugger get into Fragment.onCreateView() before going to next line
In the second case the Fragment.onCreateView() is being invoked only after the onCreate() of the activity is finished.
This looks like design bug for me but possibly as design feature.
Anyway the container is null when adding fragment statically because the related object was not yet created.
In fact the difference between the two situations is much deeper. In the case of static fragments toggling between fragments will not create the view hierarchy correctly.
For example if you will add button-A to fragment A and button-B to Fragment-B and toggle the fragments with a code looks like this (highlighting only the relevant code):
public void turnOnFragment() {
FragmentManager manager = getFragmentManager();
if (manager != null) {
manager.beginTransaction()
.setCustomAnimations(android.R.animator.fade_in, android.R.animator.fade_out)
.attach(this)
.commit();
}
}
public void turnOffFragment() {
FragmentManager manager = getFragmentManager();
if (manager != null) {
manager.popBackStackImmediate(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
manager.beginTransaction()
.setCustomAnimations(android.R.animator.fade_in, android.R.animator.fade_out)
.detach(this)
.commit();
}
}
You will see that in the case of static fragments the buttons from both fragments are presented although turning on and off. If however fragments are added programatically the toggle works fine and view hierarchy is cleaned and show only button from relevant fragment.
This is based of my experience with version 4.4.2