There is a class inheritor from the handler through which I control the graphic button.
To search for this button, I pass in the designer a link to the activation. But I want to pass the view there. How can I get a view from activity?
public class FlActivity : AppCompatActivity
{
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
SetContentView(Resource.Layout.activity_fl);
toolbar = FindViewById<Android.Support.V7.Widget.Toolbar>(Resource.Id.toolbar);
SetSupportActionBar(toolbar);
SupportActionBar.SetDisplayHomeAsUpEnabled(true);
tabLayout = FindViewById<TabLayout>(Resource.Id.tabLayout);
viewPager = FindViewById<ViewPager>(Resource.Id.viewPager);
fpAdapter = new FpAdapter(SupportFragmentManager, null);
uiHandler = new UiHandler(this, MainLooper);
}
}
public class UiHandler : Handler
{
private Activity activity { get; }
public UiHandler(Activity a, Looper loader) : base(loader)
{
activity = a;
}
public override void HandleMessage(Message msg)
{
activity.FindViewById<ImageButton>(Resource.Id.imageButton1).SetImageResource(msg.Data.GetInt("ID", Resource.Drawable.bt_off));
}
}
If I change private Activity activity { get; } to private View view { get; }, how can I transfer the view from the main activity when creating a handler instance. What to replace this in creating a nadler object?
There are quite several ways to do this :
this.Window.DecorView.RootView;
or
this.Window.DecorView.FindViewById(Android.Resource.Id.Content);
or
this.FindViewById(Android.Resource.Id.Content);
or
this.FindViewById(Android.Resource.Id.Content).RootView;
or
((ViewGroup) this.FindViewById(Android.Resource.Id.Content)).GetChildAt(0);
Although, since it's probably a layout, not a single view, I recommend using ViewGroup instead of View (these methods returns View so you have to cast them to ViewGroup if you want)
=================
Credit to this answer
Related
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.
I'm working with RecyclerView (Xamarin.Android) on Visual Studio 2017, this is my first Xamarin project since I'm a native Android developer, and I'm kinda confused respect to implementing the On Items Clicks events on the Recycler View. I just created the RecyclerView.Adapter using the default template that the IDE provides (RecyclerAdapter class), it looks a lot like the native implementation:
Here my Code:
using System;
using Android.Views;
using Android.Widget;
using Android.Support.V7.Widget;
namespace Airlink
{
class PdfAdapter : RecyclerView.Adapter
{
public event EventHandler<PdfAdapterClickEventArgs> ItemClick;
public event EventHandler<PdfAdapterClickEventArgs> ItemLongClick;
Pdf[] items;
public PdfAdapter(Pdf[] data)
{
items = data;
}
// Create new views (invoked by the layout manager)
public override RecyclerView.ViewHolder OnCreateViewHolder(ViewGroup
parent, int viewType)
{
//Setup your layout here
View itemView = null;
//var id = Resource.Layout.__YOUR_ITEM_HERE;
itemView=LayoutInflater.From(parent.Context).
Inflate(Resource.Layout.pdf_item, parent, false);
var vh = new PdfAdapterViewHolder(itemView, OnClick,
OnLongClick);
return vh;
}
// Replace the contents of a view (invoked by the layout manager)
public override void OnBindViewHolder(RecyclerView.ViewHolder
viewHolder, int position)
{
var item = items[position];
// Replace the contents of the view with that element
PdfAdapterViewHolder holder = viewHolder as
PdfAdapterViewHolder;
holder.pdf_name.Text = items[position].Name;
}
public override int ItemCount => items.Length;
void OnClick(PdfAdapterClickEventArgs args) =>
ItemClick?.Invoke(this, args);
void OnLongClick(PdfAdapterClickEventArgs args) =>
ItemLongClick?.Invoke(this, args);
}
public class PdfAdapterViewHolder : RecyclerView.ViewHolder
{
public TextView pdf_name { get; set; }
public PdfAdapterViewHolder(View itemView,
Action<PdfAdapterClickEventArgs> clickListener,
Action<PdfAdapterClickEventArgs> longClickListener) :
base(itemView)
{
pdf_name = itemView.FindViewById<TextView>
(Resource.Id.pdf_name);
itemView.Click += (sender, e) => clickListener(new
PdfAdapterClickEventArgs { View = itemView, Position =
AdapterPosition });
itemView.LongClick += (sender, e) => longClickListener(new
PdfAdapterClickEventArgs { View = itemView, Position =
AdapterPosition });
}
}
public class PdfAdapterClickEventArgs : EventArgs
{
public View View { get; set; }
public int Position { get; set; }
}
}
So, given my code, I just want to know where I can handle the clicked View, I'm not sure if I have to implement some code on the PdfAdapterClickEventArgs Class or on the OnClick and OnLongClick voids that receive the PdfAdapterClickEventArgs object, let's say I want to show a Toast showing the name of the TextView inside the View clicked. I use to handle this action on native Android using:
view.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//my code here
}
}
inside the ViewHolder constructor method.
I mean, I can change the default C# implementation in order to get something similar to the snippet above, but I would like to keep the original C# code.
You will implement your OnClick event handler in the Activity/Fragment where instance of your Adapter is created.
This line means you are making the ItemClick event public in your adapter class.
public event EventHandler<PdfAdapterClickEventArgs> ItemClick;
So you are now able to do this:
var adapter = new PdfAdapter(data);
adapter.ItemClick += OnItemClick;
....
....
myRecyclerView. SetAdapter(adapter);
And you will have a method:
public void OnItemClick(object sender, PdfAdapterClickEventArgs e)
{
var view = args.View; //this is your view
Toast.MakeText(this, $"Item Position: {args?.Position}", ToastLength.Short).Show();
}
Hope this helps.
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
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);
}
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);