Loading Resource View in Fragment with ActionBar tabs - c#

I don't know why it is not working but what am I doing wrong. It works fine if I explicitly set the TextView and not using Inflate method by the LayoutInflater. It just closes my application when I run it and when I switch to Test tab it'll just crash with no errors in the output window.
public class MyProfileActivity : Activity
{
protected override void OnCreate (Bundle bundle)
{
base.OnCreate (bundle);
ActionBar actionBar = ActionBar;
actionBar.NavigationMode = ActionBarNavigationMode.Tabs;
actionBar.SetDisplayShowTitleEnabled (false);
ActionBar.Tab tab = actionBar.NewTab ();
tab.SetText ("Home");
// tab.SetIcon (Resource.Drawable.tab_home);
tab.SetTabListener(new TabListener<HomeFragment>(this, "home"));
actionBar.AddTab (tab);
tab = actionBar.NewTab ();
tab.SetText("Terms");
// tab.SetIcon(Resource.Drawable.tab_terms);
tab.SetTabListener (new TabListener<TermsFragment> (this, "terms"));
actionBar.AddTab (tab);
tab = actionBar.NewTab();
tab.SetText("test");
// tab.SetIcon(Resource.Drawable.tab_terms);
tab.SetTabListener(new TabListener<TestFragment>(this, "test"));
actionBar.AddTab(tab);
}
}
Fragments
public class TestFragment : Fragment
{
private View _fragmentView;
public override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
// Create your fragment here
}
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
_fragmentView = inflater.Inflate(Resource.Layout.Tabs, container);
return _fragmentView;
}
}
#region HomeFragment
public class HomeFragment : Fragment
{
public HomeFragment() : base() {}
public override View OnCreateView (LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
TextView tv = new TextView (Activity);
tv.Text = "Home";
return tv;
}
}
#endregion
#region TermsFragment
public class TermsFragment : Fragment
{
public TermsFragment() : base() {}
public override View OnCreateView (LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
TextView tv = new TextView (Activity);
tv.Text = "Terms";
return tv;
}
}
#endregion
TabListener
public class TabListener<T> : Java.Lang.Object, ActionBar.ITabListener where T : Fragment
{
#region Members
private Fragment m_fragment;
private readonly Activity m_activity;
private readonly string m_tag;
private readonly T m_fragmentType;
#endregion
#region Constructor
public TabListener(Activity activity, String tag)
{
// The Activity is our host Activity and provides the Context.
// The type T and the tag will be used to instantiate the fragment.
// m_fragment will hold the instance for reuse after it is first created.
m_activity = activity;
m_tag = tag;
}
#endregion
#region ITabListener implementation
public void OnTabSelected(ActionBar.Tab tab, FragmentTransaction ft)
{
// Instantiate the reference and add it to the container,
// or attach it if it has already been created
if (m_fragment == null)
{
m_fragment = Fragment.Instantiate(m_activity, Java.Lang.Class.FromType(typeof(T)).Name);
ft.Add(global::Android.Resource.Id.Content, m_fragment, m_tag);
}
else
{
ft.Show(m_fragment);
}
}
public void OnTabUnselected(ActionBar.Tab tab, FragmentTransaction ft)
{
if (m_fragment != null)
{
ft.Hide(m_fragment);
}
}
public void OnTabReselected(ActionBar.Tab tab, FragmentTransaction ft)
{
// User selected the already selected tab. Usually do nothing.
}
#endregion
public class TestFragment : Fragment
{
private View _fragmentView;
public override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
// Create your fragment here
}
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
_fragmentView = inflater.Inflate(Resource.Layout.Tabs, container);
return _fragmentView;
}
}
}

I tried using with your code and got java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first. when navigating to TestFragment.
This answer allowed me to fix the problem by providing false for parameter attachToRoot:
_fragmentView = inflater.Inflate(Resource.Layout.TestLayout, container, false);

Related

Move Tabs Using Fragment- Nested Tabs

