Android Fragment Shared Element transition without fragmentTransaction.replace() - android

I can not do exactly what I want with the transitions to the initialization of my fragments:
val fragmentTransaction = fragmentManager.beginTransaction()
val fragments = fragmentManager.fragments
if (fragments.isNotEmpty())
for (oldFragment in fragments)
if (oldFragment.isAdded && (!second || oldFragment.tag?.substring(0, userId.length)!!.contains(fragmentTag)))
fragmentTransaction.hide(oldFragment)
//create or show
var fragment = fragmentManager.findFragmentByTag(fragmentTag + userId) as? F_Base
if (fragment == null || mKillFrags.contains(fragmentTag) || killFragment) {
if (fragment != null)
fragmentTransaction.remove(fragment)
fragment = createFragmentByTag(fragmentTag, bundle)
if (fragment == null) {
UIUtils.broadcastMessage(R.string.error)
return false
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
fragment.sharedElementEnterTransition = DetailsTransition()
fragment.sharedElementReturnTransition = null
fragment.enterTransition = Fade()
fragment.exitTransition = null
}
//todo test animation
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && sharedView.isNotEmpty()) {
for (view in sharedView)
fragmentTransaction.addSharedElement(view, view.transitionName.substring(0, view.transitionName.lastIndexOf("_")))
fragmentTransaction.replace(content.id, fragment, fragmentTag)
} else
fragmentTransaction.add(content.id, fragment, fragmentTag + userId)
} else {
fragmentTransaction.show(fragment)
}
fragmentTransaction.commitAllowingStateLoss()
In this case the transitions work because the initialization of the fragments pass by fragmentTransaction.replace() but I do not want to destroy my current fragment, except that if I leave fragmentTransaction.add() my transition is not taken.
Do you have a solution ?

Related

Fragment's method from Activity Callback is not calling

I have done as below :
Implemented Listener in Activity as MyCallBack.MyListener
Initialized it in activity as below :
App.Manager.myCallback?.myListener(this)
Below is the method implemented :
override fun onGettingData(data: MyData) {
var fragment1 = supportFragmentManager.findFragmentById(R.id.container) as Fragment1
if (fragment1 != null && fragment1.isVisible) {
fragment1.onDataReceived(data)
}
var fragment2 = supportFragmentManager.findFragmentById(R.id.container) as Fragment2
if (fragment2 != null && fragment2.isVisible) {
fragment2.onDataReceived(data)
}
}
But this method inside my fragments is not getting called.
Solution :
for (fragment in supportFragmentManager.fragments) {
if (fragment is Fragment1) {
fragment.onDataReceived(data)
} else if (fragment is Fragment2) {
fragment.onDataReceived(data)
}
}

Bottom Navigation & Fragment Recreation

