How to upgrade MvxFragmentAttribute - xamarin.android

I'm updating my project to MvvmCross 6.0 from 4.3
The MvxFragmentAttribute is gone and seems to have been replaced by MvxFragmentPresentationAttribute
However when using the new Attribute I get the following error:
System.NullReferenceException: FrameLayout to show Fragment not found
at MvvmCross.Droid.Support.V7.AppCompat.MvxAppCompatViewPresenter.ShowFragment (System.Type view, MvvmCross.Platforms.Android.Presenters.Attributes.MvxFragmentPresentationAttribute attribute, MvvmCross.ViewModels.MvxViewModelRequest request) [0x00090] in <8141cf24d04c4cc6ba0be8d85f7b3f82>:0
at MvvmCross.Platforms.Android.Presenters.MvxAndroidViewPresenter.<RegisterAttributeTypes>b__21_2 (System.Type view, MvvmCross.Presenters.Attributes.MvxBasePresentationAttribute attribute, MvvmCross.ViewModels.MvxViewModelRequest request) [0x00000] in <17df0d0bdae848b7a8a12b58d710f763>:0
at MvvmCross.Platforms.Android.Presenters.MvxAndroidViewPresenter.Show (MvvmCross.ViewModels.MvxViewModelRequest request) [0x00014] in <17df0d0bdae848b7a8a12b58d710f763>:0
at MvvmCross.Platforms.Android.Views.MvxAndroidViewDispatcher+<>c__DisplayClass2_0.<ShowViewModel>b__0 () [0x00000] in <17df0d0bdae848b7a8a12b58d710f763>:0
at MvvmCross.Base.MvxMainThreadAsyncDispatcher+<>c__DisplayClass0_0.<ExecuteOnMainThreadAsync>b__0 () [0x00000] in <17df0d0bdae848b7a8a12b58d710f763>:0
at MvvmCross.Base.MvxMainThreadAsyncDispatcher+<>c__DisplayClass1_0+<<ExecuteOnMainThreadAsync>b__0>d.MoveNext () [0x00011] in <17df0d0bdae848b7a8a12b58d710f763>:0
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw () [0x0000c] in <f32579baafc1404fa37ba3ec1abdc0bd>:0
at System.Runtime.CompilerServices.AsyncMethodBuilderCore+<>c.<ThrowAsync>b__6_0 (System.Object state) [0x00000] in <f32579baafc1404fa37ba3ec1abdc0bd>:0
at Android.App.SyncContext+<>c__DisplayClass2_0.<Post>b__0 () [0x00000] in <263adecfa58f4c449f1ff56156d886fd>:0
at Java.Lang.Thread+RunnableImplementor.Run () [0x00008] in <263adecfa58f4c449f1ff56156d886fd>:0
at Java.Lang.IRunnableInvoker.n_Run (System.IntPtr jnienv, System.IntPtr native__this) [0x00008] in <263adecfa58f4c449f1ff56156d886fd>:0
at (wrapper dynamic-method) System.Object.515beccd-4ae3-42be-a59f-9c8897c1459b(intptr,intptr)
Unfortunately this does not tell me which FrameLayout or even which Fragment and the stack trace contains none of my code.
However only one of the Fragments has been hit before this happens:
Old Attribute usage:
[MvxFragment(typeof(MainViewModel), Resource.Id.main_frame_layout)]
New Attribute usage:
[MvxFragmentPresentation(typeof(MainViewModel), Resource.Id.main_frame_layout)]
And yes main_frame_layout does exist in my main_view layout which is as follows:
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:local="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/main_draw_layout"
android:fitsSystemWindows="true">
<android.support.design.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/main_frame_layout"
android:layout_centerInParent="true" />
</android.support.design.widget.CoordinatorLayout>
<FrameLayout
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="left|start"
android:id="#+id/main_navigation_layout" />
</android.support.v4.widget.DrawerLayout>

I was calling _navigationService.Navigate from the Start method of my ViewModel to show the fragment. However the lifecycle changed in MvvmCross 5 which is detailed here. The Start method now triggers before the view is shown or at least before it has finished being shown. This means that the Activity exists but the layout hasn't been inflated yet so main_frame_layout doesn't exist yet.
The fix was to move the Navigate call to the new ViewAppeared method.

Related

CustomComponent FindViewById from referenced project returns null