I am new in android, especially in Xamarin Android C#. I have bottom navigation tabs and fragments. They are fragment_picture (tab_picture), fragment home (tab_home), fragment notifications (tab_notifications), fragment_profile (tab_profile). Inside fragment_picture (tab_picture), I have 2 tabs. They are fragment_camera(tab_camera), and fragment_gallery(tab_gallery). I want to move the tabs of fragment_picture such as when I click the tab_camera, it shows the XML of the tab_camera and when I click the tab_gallery, It shows the XML of the tab_gallery.
I don't use viewpager. I am just using fragments. The tab won't change the position to its XML. I've searched a lot in Google But, I still confused. Please help me
using System;
using Android.OS;
using Android.Support.Design.Widget;
using Android.Views;
using Android.Support.V4.App;
using Android.Widget;
namespace MyPhotographicMemory.Droid.Fragments
{
public class Fragment_Picture : Fragment
{
TabLayout tabLayout;
private TabLayout.Tab tabGallery, tabCamera;
Fragment fragment = null;
public override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
}
public static Fragment_Picture NewInstance()
{
var frag2 = new Fragment_Picture { Arguments = new Bundle() };
return frag2;
}
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
var ignored = base.OnCreateView(inflater, container, savedInstanceState);
var v = inflater.Inflate(Resource.Layout.FragmentPicture, null);
tabLayout = v.FindViewById<TabLayout>(Resource.Id.pictureTabLayout);
tabLayout.TabSelected += (object sender, TabLayout.TabSelectedEventArgs e) =>
{
switch (e.Tab.Position)
{
case Resource.Layout.FragmentCamera: //Resource.Id.tabItem1
fragment = new Fragment_Camera();
TabLayout.Tab tab = tabLayout.GetTabAt(0);
tab.Select();
break;
case Resource.Layout.FragmentGallery: //Resource.Id.tabItem1
fragment = new Fragment_Gallery();
TabLayout.Tab tab1 = tabLayout.GetTabAt(1);
tab1.Select();
break;
}
};
return v;
}
}
}
Do you want to achieve the result like this GIF?
If so, you should achieve the nested tabs with fragment.
Here is my code when in my first fragment.
public class Fragment1 : Fragment
{
public override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
// Create your fragment here
}
public static Fragment1 NewInstance()
{
var frag1 = new Fragment1 { Arguments = new Bundle() };
return frag1;
}
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
var ignored = base.OnCreateView(inflater, container, savedInstanceState);
var view = inflater.Inflate(Resource.Layout.fragment1, null);
BottomNavigationView bottomNavigationView= view.FindViewById<BottomNavigationView>(Resource.Id.mynested_bottom_navi);
bottomNavigationView.NavigationItemSelected += BottomNavigationView_NavigationItemSelected;
LoadFragment(Resource.Id.tab_camera);
return view;
}
private void BottomNavigationView_NavigationItemSelected(object sender, BottomNavigationView.NavigationItemSelectedEventArgs e)
{
LoadFragment(e.Item.ItemId);
}
//switch fragment by tab id.
void LoadFragment(int id)
{
Fragment fragment = null;
switch (id)
{
case Resource.Id.tab_gallery:
fragment = Fragment_Gallery.NewInstance();
break;
case Resource.Id.tab_camera:
fragment = Fragment_Camera.NewInstance();
break;
}
if (fragment == null)
return;
FragmentManager.BeginTransaction()
.Replace(Resource.Id.mynest_content_frame, fragment)
.Commit();
}
}
Here is my demo, you can refer to it.
https://drive.google.com/file/d/1XZE6YopdsvDHY8-DwXBxRpHz3oFlJbOR/view

Fragment transaction with SetCustomAnimations crashes on back pressed: The specified child already has a parent

I am making an android app with Xamarin. In one of my activities I have a frame layout that I put fragments in. In the transaction I use SetCustomAnimations(), Replace(), and AddToBackStack(). When I press the back button or call OnBackPressed(), the app crashes with the exception:
Java.Lang.IllegalStateException: The specified child already has a parent.
You must call removeView() on the child's parent first.
If I remove the call to SetCustomAnimations() it works, but I don't get any animations.
The activity:
[Android.App.Activity(Label = "Login")]
public class LoginActivity : AppCompatActivity
{
private FrameLayout view;
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
view = new FrameLayout(this)
{
Id = View.GenerateViewId()
};
SetContentView(view);
SupportFragmentManager.BeginTransaction().Add(view.Id, SelectDomainFragment.Create()).Commit();
}
public void ShowFragment(Fragment fragment)
{
SupportFragmentManager.BeginTransaction()
.SetCustomAnimations(Resource.Animation.enter_right, Resource.Animation.exit_left, Resource.Animation.enter_left, Resource.Animation.exit_right)
.Replace(view.Id, fragment)
.AddToBackStack(null)
.Commit();
}
}
SelectDomainFragment:
public class SelectDomainFragment : Fragment
{
private SelectDomainView view;
public static SelectDomainFragment Create() => new SelectDomainFragment();
public override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
view = new SelectDomainView(Context);
view.CloudButtonClicked += (sender, e) => ToLogin(1);
view.OnPremButtonClicked += (sender, e) => ToLogin(2);
}
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) => view;
private void ToLogin(int target) => ((LoginActivity)Activity).ShowFragment(LoginFragment.Create(target));
}
LoginFragment:
public class LoginFragment : Fragment
{
private LoginView view;
public static LoginFragment Create(int target)
{
var args = new Bundle();
args.PutInt("target", target);
return new LoginFragment
{
Arguments = args
};
}
public override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
view = new LoginView(Context);
}
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) => view;
}