I have a bottom navigation which runs on my Main Activity
private void BottomNavigation_NavigationItemSelected(object sender, BottomNavigationView.NavigationItemSelectedEventArgs e)
{
LoadFragment(e.Item.ItemId);
}
void LoadFragment(int id)
{
Android.Support.V4.App.Fragment fragment = null;
switch (id)
{
case Resource.Id.navigation_1:
fragment = Fragment1.NewInstance();
break;
case Resource.Id.navigation_2:
fragment = Fragment2.NewInstance();
break;
case Resource.Id.navigation_3:
fragment = Fragment3.NewInstance();
break;
}
if (fragment == null)
return;
SupportFragmentManager.BeginTransaction()
.Replace(Resource.Id.content_frame, fragment)
.Commit();
}
In my Fragment1 (which is Resource.Id.navigation_1) I am refreshing data
public async override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
RefreshItems(false);
}
The issue is, every time I click on navigation_1 for fragment1, the data gets refreshed.
I don't want to load the fragment/data again once it's already loaded.
Is it possible in Xamarin?
You can check if current fragment also executed Here is a function
public Fragment find(String identifier) {
Fragment fragment = (Fragment) getSupportFragmentManager().findFragmentByTag(identifier);
return fragment;
}
and in BottomNavigationView you must check like this
if (find(fragment.getIdentifier()) != null) {
return;
}
I'd this problem sometime ago, however I was developing on native kotlin code, here goes the code I've used to handle this:
class Navigation(val supportFragmentManager: FragmentManager,
val filterIcon: ImageView ): BottomNavigationView.OnNavigationItemSelectedListener {
override fun onNavigationItemSelected(item: MenuItem): Boolean {
when(item.itemId){
R.id.menu_bottom_home ->{
changeFragment(HomeFragment(), Constants.FRAG_HOME_FRAGMENT)
filterIcon.visibility = View.VISIBLE
}
R.id.menu_bottom_favorites ->{
changeFragment(FavoritesFragment(), Constants.FRAG_FAVORITES_FRAGMENT)
filterIcon.visibility = View.INVISIBLE
}
R.id.menu_bottom_reservations ->{
changeFragment(ReservationFragment(), Constants.FRAG_RESERVATION_FRAGMENT)
filterIcon.visibility = View.INVISIBLE
}
R.id.menu_bottom_profile ->{
changeFragment(ProfileFragment(), Constants.FRAG_PROFILE_FRAGMENT)
filterIcon.visibility = View.INVISIBLE
}
}
return true
}
fun changeFragment(fragment: Fragment, tag : String){
var contains: Fragment? = null
var current: Fragment? = null
if(supportFragmentManager.fragments != null && supportFragmentManager.fragments.isNotEmpty()) {
contains = supportFragmentManager.fragments.firstOrNull { x -> x.tag == tag }
current = supportFragmentManager.fragments.first { x -> x.isVisible }
}
if(current != null){
supportFragmentManager.beginTransaction()
.hide(current).commit()
}
if(contains != null){
supportFragmentManager.beginTransaction()
.show(contains).commit()
}
else{
supportFragmentManager.beginTransaction()
.add(R.id.fl_fragment_container, fragment, tag)
.addToBackStack(tag)
.commit()
}
}
}
Then you just have to set setOnNavigationItemSelectedListener with your Navigation class like this:
bnv_navigation.setOnNavigationItemSelectedListener(Navigation(supportFragmentManager, iv_filter_bar))

Previous fragment visible behind current one after screen rotation

In my main activity I am displaying several fragments depending on option chosen by the user. When screen is rotated and then fragment changed, the previous fragment, one that was visible while rotating screen, is transparent and still visible behind or over new one. I am adding fragments using add() method and then removing old ones with remove(). I am aware that simply using replace() would fix my problem but my main fragment contains many network and database calls on create and I would like to remember its state rather than make many unnecessary calls.
Why does my problem even occur when I am removing fragments other than the main one? How can I fix it?
Here is how I am setting my fragments:
public void setFragment(FragmentEnum fragmentEnum, #Nullable Bundle bundle) {
Fragment oldFragment = getCurrentFragment();
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction ft = fragmentManager.beginTransaction();
currentFragment = fragmentEnum;
mMapFragment = (MainMapFragment) fragmentManager.findFragmentByTag(MainMapFragment.TAG);
if (mMapFragment == null) {
mMapFragment = MainMapFragment.newInstance(bundle);
}
if (fragmentEnum == FragmentEnum.MAP) {
if (!mMapFragment.isAdded()) {
ft.add(R.id.container_contents, mMapFragment, MainMapFragment.TAG);
}
if (oldFragment != null && !(oldFragment instanceof MainMapFragment)) {
ft.remove(oldFragment);
}
ft.show(mMapFragment);
if (mNavigationDrawerFragment != null) mNavigationDrawerFragment.unselectItems();
} else {
ft.hide(mMapFragment);
Fragment newFragment = null;
String tag = null;
switch (fragmentEnum) {
case LIST:
newFragment = MainListFragment.newInstance();
tag = MainListFragment.TAG;
break;
case FAVORITE:
newFragment = MainFavoriteFragment.newInstance();
tag = MainFavoriteFragment.TAG;
break;
case ADD:
newFragment = MainAddFragment.newInstance();
tag = MainAddFragment.TAG;
break;
case USER:
newFragment = MainUserFragment.newInstance();
tag = MainUserFragment.TAG;
break;
}
ft.add(R.id.container_contents, newFragment, tag);
if (oldFragment != null && !(oldFragment instanceof MainMapFragment)) {
ft.remove(oldFragment);
}
ft.show(newFragment);
}
ft.commit();
}
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putSerializable(SAVE_STATE_CURR_FRG, currentFragment);
}
onCreate(){
...
if (savedInstanceState != null) {
FragmentEnum savedFragmentEnum = (FragmentEnum) savedInstanceState.getSerializable(SAVE_STATE_CURR_FRG);
setFragment(savedFragmentEnum, null);
}
...
}