In C# Xamarin Android projects, I am trying to create a custom compound component to select a month. This extends LinearLayout.
I expect to re-use it (and keep my main project more tidy), so I have 2 projects - my main/parent project, containing my Activity and multiple Fragments; and a simple Android project for my component.
When I run the component project directly using its own Activity, it works as expected, but when I reference it from my main project, my FindViewById return null.
I can see that this and context are from the main project - and I would like to understand what I've done wrong or what I need to add so that it finds the component's layout and controls to inflate?
Main view (Ui.DateScrollPicker.Droid is my component's namespace and ScrollMonthPicker my class):
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingLeft="10dp"
android:paddingRight="10dp">
.......
<ui.datescrollpicker.droid.ScrollMonthPicker
android:id="#+id/my_scroll_month_picker"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
Referenced from a Android.Support.V4.App.Fragment in the override View OnCreateView method:
scrollMonthPicker = view.FindViewById<ScrollMonthPicker>(Resource.Id.scroll_month_picker);
scrollMonthPicker.DateChanged += DatePicker_Changed;
By extending LinearLayout, I am implementing 2 constructors, including, ScrollMonthPicker(Context context, IAttributeSet attrs) : base(context, attrs, 0) which is called, and goes off to inflate my component's layout.
Component view (scroll_month_layout.axml):
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<TextView
android:id="#+id/month_current_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Month" />
<TextView
android:id="#+id/year_current_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Year" />
<android.support.v7.widget.RecyclerView
android:id="#+id/month_recycler_view"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</merge>
ScrollMonthPicker constructor:
public ScrollMonthPicker(Context context, IAttributeSet attrs)
: base(context, attrs, 0)
{
InflateLayout(context, attrs);
}
ScrollMonthPicker inflating view:
private void InflateLayout(Context context, IAttributeSet attrs)
{
var inflater = LayoutInflater.FromContext(this.Context);
inflater.Inflate(Resource.Layout.scroll_month_layout, this);
monthCurrentText = FindViewById<TextView>(Resource.Id.month_current_text);
yearCurrentText = FindViewById<TextView>(Resource.Id.year_current_text);
monthRecyclerView = FindViewById<RecyclerView>(Resource.Id.month_recycler_view);
monthRecyclerView.HasFixedSize = true;
......
}
The component works fine when called from 'its own' Activity, but all FindViewById are null (monthCurrentText, yearCurrentText and monthRecyclerView) when referenced by the fragment in the main project.
If I wait for the exception to be thrown in the main fragment from monthRecyclerView.HasFixedSize, I do not get a null reference exception, but this one:
Unhandled Exception:
System.NotSupportedException: Could not activate JNI Handle 0x7fff30d40b00 (key_handle 0x8f4c53e) of Java type 'md53fd9f663a5e8ddcd0a5da1b57d05fd99/ScrollMonthPicker' as managed type 'Ui.DateScrollPicker.Droid.ScrollMonthPicker'.
How do I make it populate as expected by referencing from a separate project??
Many thanks.
It seems your both projects are the android project. You could not use another android project code in one project. I think you need to create your CustomComponent project as a library.

Close app after closing last fragment

I am writing an app with Xamarin.Android and MvvmCross. I am using fragments and have a "content container" design where I show the fragments in my content_frame view:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:local="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center">
<FrameLayout
android:id="#+id/content_frame"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_centerInParent="true" />
</FrameLayout>
Everything works, and I can navigate through my app by showing/closing the fragments in my content_frame, but when I close the last fragment, instead of closing the app, it shows a blank screen instead. I reckon I can fix this in my MvxAppCompatViewPresenter class but I don't know how? I currently don't have anything in my view presenter:
public class ViewPresenter : MvxAppCompatViewPresenter
{
public ViewPresenter(IEnumerable<Assembly> androidViewAssemblies) : base(androidViewAssemblies)
{
}
public override void Show(MvxViewModelRequest request)
{
base.Show(request);
}
public override void Close(IMvxViewModel viewModel)
{
base.Close(viewModel);
}
}
Here is the first fragment:
[MvxFragmentPresentation(typeof(LoginViewModel), Resource.Id.content_frame, true)]
[Register("myapp.droid.fragments.LoginSelectionFragment")]
public class LoginSelectionFragment : BaseFragment<LoginSelectionViewModel>
{
protected override int FragmentId => Resource.Layout.fragment_login_selection;
}
One of the approaches you can use, is to not add the fragments that are starting fragments to the backstack by setting the MvxFragmentPresentation property AddToBackStack to false (false is also the default if no parameter is passed).
The idea there is that for the first fragment you would rely on the activity being added to the backstack. Essentially, the first fragment and the activity could then be considered the same with respect to the backstack, eliminating the blank screen.
However, this would only work if the starting fragments do not need to be added to the backstack, with in the same activity context, later in the navigational flow. In future versions of MvvmCross you would easily be able to overcome this limitation via the updated IMvxOverridePresentationAttribute.

can we cast EditText to TextView in android

by mistake i cast my editText view to Textview class object, but i did't get class cast exception
here is my code. let me explain , why this??
in my .xml file
<EditText
android:id="#+id/adduser_phone"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="#dimen/margin_10"
android:hint="#string/mob_no"
android:inputType="phone"
/>
in activity class.
TextView adduser_phone = (TextView) findViewById(R.id.adduser_phone);
here not hit the class castexception. why..???
yes you can do so because EditText is sublcass of TextView
EditText is a thin veneer over TextView that configures itself to be editable.
checkout dev link1 dev link2for detail

add layout to splashscreen

Is it possible to add a layout to the MvxSplashScreenActivity? I have overiden the OnViewModelSet like in all the other activities and placed the following code:
protected override void OnViewModelSet()
{
base.OnViewModelSet ();
SetContentView (Resource.Layout.SplashScreen);
}
The layout I am trying to load is:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:minWidth="25px"
android:minHeight="25px">
<ImageView
android:src="#drawable/applogo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/imageView1"
android:layout_centerInParent="true" /></RelativeLayout>
and I am getting the following exception:
Cirrious.CrossCore.Exceptions.MvxException: Failed to resolve type
Cirrious.MvvmCross.Binding.BindingContext.IMvxBindingContextStack`1[[Cirrious.MvvmCross.Binding.Droid.BindingContext.IMvxAndroidBindingContext,
Cirrious.MvvmCross.Binding.Droid, Version=1.0.0.0, Culture=neutral,
PublicKeyToken=null]]
I cant seem to find anything online regarding the mvvmcross splash screen..
Ahy Ideas?
You can't use a databound layout within the splashscreen - the splashscreen is displayed before mvvmcross is fully started.
However, for a simple layout, you do pass a resource Id down to the base class constructor:
public class SplashScreen : MvxSplashScreenActivity
{
public SplashScreen()
: base(Resource.Layout.SplashScreen)
{
}
}
Further - to avoid black start screens - most people use a theme to specify a whole screen image in their splashscreen - see the 'standard' splash screen supplied by nuget - https://github.com/slodge/MvvmCross/blob/v3/nuspec/DroidContent/SplashScreen.cs.pp
Ensure Initialize Setup before calling OnCreate.
protected override void OnCreate(Bundle bundle)
{
var setupSingleton = MvxAndroidSetupSingleton.EnsureSingletonAvailable(this);
setupSingleton.EnsureInitialized();
base.OnCreate(bundle);
}

mouseover actionscript

I am designing a page in flex and I have a one image. Specific text should be shown when user hovers mouse to the image. Here is my actionScript code that I wrote but it is not working (it is not showing text on mouseOver event:(
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx" minWidth="100" minHeight="100">
<fx:Script>
private var helpText:String = "Some Text."
private function helpIconEvent(e:MouseEvent):void{
if(e.type == "mouseOver"){
e.currentTarget.helpText.visible = true;
}
}
private function addEventToHelpIcon():void {
helpIcon.addEventListener(MouseEvent.MOUSE_OVER, helpIconEvent);
}
</fx:Script>
<mx:Image id="helpIcon" x="270" y="187" width="50" height="50" mouseOver="addEventToHelpIcon"
source="source_path"/>
Any help/insight will be highly appreciated.
Thanks.
There are several issues:
You are not adding the mouse over listener correctly. You are actually adding two event listeners, one in MXML and then when that event happens you add the second one. Just use the MXML listener (see below).
In function that runs when the mouse over happens, you are trying to set the visible property on a String object. A String by itself will not display anything. You can display the String with a Label object, in a tool-tip, or some other GUI object. You need to figure out the right GUI object to use and pass the text to that object.
Here's a very simple example:
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
minWidth="100" minHeight="100">
<fx:Script>
private function onMouseOver():void {
helpLabel.visible=true;
}
private function onMouseOut():void {
helpLabel.visible=false;
}
</fx:Script>
<s:Image id="helpIcon" x="270" y="187"
width="50" height="50"
mouseOver="onMouseOver()" mouseOut="onMouseOut()"
source="source_path"/>
<!-- note the mouse event handlers are so simple in this case, you can also do them in line -->
<s:Image id="alternateMethod" mouseOver="helpLabel.visible=true;"
mouseOut="helpLabel.visible=false;" />
<s:Label id="helpLabel" x="100" y="100" visible="false" text="Some Text."/>
</s:Application>
It think your code should be more like this, but I'm not used to flex so bear with me if I'm wrong.
private function foo(e:MouseEvent):void {
if(e.type == MouseEvent.ROLL_OVER)
//Do stuff...
}

Resources