How to pass data between tabs on Xamarin Android? - c#

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

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

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.

Send data from one fragment to another fragment in xamarin.android

I am trying to do an application with Xamarin.android. It is "ToDoList" and I have two tabs, one of them is to create and edit the chores(it is called "undone"). The other one is for the chores that are marked as done(it is called done). I have used Fragments for the tabs and what I am trying to accomplish is when I mark one task as done I want to delete it from the list in the "undone" tab and add one to the list in "done" tab.
MainActivity.cs
public class MainActivity : Activity
{
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
// Set our view from the "main" layout resource
SetContentView(Resource.Layout.activity_main);
this.ActionBar.NavigationMode = ActionBarNavigationMode.Tabs;
var tab1 = ActionBar.NewTab();
tab1.SetText("Undone");
var tabFirst = new UndoneListFragment();
tab1.TabSelected += (sender, e) =>
{
var fragment = this.FragmentManager.FindFragmentById(Resource.Id.tabsContainer);
if (fragment != null)
e.FragmentTransaction.Remove(fragment);
e.FragmentTransaction.Add(Resource.Id.tabsContainer, tabFirst);
};
tab1.TabUnselected += (sender, e) =>
{
e.FragmentTransaction.Remove(tabFirst);
};
var tab2 = ActionBar.NewTab();
tab2.SetText("Done");
var tabSecond = new DoneListFragment();
tab2.TabSelected += (sender, e) =>
{
var fragment = this.FragmentManager.FindFragmentById(Resource.Id.tabsContainer);
if (fragment != null)
e.FragmentTransaction.Remove(fragment);
e.FragmentTransaction.Add(Resource.Id.tabsContainer, tabSecond);
};
tab2.TabUnselected += (sender, e) =>
{
e.FragmentTransaction.Remove(tabSecond);
};
ActionBar.AddTab(tab1);
ActionBar.AddTab(tab2);
}
}
UndoneListFragment.cs
public class UndoneListFragment : Fragment
{
List<Task> tasks = new List<Task>();
ListView lView;
TasksViewAdapter adapter;
View view;
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);
base.OnCreateView(inflater, container, savedInstanceState);
view = inflater.Inflate(Resource.Layout.undoneListView, container, false);
Button addBtn = view.FindViewById<Button>(Resource.Id.AddBtn);
lView = view.FindViewById<ListView>(Resource.Id.TasksViewList);
adapter = new TasksViewAdapter(view.Context, tasks);
lView.Adapter = adapter;
lView.ItemClick += Edit;
addBtn.Click += AddTask;
return view;
}
}
public override void OnActivityResult(int requestCode, Result resultCode, Intent data)
{
base.OnActivityResult(requestCode, resultCode, data);
switch (requestCode)
{
case 10:
if (resultCode == Result.Ok) //Edit an already existing task
{
var replacedTaskName = data.GetStringExtra("new_name" ?? "Name not found");
var position = data.GetIntExtra("listPos", 0);
bool done = data.GetBooleanExtra("done", false);
Task t = new Task(replacedTaskName);
if (done == true)
{
t.TaskStatus = Status.Done;
//here I want to implement the code to somehow
//send the replacesTaskName to the second fragment
//Your suggestion is here
DoneListFragment frag = new DoneListFragment();
Bundle b = new Bundle();
b.PutString("MyKey",replacedTaskName);
frag.Arguments = b;
}
tasks[position] = t;
adapter.NotifyDataSetChanged();
string newName = tasks[position].TaskName;
break;
}
}
}
DoneListFragment.cs
public class DoneListFragment : Fragment
{
List<Task> doneTasks = new List<Task>();
ListView lView;
TasksViewAdapter adapter;
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
base.OnCreateView(inflater, container, savedInstanceState);
var view = inflater.Inflate(Resource.Layout.doneListView, container, false);
Button deleteBtn = view.FindViewById<Button>(Resource.Id.DeleteAllBtn);
lView = view.FindViewById<ListView>(Resource.Id.doneList);
adapter = new TasksViewAdapter(view.Context, doneTasks);
lView.Adapter = adapter;
if(Arguments != null)
{
string value= Arguments.GetString("MyKey");
}
//click to delete done tasks
deleteBtn.Click += DeleteAll;
return view;
}
}
For Sending data from one fragment another one simple step is creating constructor while replacing
the fragment or calling fragment.
For Example:
Suppose I have two fragments ,
ActionFragment
ActionDetailsFragment
Now I want to send data from “ActionFragment” to “ActionDetailsFragment”:
In ActionFragment :
fragmentManager = getChildFragmentManager();
fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.frameLayoutActionDetails, new ActionDetailsFragment(Data x, Data y)).commitAllowingStateLoss( );
Now below code will be in “ActionDetailsFragment”
public class ActionDetailsFragment extends AppFragment {
Data x;
Data y;
public ActionDetailsFragment(Data x, Data y) {
super();
this.x = x;
this.y = y;
}
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
—————————————
////———Here is your code———————
—————————————————
}
Now you can use “Data x, Data y” in ActionDetailsFragment…
Thats it…
Note: Data x and Data y both are imaginary variables..
Activity’s cannot use in TabLayout or ViewPager you must have to use Fragments .
You can use “Bundle” instead,
Fragment fragment = new Fragment();
Bundle bundle = new Bundle();
bundle.putInt(key, value);
fragment.setArguments(bundle);
Then in your Fragment, retrieve the data (e.g. in onCreate() method) with:
Bundle bundle = this.getArguments();
if (bundle != null) {
int myInt = bundle.getInt(key, defaultValue);
}
Well if you are looking for Fragment to Fragment data transfer the smart way of doing it would be using fragment arguments, Something like this:
When you initialize the fragment object in your case:
var frag= new DoneListFragment();
Bundle bundle= new Bundle();
bundle.PutString("YourKey", "YourValue");
frag.Arguments=bundle;
Then to retrieve this data you do something like this in DoneListFragment in the OnCreateView method:
if(Arguments!=null)
{
String value = Arguments.GetString("YourKey");
}

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);
}

Loading Resource View in Fragment with ActionBar tabs

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);

Categories

Resources