On click Blank Area Next Fragment Automaticall call

I have 3 fragments open on condition of First fragments list item Click >> Second fragment open and so on Second. But when I click on first fragment's blank space it automatically open second fragment and click on second fragment's blank space it goes to third fragment
This is class I have maintain for fragment transaction
public static void addABCDHomeFragment(FragmentManager fragManager, String categoryName) {
FragmentTransaction ft = fragManager.beginTransaction();
Fragment abcdHomeFragment = fragManager.findFragmentByTag(ABCD_HOME_FRAGMENT);
if (abcdHomeFragment == null || abcdHomeFragment.isRemoving()) {
ABCDHomeFragment homeFragment = new ABCDHomeFragment(categoryName);
ft.add(R.id.frame_layout, homeFragment, ABCD_HOME_FRAGMENT);
ft.addToBackStack(ABCD_HOME_FRAGMENT);
ft.commit();
} else if (abcdHomeFragment != null && abcdHomeFragment.isAdded()) {
fragManager.popBackStack();
ABCDHomeFragment homeFragment = new ABCDHomeFragment(categoryName);
ft.replace(R.id.frame_layout, homeFragment, ABCD_HOME_FRAGMENT);
ft.addToBackStack(ABCD_HOME_FRAGMENT);
ft.commit();
}
}
public static void addProductListFragment(FragmentManager fragmentManager,String categoryId ,
String parent , int option) {
FragmentTransaction ftproductlist = fragmentManager.beginTransaction();
Fragment plistFragment = fragmentManager.findFragmentByTag(PRODUCT_LIST_FRAGMENT);
ProductListFragment productListFragment = new ProductListFragment(categoryId,parent,option);
if (plistFragment != null && plistFragment.isAdded() && plistFragment.isVisible()) {
fragmentManager.popBackStack();
ftproductlist.add(R.id.frame_layout, productListFragment,PRODUCT_LIST_FRAGMENT);
ftproductlist.addToBackStack(PRODUCT_LIST_FRAGMENT);
ftproductlist.commit();
} else {
ftproductlist.replace(R.id.frame_layout, productListFragment, PRODUCT_LIST_FRAGMENT);
ftproductlist.addToBackStack(PRODUCT_LIST_FRAGMENT);
ftproductlist.commit();
}
}
public static void removeProductListFragment(FragmentManager fragmentManager) {
FragmentTransaction ftproductlist = fragmentManager.beginTransaction();
Fragment productListFragment = fragmentManager.findFragmentByTag(PRODUCT_LIST_FRAGMENT);
if (productListFragment != null && productListFragment.isAdded()) {
ftproductlist.remove(productListFragment);
fragmentManager.popBackStack();
ftproductlist.commit();
}
}
public static void addProductDetailFragment(FragmentManager fragManager,String productId,String parent, String productName) {
FragmentTransaction ftproduct = fragManager.beginTransaction();
Fragment productDetailFragment = fragManager.findFragmentByTag(PRODUCT_DETAIL_FRAGMENT);
if (productDetailFragment == null || productDetailFragment.isRemoving()) {
ProductDetailFragment dtailFragment = new ProductDetailFragment(productId, parent,productName);
ftproduct.add(R.id.frame_layout, dtailFragment, PRODUCT_DETAIL_FRAGMENT);
ftproduct.addToBackStack(PRODUCT_DETAIL_FRAGMENT);
ftproduct.commit();
} else if (productDetailFragment != null && productDetailFragment.isAdded()) {
fragManager.popBackStack();
ProductDetailFragment dtailFragment = new ProductDetailFragment(productId,parent,productName);
ftproduct.replace(R.id.frame_layout, dtailFragment,PRODUCT_DETAIL_FRAGMENT);
ftproduct.addToBackStack(PRODUCT_DETAIL_FRAGMENT);
ftproduct.commit();
}
}