MVVMCross After navigating to fragment the layout is not refreshed

I have a tabbed application that uses MvxTabsFragmentActivity.
When I navigate between tabs, there is the correct behaviour but the first time when I try to navigate inside a fragment, the called fragment is not replaced.
I tried to use both IMvxNavigationService and also the classic method, using ShowViewModel but they both act the same.
I also used a custom presenter for showing the fragments, but with or without it, the problem stills the same.
There is the image with the printscreen
https://imgur.com/a/cfRWlsl
The red highlighted is the fragment where I am navigating to and the black one is the previous fragment.
This is the View that has the button needed for navigation:
public class TrackView : MvxFragment<TrackViewModel>
{
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
base.OnCreateView(inflater, container, savedInstanceState);
var view = this.BindingInflate(Resource.Layout.track_layout, null);
var nextButton = view.FindViewById<Button>(Resource.Id.navigate);
var set = this.CreateBindingSet<TrackView, TrackViewModel>();
set.Bind(nextButton).To(vm => vm.NavigateEventCommand);
set.Apply();
return view;
}
}
This is its ViewModel:
public class TrackViewModel : MvxViewModel
{
private readonly IMvxNavigationService _navigationService;
public TrackViewModel(IMvxNavigationService navigationService)
{
this._navigationService = navigationService;
}
public IMvxCommand NavigateEventCommand => new MvxAsyncCommand(NavigateCommand);
private async Task NavigateCommand()
{
await _navigationService.Navigate<RegistrationViewModel>();
}
}
This is the View where I am navigating to:
public class RegistrationView : MvxFragment
{
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
base.OnCreateView(inflater, container, savedInstanceState);
var view = this.BindingInflate(Resource.Layout.registration_layout, null);
//var set = this.CreateBindingSet<RegistrationView, RegistrationViewModel>();
//set.Apply();
return view;
}
}
This is its ViewModel:
public class RegistrationViewModel : MvxViewModel
{
}
I have also a tabbed Application that uses MvxTabsFragmentActivity, and I am creating the tabs like this:
protected override void AddTabs(Bundle args)
{
this.AddTab<TrackView>(
args,
Mvx.IocConstruct<TrackViewModel>(),
this.CreateTabFor("0", Resource.Drawable.ic_track, "Track"));
this.AddTab<SendView>(
args,
new SendViewModel(),
this.CreateTabFor("1", Resource.Drawable.ic_send, "Send"));
this.AddTab<ProfileView>(
args,
new ProfileViewModel(),
this.CreateTabFor("2", Resource.Drawable.ic_profile, "Profile"));
this.AddTab<MoreView>(
args,
new MoreViewModel(),
this.CreateTabFor("3", Resource.Drawable.ic_more, "More"));
}
I have a custom presenter that is registered this way in the main class:
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
this.tabHost = this.FindViewById<TabHost>(Android.Resource.Id.TabHost);
presenter = (DroidPresenter)Mvx.Resolve<IMvxAndroidViewPresenter>();
var initialFragment = new TrackView() { ViewModel = Mvx.IocConstruct<TrackViewModel>() };
presenter.RegisterFragmentManager(FragmentManager, initialFragment);
}
As I said, with or without the presenter, there is the same behaviour.
I am using MvvmCross 5.7.

How to pass data between tabs on Xamarin Android?

