I want to display a toast Message. If I'd do this in onCreate() it'd work fine. But I want to do it like this and I get an error:
Java.Lang.NullPointerException: Attempt to invoke virtual method
'android.content.res.Resources android.content.Context.getResources()'
on a null object reference
What should I do?
public void textToast(string textToDisplay) {
Toast.MakeText(this,
textToDisplay, ToastLength.Long).Show();
}
class SampleTabFragment : Fragment
{
Button add;
MainActivity main = new MainActivity();
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
base.OnCreateView(inflater, container, savedInstanceState);
var view = inflater.Inflate(Resource.Layout.Tab, container, false);
add = view.FindViewById<Button>(Resource.Id.add);
add.Click += Click;
return view;
}
void Click(object sender, EventArgs eventArgs)
{
main.textToast( "I like Toast!");
}
}
The Java.Lang.NullPointerException is triggered because you are manually creating and using an instance of MainActivity.
Instead of using a custom instance of MainActivity to display your toast message in Click, simplify your code to use the fragments existing activity reference:
public void textToast(string textToDisplay) {
Toast.MakeText(this,
textToDisplay, ToastLength.Long).Show();
}
class SampleTabFragment : Fragment
{
Button add;
// Remove manual creation code
// MainActivity main = new MainActivity();
// ...
void Click(object sender, EventArgs eventArgs)
{
(Activity as MainActivity).textToast( "I like Toast!");
}
}
This code assumes that the owning activity is always an instance of MainActivity.
See:
Fragment getActivity()
How to use the method of an Activity in a DialogFragment?
If I understand correctly your question, I think a good solution may be this one:
public void makeToast(Context ctx, string str)
{
Toast.MakeText(ctx, str, ToastLength.Long).Show();
}
And when you use it in every fragment you have, you can call it just writing:
makeToast(this.Activity, "test!");
Works for me, let me know :)
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 am creating an Android App using Xamarin. Basically the app detect the incoming SMS and if the sender is a specific number the GUI get updated. I have created a class that inherits BroadcastReceiver and at the proper conditions Raises the event but the event is always null even though I subscribe with a method that when is susbcribed in a button click event works correctly (I create an instance of this class in MainActivity and add the EventHandler in OnCreate method) After the research it seems a bit strange that the code EventHandler is null... What am I doing wrong?
Thanks in advance!
BroadcastReceiver
[BroadcastReceiver(Exported = true, Enabled = true)]
[IntentFilter(new[] { "android.provider.Telephony.SMS_RECEIVED"}, Priority =(int)IntentFilterPriority.HighPriority)]
public class NewSMSReceived : BroadcastReceiver
{
public event EventHandler SMSArrived;
public override void OnReceive(Context context, Intent intent)
{
if (Android.Provider.Telephony.Sms.Intents.SmsReceivedAction.Equals(intent.Action))
{
var info_Intent = intent.Extras;
if (info_Intent != null)
{
Java.Lang.Object[] pdus = (Java.Lang.Object[]) info_Intent.Get("pdus");
var msg = SmsMessage.CreateFromPdu((byte[]) pdus[0]);
var msg_from = msg.DisplayOriginatingAddress;
if (msg_from == "xxxxxxxxxx")
{
Toast.MakeText(Application.Context, "New message from Weather Station", ToastLength.Short).Show();
SMSArrived?.Invoke(this, EventArgs.Empty);
}
}
}
}
}
Main Activity Code
public class MainActivity : AppCompatActivity
{
private NewSMSReceived SMSNotificator = new NewSMSReceived();
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
SetContentView(Resource.Layout.activity_main);
this.SMSNotificator.SMSArrived += new EventHandler(this.showNewData);
}
private void showNewData(object sender, EventArgs e)
{
//
}
}
I made it work using the answer of question Xamarin Android : Change UI TextView text from Service or Receiver and the comment of #SushiHangover helped me realize the problem. Registering the Instance of NewSMSReceived using OnResume method and unregistering it using OnPause method solved the problem (at the first try I did non delete the property IntentFilter of NewSMSReceived and it did not worked). Code modifications are as follows:
[BroadcastReceiver(Exported = true, Enabled = true)]
public class NewSMSReceived : BroadcastReceiver
{
public event EventHandler SMSArrived;
//nothing changes
Code in MainActivity (Instance is initialized only once and event is subscribed at "OnCreate" method)
protected override void OnResume()
{
base.OnResume();
var NewSMS = new IntentFilter("android.provider.Telephony.SMS_RECEIVED");
RegisterReceiver(this.SMSNotificator, NewSMS);
}
protected override void OnPause()
{
base.OnPause();
UnregisterReceiver(this.SMSNotificator);
}
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 have tried many ways, but haven't succeeded.
In this way I can create a new UIBarButtonItem and it works, the problem is that it dosent lock like a backButton/ArrowBackButton:
public override void ViewWillAppear (bool animated)
{
base.ViewWillAppear (animated);
this.NavigationItem.LeftBarButtonItem = new UIBarButtonItem ("Tillbaka", UIBarButtonItemStyle.Plain, delegate(object sender, EventArgs e) {
this.NavigationController.PopViewControllerAnimated (true);
});
}
Have tried this, but haven't worked:
public override void ViewWillAppear (bool animated)
{
base.ViewWillAppear (animated);
this.NavigationItem.SetHidesBackButton(false,true);
}
With MonoTouch.Dialog you have to set a "pushing" flag in order for it to show the back button. You can do this in the constructor, as indicated below:
public class MyViewController : DialogViewController
{
public MyViewController
: base(new RootElement("foo"), true)
{
}
}
Can you give some context to this please? How is this ViewController being added to the NavigationController ?
If you use the PushViewController method (as below) then a back button will be automatically added.
var viewController = new UIViewController();
this.NavigationController.PushViewController(viewController, true);