Fragments keep duplicating

Hee Guys,
I'm currently working with fragments and I'm trying to manage it so that when you click twice on the same menu item it won't put 2 of the same fragments on top of eachother. However it still does. Could anyone tell me what I'm doing wrong?
/*
* Method to check which action is behind the selected Menu item. Then call ShowFragment()
* With the correct fragment parameter used with this Menu action value.
*/
public void getAction(int position, Cursor cursor) {
// TODO Auto-generated method stub
mCursor = cursor;
cursor.moveToPosition(position);
String action = cursor.getString(cursor.getColumnIndex(AppMenu.ACTION));
if (action != null) {
if (action.equalsIgnoreCase("home")) {
trans = manager.beginTransaction();
BaseFragment newFragment = new HomeFragment();
if (manager.findFragmentByTag(newFragment.getTag()) != null) {
mCursor.moveToPosition(position);
// Set the current fragment
mCurrentFragment = newFragment;
Bundle bundle = new Bundle();
int fragId = mCursor.getInt(mCursor.getColumnIndex(AppMenu._ID));
Log.i(TAG, fragId + " ");
bundle.putInt("fragmentID", fragId);
newFragment.setArguments(bundle);
trans.replace(R.id.fragmentContainer, newFragment).addToBackStack(
newFragment.tag());
trans.commit();
} else {
trans.show(newFragment);
}
} else if (action.equalsIgnoreCase("event")) {
showFragment(new EventsFragment(), position);
} else if (action.equalsIgnoreCase("location")) {
showFragment(new LocationsFragment(), position);
} else if (action.equalsIgnoreCase("news")) {
showFragment(new NewsFragment(), position);
} else if (action.equalsIgnoreCase("bars")) {
showFragment(new BarsFragment(), position);
} else if (action.equalsIgnoreCase("currency")) {
showFragment(new CurrencyFragment(), position);
} else if (action.equalsIgnoreCase("map")) {
showFragment(new MapFragment(), position);
}
} else {
Log.i(TAG, "You've got a nullpointerexception on getAction().");
}
}
/*
* Method that's called when changing from fragment through Menu or HomeMenu.
*/
public void showFragment(BaseFragment newFragment, int position) {
trans = manager.beginTransaction();
if (manager.findFragmentByTag(newFragment.tag()) == null) {
mCursor.moveToPosition(position);
// Set the current fragment
mCurrentFragment = newFragment;
// Go on and set the bundle values and pass it on the fragment.
Bundle bundle = new Bundle();
int fragId = mCursor.getInt(mCursor.getColumnIndex(AppMenu._ID));
Log.i(TAG, fragId + " ");
bundle.putInt("fragmentID", fragId);
newFragment.setArguments(bundle);
trans.replace(R.id.fragmentContainer, newFragment).addToBackStack(
newFragment.tag());
trans.commit();
} else {
trans.show(newFragment);
}
}
And here are 2 callbacks for when something changes or when back pressed.
/*
* Interface method called whenever a new fragment is created.
*/
#Override
public void onNewFragment(BaseFragment newFragment) {
// TODO Auto-generated method stub
FragmentManager fm = getSupportFragmentManager();
Class<? extends Fragment> newFragmentClass = newFragment.getClass();
if (newFragmentClass == EventsFragment.class
|| newFragmentClass == LocationsFragment.class
|| newFragmentClass == MapFragment.class
|| newFragmentClass == NewsFragment.class
|| newFragmentClass == CurrencyFragment.class
|| newFragmentClass == BarsFragment.class) {
for (Fragment fragment : fm.getFragments()) {
if (fragment != null && ((Object) fragment).getClass() == newFragmentClass) {
while (((Object) mCurrentFragment).getClass() != newFragmentClass) {
popFragment();
}
popFragment();
break;
}
}
}
}
/*
* Interface method called when you navigate back from a fragment.
* Checks which fragment is active, calls upon this fragments back function to clear any data,
* then pops the first fragment on the backstack.
*/
#Override
public void onBackNavigated() {
// TODO Auto-generated method stub
if ((mCurrentFragment instanceof HomeFragment)
&& !((HomeFragment) mCurrentFragment)
.isStackEmpty()) {
System.exit(0);
// break;
}
if ((mCurrentFragment instanceof LocationsFragment)
&& !((LocationsFragment) mCurrentFragment)
.isStackEmpty()) {
((LocationsFragment) mCurrentFragment).goBack();
// return;
}
if ((mCurrentFragment instanceof EventsFragment)
&& !((EventsFragment) mCurrentFragment)
.isStackEmpty()) {
((EventsFragment) mCurrentFragment).goBack();
// return;
}
if ((mCurrentFragment instanceof MapFragment)
&& !((MapFragment) mCurrentFragment).isStackEmpty()) {
((MapFragment) mCurrentFragment).goBack();
// break;
}
if ((mCurrentFragment instanceof BarsFragment)
&& !((BarsFragment) mCurrentFragment).isStackEmpty()) {
((BarsFragment) mCurrentFragment).goBack();
// break;
}
if ((mCurrentFragment instanceof CurrencyFragment)
&& !((CurrencyFragment) mCurrentFragment).isStackEmpty()) {
((CurrencyFragment) mCurrentFragment).goBack();
// break;
}
popFragment();
}
/*
* Pops the first fragment in the backstack. Then sets this as the new current fragment.
* If no fragment is in the backstack then finish() is called. Which will destroy the only fragment.
* Which in this case exits the application.
*/
public void popFragment() {
if (manager.getBackStackEntryCount() > 1) {
manager.popBackStack();
manager.executePendingTransactions();
ArrayList<Fragment> reversedFragments = new ArrayList<Fragment>(
manager.getFragments());
Collections.reverse(reversedFragments);
for (Fragment fragment : reversedFragments)
if (fragment != null) {
mCurrentFragment = (BaseFragment) fragment;
break;
}
} else {
finish();
}
}
**NOTE : ** The tag() functions calls a final String from the fragment itself with the same hardcoded tag everytime. So every fragment of the same class has the same tag. (Which should prevent double fragments, but it still doesn't)
Solution:
The tag() was returning null al the time. (Don't know why) So I changed the showFragment(fragment, tag, position) and hardcoded the tag in the mainactivity. Then used :
trans.replace(R.id.fragmentContainer, newFragment, tag);
//instead of
trans.replace(R.id.fragmentContainer, newFragment).addToStackBack(tag);
Don't forget to still add it to the backstack! Or else your back navigation won't work.
Just add an extra line: trans.addToBackStack(tag);
You can set the tag while adding/replacing the Fragment,
So you need to mention it as :
trans.replace(R.id.fragmentContainer, newFragment,tag);
Pass the tag value to the method according to the Fragment
showFragment(new EventsFragment(),tag, position);
Hope it will help you ツ

Categories

Resources