I have two tabs in viewpager. So in fragment1 I input a text and when in tab2 is selected and then fragment2 will get data from that text from fragment1.
ex: when I open tab1 -> I input a text is "abcd" -> I open tab2 -> I want to get a text is "abcd" from tab1(fragment1).
public class Fragment1 : Android.Support.V4.App.Fragment
{
Fragment2 frag2 = new Fragment2();
public override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
// Create your fragment here
}
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
// Use this to return your custom view for this Fragment
// return inflater.Inflate(Resource.Layout.YourFragment, container, false);
Singleton singleton = Singleton.GetInstance();;
singleton.SetSource("abcd");
}
}public class Singleton
{
private static Singleton singleton;
string a = null;
private Singleton()
{
}
public static Singleton GetInstance()
{
if (singleton == null)
singleton = new Singleton();
return singleton;
}
public void SetSource(string text)
{
this.a = text;
}
public string showMessage()
{
return a;
}
}
public class Fragment2 : Android.Support.V4.App.Fragment
{
public List<string> data = new List<string>();
public void add(string t)
{
data.Add(t);
}
TextView txt;
public override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
// Create your fragment here
}
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
// Use this to return your custom view for this Fragment
// return inflater.Inflate(Resource.Layout.YourFragment, container, false);
//return base.OnCreateView(inflater, container, savedInstanceState);
return inflater.Inflate(Resource.Layout.view2, container, false);
txt = View.FindViewById<TextView>(Resource.Id.txtgetdata);
if (data != null)
{
txt.Text = data[0];
}
else
Toast.MakeText(Application.Context, "no", ToastLength.Long).Show();
}
}
I have tried it but don't having the result.
After a few hours of research/test & debugging and solved, try this out.
In your Activity
add public string tabFragment { get; set; }
In your source fragment
ie on click or that navigate to other fragment
string tab = ((ActivityUser)Activity).tabFragmentAddUser;
FragmentAddUser fragment = (FragmentAddUser);
Activity.SupportFragmentManager.FindFragmentByTag(tab);
fragment.setValText("Hello");
viewPager.SetCurrentItem(1, true);
In your destination fragment (ie which elements will be modified)
add a method like this
public void setValText(string str) { txtUsrFirstName.Text = str; }
==================================================================
Fragment fragment = new Fragment();
Bundle bundle = new Bundle();
bundle.putInt(key, value);
fragment.SetArguments(bundle);
in on create of the other fragment
Bundle bundle = this.GetArguments();
int myInt = bundle.GetInt(key, defaultValue);
optionaly two fragments should never communicate directly and should communicate through activity
From Developers website:
Often you will want one Fragment to communicate with another, for
example to change the content based on a user event. All
Fragment-to-Fragment communication is done through the associated
Activity. Two Fragments should never communicate directly.
refer this and this

Issues taking photo from viewpager fragment

I'm developing an app using Xamarin.Android that implements a viewpager / actionbar tabs. When I take a photo using the camera, the app will crash when I tap "save." The following error shows up.
System.NotSupportedException: Unable to find the default constructor on type App3.ViewPagerFragment. Please provide the missing constructor.
This only happens when I use the camera. It will pick an image from gallery just fine. I've placed the code for the ViewPagerFragment and FragmentPagerAdapter along with the call I make to request camera functionality. If anybody knows what the heck I'm doing wrong. Please let me know. I've been bashing my head against the wall for days trying to figure this thing out. Thanks.
UPDATE: I also found the same problem to happen when my app goes landscape. I basically used this sample app and added camera functionality to it. HelloSwipeViewWithTabs
== ViewPagerFragment ==
class ViewPagerFragment : Android.Support.V4.App.Fragment {
private Func<LayoutInflater, ViewGroup, Bundle, View> _view;
public ViewPagerFragment(Func<LayoutInflater, ViewGroup, Bundle, View> view)
{
_view = view;
}
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
base.OnCreateView(inflater, container, savedInstanceState);
return _view(inflater, container, savedInstanceState);
}
== FragmentPagerAdapter ==
class FragmentPagerAdapter : FragmentPagerAdapter { private List<Android.Support.V4.App.Fragment> _fragmentList = new List<Android.Support.V4.App.Fragment>(); public FragmentPagerAdapter(Android.Support.V4.App.FragmentManager fm) : base(fm) {}
public override int Count
{
get { return _fragmentList.Count; }
}
public override Android.Support.V4.App.Fragment GetItem(int position)
{
return _fragmentList[position];
}
public void AddFragment(ViewPagerFragment fragment)
{
_fragmentList.Add(fragment);
}
public void AddFragmentView(Func<LayoutInflater, ViewGroup, Bundle, View> view)
{
_fragmentList.Add(new ViewPagerFragment(view));
}
}
public class ViewPageListenerForActionBar : ViewPager.SimpleOnPageChangeListener
{
private ActionBar _bar;
public ViewPageListenerForActionBar(ActionBar bar)
{
_bar = bar;
}
public override void OnPageSelected(int position)
{
_bar.SetSelectedNavigationItem(position);
}
}
public static class ViewPagerExtensions
{
public static ActionBar.Tab GetViewPageTab(this ViewPager viewPager, ActionBar actionBar, string name)
{
var tab = actionBar.NewTab();
tab.SetText(name);
tab.TabSelected += (o, e) =>
{
viewPager.SetCurrentItem(actionBar.SelectedNavigationIndex, false);
};
return tab;
}
}
== Camera Intent ==
private void TakeAPicture() { Intent pickIntent = new Intent();
pickIntent.SetType("image/*");
pickIntent.SetAction(Intent.ActionGetContent);
Intent takePhotoIntent = new Intent(MediaStore.ActionImageCapture);
String pickTitle = "Select or take a new Picture"; // Or get from strings.xml
Intent chooserIntent = Intent.CreateChooser(pickIntent, pickTitle);
chooserIntent.PutExtra
(
Intent.ExtraInitialIntents,
new Intent[] { takePhotoIntent }
);
StartActivityForResult(chooserIntent, 0);
}

Categories

Resources