How do I assign click events to buttons in an app with multiple pages?
I can do it fine with a single page app, as follows:
namespace Welf
{
[Activity(Label = "Welf", MainLauncher = true, Icon = "#drawable/icon")]
public class MainActivity : Activity
{
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
// Set our view from the "main" layout resource
SetContentView (Resource.Layout.Main);
// Assign click event to direct to next page (page1)
Button cmdFirst = FindViewById<Button>(Resource.Id.cmdFirst);
cmdFirst.Click += delegate
{
SetContentView(Resource.Layout.page1);
};
}
}
}
But at what point to I assign click events for buttons on other pages?
If I do the following it doesn't work, I'm guessing because the pages aren't instantiated yet so FindByView returns a null:
public class MainActivity : Activity
{
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
// Set our view from the "main" layout resource
SetContentView (Resource.Layout.Main);
// Assign click event to direct to next page (page1)
Button cmdFirst = FindViewById<Button>(Resource.Id.cmdFirst);
cmdFirst.Click += delegate
{
SetContentView(Resource.Layout.page1);
};
// This assignment doesn't work
Button cmdSecond = FindViewById<Button>(Resource.Id.cmdSecond);
cmdSecond.Click += delegate
{
SetContentView(Resource.Layout.page3);
}
}
}
...and the following does work, but then I get into trouble with repeat visits to the same page because I'm adding the event a second time:
public class MainActivity : Activity
{
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
// Set our view from the "main" layout resource
SetContentView (Resource.Layout.Main);
// Assign click event to direct to next page (page1)
Button cmdFirst = FindViewById<Button>(Resource.Id.cmdFirst);
cmdFirst.Click += delegate
{
SetContentView(Resource.Layout.page1);
// This click works the first time only, for obvious reasons
Button cmdSecond = FindViewById<Button>(Resource.Id.cmdSecond);
cmdSecond.Click += delegate
{
SetContentView(Resource.Layout.page3);
}
};
}
}
So how should I do it?
You should only call SetContentView() once in any activity. What you need to do here is make a new activity.
1: Right click on your project and click new item.
2: Create a new activity and a new Android layout.
3: Then in your new Activity.cs call SetContentView for your new layout you just created.
4: Then use this code to call your new activity: Intent intent = new Intent(this,typeof(mynewactivityname));
StartActivity(intent);
Related
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
I'm trying to link the hardware back button into my WebViews in Xamarin for Android. My WebViews are contained within OnCreate instances of TabHost (which is deprecated, but I'm using it anyway) I've got this inside my MainActivity : TabActivity class
public override void OnBackPressed()
{
base.OnBackPressed();
}
and here's an example of one of my Tab Activity Classes
[Activity]
public class SpeakersActivity : Activity
{
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
//set the content view
SetContentView(Resource.Layout.subs);
//declare webview and tell our code where to find the XAML resource
WebView subWebView = FindViewById<WebView>(Resource.Id.webViewSubs);
//set the webview client
subWebView.SetWebViewClient(new WebViewClient());
//load the subscription url
subWebView.LoadUrl("https://www.bitchute.com/subscriptions/");
//enable javascript in our webview
subWebView.Settings.JavaScriptEnabled = true;
//zoom control on? This should perhaps be disabled for consistency?
//we will leave it on for now
subWebView.Settings.BuiltInZoomControls = true;
subWebView.Settings.SetSupportZoom(true);
//scrollbarsdisabled
// subWebView.ScrollBarStyle = ScrollbarStyles.OutsideOverlay;
subWebView.ScrollbarFadingEnabled = false;
}
}
and I've seen a lot information how to use this
subWebView.GoBack();
to goback in a webview, but the problem is that my WebViews are not within the scope of my hardware back button. The hardware back button is inside mainactivity class and my webviews are inside individual instances of the tab activities.
What's the best way to correct this issue? Thank you!
Thanks so much #SushiHangover !!!
I solved it like this:
[Activity]
public class SpeakersActivity : Activity
{
public override void OnBackPressed()
{
WebView subWebView = FindViewById<WebView>(Resource.Id.webViewSubs);
subWebView.GoBack();
}
}
EDIT: the way you do this with a ViewPager and Fragment is as follows:
//put this code in your MainActivity.cs
public override bool OnKeyDown(Android.Views.Keycode keyCode, KeyEvent e)
{
if (e.KeyCode == Android.Views.Keycode.Back)
{
switch (_viewPager.CurrentItem)
{
case 0:
_fm1.WebViewGoBack();
break;
case 1:
_fm2.WebViewGoBack();
break;
case 2:
_fm3.WebViewGoBack();
break;
case 3:
_fm4.WebViewGoBack();
break;
case 4:
_fm5.WebViewGoBack();
break;
}
}
return false;
}
then in a fragment instantiate WebView assign in OnCreate and create a method for going back inside Fragment :
protected static WebView _wv;
protected static View _view;
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
_view = inflater.Inflate(Resource.Layout.TheFragmentLayout1, container, false);
_wv = _view.FindViewById<WebView>(Resource.Id.webView1);
//lots of code
}
public void WebViewGoBack()
{
if (_wv.CanGoBack())
_wv.GoBack();
}
I'm trying to create an activity with two tabs, one holding FragmentA and one holding FragmentB. Here is how I add the fragments to the Activity:
[Activity(Label = "My App")]
public class MyActivity : Activity
{
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
SetContentView(Resource.Layout.ConnectionMenu);
ActionBar.NavigationMode = ActionBarNavigationMode.Tabs;
AddTab("A", new FragmentA());
AddTab("B", new FragmentB());
}
private void AddTab(string tabText, Fragment fragment)
{
var tab = ActionBar.NewTab();
tab.SetText(tabText);
tab.TabSelected += (sender, e) =>
{
e.FragmentTransaction.Replace(
Resource.Id.fragmentContainer,
fragment);
};
ActionBar.AddTab(tab);
}
}
When I rotate the orientation I want to keep fields filled out in the fragments the same. I save my data in OnSaveInstanceState and restore the data in OnActivityCreated. However, I'm noticing that the OnCreateView and OnActivityCreated methods are being called twice per rotate. The first time containing my filled in Bundle and the second time with bundle being null.
I assume that my error is in the MyActivity class but if you need more information let me know!
Given you create the fragment in your Activity.OnCreate(), you will always have 2 calls due to creating new ones in the method, and maintaining the old ones in the base.OnCreate(). What you should probably do is instead of always creating these fragments, you can search via a tag or ID for an existing fragment and use those in the Tabs instead.
i.e.
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
SetContentView(Resource.Layout.ConnectionMenu);
ActionBar.NavigationMode = ActionBarNavigationMode.Tabs;
if(savedInstanceState == null)
{
AddTab("A", new FragmentA());
AddTab("B", new FragmentB());
}
else
{
Fragment a = (FragmentA)SupportFragmentManager.FindFragmentByTag("my_tag_a");
Fragment b = (FragmentB)SupportFragmentManager.FindFragmentByTag("my_tag_b");
AddTab("A", a);
AddTab("B", b);
}
}
I ended up solving the issue. as #JonDouglas said you need to make sure the tab wasn't already loaded before creating a new fragment. To do this the fragment can be loaded from the FragmentManager class using a tag. During the TabSelected event if the fragment was not previously create, a new fragment is created and added to the event FragmentTransaction using the tag. During the TabUnselected event, if the fragment was created then it is detached.
I also added in a Bundle value to hold onto the last active tab.
Here is the code I used to solve the issue.
[Activity(Label = "My App")]
public class MyActivity : Activity
{
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
SetContentView(Resource.Layout.ConnectionMenu);
ActionBar.NavigationMode = ActionBarNavigationMode.Tabs;
AddTab("A", "a_fragment", () => new FragmentA());
AddTab("B", "b_fragment", () => new FragmentB());
if (savedInstanceState != null)
{
var selectedTab = savedInstanceState.GetInt(
"ActionBar.SelectedNavigationIndex", 0);
ActionBar.SetSelectedNavigationItem(selectedTab);
}
}
protected override void OnSaveInstanceState(Bundle savedInstanceState)
{
base.OnSaveInstanceState(savedInstanceState);
savedInstanceState.PutInt(
"ActionBar.SelectedNavigationIndex",
ActionBar.SelectedNavigationIndex);
}
private void AddTab<TFragment>(
string tabText,
string tag,
Func<TFragment> ctor) where TFragment : Fragment
{
var tab = ActionBar.NewTab();
tab.SetText(tabText);
tab.SetTag(tag);
var fragment = FragmentManager.FindFragmentByTag<TFragment>(tag);
tab.TabSelected += (sender, e) =>
{
if (fragment == null)
{
fragment = ctor.Invoke();
e.FragmentTransaction.Add(
Resource.Id.fragmentContainer,
fragment,
tag);
}
else
{
e.FragmentTransaction.Attach(fragment);
}
};
tab.TabUnselected += (sender, e) =>
{
if (fragment != null)
{
e.FragmentTransaction.Detach(fragment);
}
};
ActionBar.AddTab(tab);
}
}
I'm trying to create custom AlertDialog but I have one problem.
My alert dialog class below:
class AddNewDayAlert : DialogFragment
{
public static AddNewDayAlert NewInstance(Bundle bundle)
{
AddNewDayAlert fragment = new AddNewDayAlert();
fragment.Arguments = bundle;
return fragment;
}
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
// Use this to return your custom view for this Fragment
View view = inflater.Inflate(Resource.Layout.AddNewDayLayout, container, false);
Button btn = view.FindViewById<Button>(Resource.Id.dialog_text);
EditText txt = view.FindViewById<EditText>(Resource.Id.dialog_text);
btn.Click += delegate
{
string day_name;
if (txt.Text != "" && txt.Text != null)
{
day_name = txt.Text;
Toast.MakeText(this.Activity, "Sėkmingai pridėta!!!", ToastLength.Long).Show();
Dismiss();
}
else
Toast.MakeText(this.Activity, "Pamiršote įvesti dienos pavadinimą!", ToastLength.Long).Show();
};
return view;
}
}
and problem is here:
Android.Support.V4.App.FragmentTransaction ft = FragmentManager.BeginTransaction();
Fragment prev = FragmentManager.FindFragmentByTag("dialog");
if (prev != null)
{
ft.Remove(prev);
}
ft.AddToBackStack(null);
AddNewDayAlert fragmentas = AddNewDayAlert.NewInstance(null);
fragmentas.Show(ft, "dialog");
In the last line I get an error because fragmentas.Show() is using Android.App.FragmentTransaction and I am using Android.Support.V4.App.FragmentTransaction.
If I try to use Android.App.FragmentTransaction I get error in first line:
Maybe there is way to convert? What should I do ?
You are conflicting two different FragmentTransaction classes. If you decide to use Support v4 Fragments, then you will want to use the Support v4 FragmentTransaction.
https://developer.android.com/reference/android/app/FragmentTransaction.html
https://developer.android.com/reference/android/support/v4/app/FragmentTransaction.html
You will also need to ensure that you are using a AppCompatActivity(Which inherits FragmentActivity and has a reference to Android.Support.V4.App.FragmentManager) if you go down the v4 route. That way you will have access to SupportFragmentManager that is the same type.
EX:
[Activity(Label = "App7", MainLauncher = true, Icon = "#drawable/icon")]
public class MainActivity : AppCompatActivity
{
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
// Set our view from the "main" layout resource
// SetContentView (Resource.Layout.Main);
Android.Support.V4.App.FragmentTransaction ft = SupportFragmentManager.BeginTransaction();
}
}
Your AddNewDayAlert class should also inherit from V4 DialogFragment
class AddNewDayAlert : Android.Support.V4.App.DialogFragment
I am new with Xamarin, but I am training by creating parking application. Now I got issue by trying to access to another layout.
This is my MainActivity.cs
[Activity(Label = "CustomActionBarParking", MainLauncher = true, Icon = "#drawable/icon", Theme ="#style/CustomActionBarTheme")]
public class MainActivity : Activity
{
private LinearLayout mBarZone;
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
ActionBar.SetDisplayShowCustomEnabled(true);
SetContentView(Resource.Layout.action_bar);
mBarZone = FindViewById<LinearLayout>(Resource.Id.linearLayout2);
mBarZone.Click += (object sender, EventArgs args) =>
{
SetContentView(Resource.Layout.zones_list);
};
}}}
Here I am accessing from my menu, by clicking on "Zones" action bar. And open "zones list" layout.
From here I want to access to another Layout: vehicle_not_parked by clicking on blue zone action bar button. But I don't know where I have to initialize it, because when I initialized that in MainAcitivy class on OnCreate method, then I got error, that my object is nullable. Then I create ZonesActivity.cs which looks like this:
[Activity(Label = "CustomActionBarParking")]
public class ZonesActivity : Activity
{
private LinearLayout mBlueZone;
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
SetContentView(Resource.Layout.zones_list);
mBlueZone = FindViewById<LinearLayout>(Resource.Id.linearLayout2);
mBlueZone.Click += (object sender, EventArgs args) =>
{
SetContentView(Resource.Layout.vehicle_not_parked);
};
}}}
But when I tryed to call this class in Main Activity class I have to deal with Bundle savedInstanceState property. I am not really know how I can from one view -> 2nd view and then -> 3rd view.
If I understand you correctly, your swapping out the layouts in the button click event? I think it would be best to start a new activity
mBarZone.Click += delegate {
StartActivity(typeof(ZonesActivity));
};
Docs on starting a new activity