Xamarin MVVMCross - Binding on a Google Maps InfoWindow - xamarin

I´m trying to use MvvmCross binding inside a Google Maps InfoWindow, on Android. I have a MvxFragment with a MapFragment inside.
I created a custom xml layout for the InfoWindow and tried to use a MvxFrameControl to display it, like this:
public class InfoWindow : Java.Lang.Object, GoogleMap.IInfoWindowAdapter
{
private readonly BaseStateFragment _window;
private readonly Dictionary<string, Restaurant> _restaurants;
public InfoWindow(BaseStateFragment window, Dictionary<string, Restaurant> restaurants)
{
_window = window;
_restaurants = restaurants;
}
public View GetInfoContents(Marker p0)
{
var layoutContainer = new MvxFrameControl(Resource.Layout.fragment_home_map_info, _window.Context, null)
{
LayoutParameters = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WrapContent, ViewGroup.LayoutParams.WrapContent),
DataContext = _restaurants[p0.Id] //ViewModel
};
return layoutContainer;
}
public View GetInfoWindow(Marker p0)
{
return null;
}
When I click on the marker, the method GetInfoContents is called but nothing is happening (the event binding is ok, I got it to work without binding).
Any ideas ?
As Requested, the layout file:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<include
android:id="#+id/restaurant_item"
android:layout_width="310dp"
android:layout_height="190dp"
layout="#layout/item_list_restaurant" />
<Button
android:id="#+id/bt_open_restaurant"
android:layout_width="310dp"
android:layout_height="wrap_content"
android:text="#string/label_go_restaurant_page"
android:textColor="#color/colorFacebook"
android:background="#color/lightBlue"
android:paddingStart="8dp"
android:paddingEnd="8dp"
android:layout_gravity="bottom"
android:layout_marginTop="8dp"
android:textAllCaps="true"
android:layout_marginBottom="4dp" />
</FrameLayout>

Related

How to persist a choice in a Xamarin Android alert dialog

This dialog box displays correctly, except that the user's choice is not captured:
var dialogView = LayoutInflater.Inflate(Resource.Layout.list_view, null);
Android.App.AlertDialog alertDialog;
var items = new string[] { "A","B","C" };
var adapter = new ArrayAdapter<string>(this, Android.Resource.Layout.SimpleListItem1, items);
using (var dialog = new Android.App.AlertDialog.Builder(this))
{
dialog.SetTitle("Choose Letter");
dialog.SetMessage("Just Click!");
dialog.SetView(dialogView);
dialog.SetNegativeButton("Cancel", (s, a) => { });
dialog.SetPositiveButton("OK", (s, a) => {
{
if (a.Which!=-1)
//BUT I don't know how to persist the choice
//when I click on one of the letters, it briefly
//shows the choice (the background is briefly grayed
//but the choice doesn't persist
//so when I click OK, a.Which is -1
{
//do things with the choice
}
}});
alertDialog = dialog.Create();
}
dialogView.FindViewById<ListView>(Resource.Id.listview).Adapter = adapter;
alertDialog.Show();
}
And this is the axml:
<?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">
<ListView
android:id="#+id/listview"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
How do I 1) show the user's choice more than the line being briefly grayed and 2) how do I persist that choice?
Do you want to achieve the result like following GIF?
If so, you can create a ListItem to replace the Android.Resource.Layout.SimpleListItem1
Here is my ListItem
var adapter = new ArrayAdapter<string>(this, Resource.Layout.list_item, items);
list_item
<?xml version="1.0" encoding="utf-8" ?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#android:id/text1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#drawable/mybackground"
android:gravity="center_vertical"
android:padding="3dp"
android:textSize="20sp"
/>
mybackground
<?xml version="1.0" encoding="utf-8" ?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="#android:color/background_light"
android:state_pressed="false" android:state_selected="false"/>
<item android:drawable="#android:color/background_dark"
android:state_pressed="true"/>
<item android:drawable="#color/light_blue" android:state_pressed="false"
android:state_selected="true"/>
And achieve the listview.ItemClick += Listview_ItemClick;
private void Listview_ItemClick(object sender, AdapterView.ItemClickEventArgs e)
{
e.View.Selected = true;
}
Here is my demo.
https://github.com/851265601/Xamarin.Android_ListviewSelect

How to persist a choice in a Xamarin Alert Dialog [duplicate]

This dialog box displays correctly, except that the user's choice is not captured:
var dialogView = LayoutInflater.Inflate(Resource.Layout.list_view, null);
Android.App.AlertDialog alertDialog;
var items = new string[] { "A","B","C" };
var adapter = new ArrayAdapter<string>(this, Android.Resource.Layout.SimpleListItem1, items);
using (var dialog = new Android.App.AlertDialog.Builder(this))
{
dialog.SetTitle("Choose Letter");
dialog.SetMessage("Just Click!");
dialog.SetView(dialogView);
dialog.SetNegativeButton("Cancel", (s, a) => { });
dialog.SetPositiveButton("OK", (s, a) => {
{
if (a.Which!=-1)
//BUT I don't know how to persist the choice
//when I click on one of the letters, it briefly
//shows the choice (the background is briefly grayed
//but the choice doesn't persist
//so when I click OK, a.Which is -1
{
//do things with the choice
}
}});
alertDialog = dialog.Create();
}
dialogView.FindViewById<ListView>(Resource.Id.listview).Adapter = adapter;
alertDialog.Show();
}
And this is the axml:
<?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">
<ListView
android:id="#+id/listview"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
How do I 1) show the user's choice more than the line being briefly grayed and 2) how do I persist that choice?
Do you want to achieve the result like following GIF?
If so, you can create a ListItem to replace the Android.Resource.Layout.SimpleListItem1
Here is my ListItem
var adapter = new ArrayAdapter<string>(this, Resource.Layout.list_item, items);
list_item
<?xml version="1.0" encoding="utf-8" ?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#android:id/text1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#drawable/mybackground"
android:gravity="center_vertical"
android:padding="3dp"
android:textSize="20sp"
/>
mybackground
<?xml version="1.0" encoding="utf-8" ?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="#android:color/background_light"
android:state_pressed="false" android:state_selected="false"/>
<item android:drawable="#android:color/background_dark"
android:state_pressed="true"/>
<item android:drawable="#color/light_blue" android:state_pressed="false"
android:state_selected="true"/>
And achieve the listview.ItemClick += Listview_ItemClick;
private void Listview_ItemClick(object sender, AdapterView.ItemClickEventArgs e)
{
e.View.Selected = true;
}
Here is my demo.
https://github.com/851265601/Xamarin.Android_ListviewSelect

Bizzare behavior of NotifyDataSetChanged() Xamarin.Android

I am working on an application that asks the user to provide photos for items.
My main object is following
public class PickedObject
{
int ID { get; set; }
int Name{ get; set; }
bool HasPhotos { get; set; }
}
And I have another table for Photos since one item can have multiple photos.
What's happening is that in my adapter, I have created a recycler view so that if an Item has images, there should be an ImageButton visible in front of it.
Here is my row template
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:weightSum="8">
<TextView
android:id="#+id/lblItemName"
android:layout_width="0dp"
android:layout_weight="6"
android:layout_marginTop="5dp"
android:layout_marginLeft="10dp"
android:layout_height="wrap_content"
android:textSize="20dp"
android:text="" />
<ImageButton
android:layout_weight="1"
android:layout_width="35dp"
android:layout_height="35dp"
android:adjustViewBounds="true"
android:scaleType="fitCenter"
android:layout_marginTop="5dp"
android:background="#android:color/transparent"
android:padding="5dp"
android:src="#drawable/rowcamera"
android:id="#+id/btnCamera" />
<ImageButton
android:layout_weight="1"
android:layout_width="35dp"
android:layout_height="35dp"
android:adjustViewBounds="true"
android:scaleType="fitCenter"
android:layout_marginTop="5dp"
android:background="#android:color/transparent"
android:padding="5dp"
android:src="#drawable/rowpicture"
android:id="#+id/btnPicture" />
</LinearLayout>
In my adapter I am using the following in GetView(int position, View convertView, ViewGroup parent)
public override View GetView(int position, View convertView, ViewGroup parent)
{
ItemViewHolder holder = null;
PickedObject item = _items[position];
View view = convertView;
try
{
if (view != null)
holder = (ItemViewHolder)view.Tag; //Holder
if(holder == null)
{
holder = new ItemViewHolder();
view = _context.LayoutInflater.Inflate(Resource.Layout.ItemRow, null);
holder.ItemName = view.FindViewById<TextView>(Resource.Id.lblItemName);
holder.CameraButton = view.FindViewById<ImageButton>(Resource.Id.btnCamera);
holder.CameraButton.Tag = item.ID;
holder.PictureButton = view.FindViewById<ImageButton>(Resource.Id.btnPicture);
holder.PictureButton.Tag = item.ID;
CameraClickListener cameraListener = new CameraClickListener(_context, this);
cameraListener.CameraClickEvent += CameraClickedEvent;
holder.CameraButton.SetOnClickListener(cameraListener);
ImageClickListener imageClickListener = new ImageClickListener(_context);
imageClickListener.ImageClickEvent += ImageClickedEvent;
holder.PictureButton.SetOnClickListener(imageClickListener);
view.Tag = holder;
}
holder.ItemName.Text = item.Name;
holder.CameraButton.Visibility = ViewStates.Visible;
if (item.HasPhotos)
{
holder.PictureButton.Visibility = ViewStates.Invisible;
}
else
{
holder.PictureButton.Visibility = ViewStates.Visible;
}
}
catch(Exception ex)
{
Log.Error("Item adapter", ex.Message);
}
return view;
}
Now in my MainActivity I have called two activities (for result), One is the camera activity that starts the camera and saves the photo.
The other activity is a gallery type activity that is created within the project.
The activity on result is called as follows:
protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
{
base.OnActivityResult(requestCode, resultCode, data);
int itemId = CameraApp.ID; // Camera App is a static object to hold data
if (requestCode == General.CAMERA_ACTIVITY) // Camera activity code 110
{
CompleteCameraActivity(resultCode, data); // Save picture into gallery and update db
}
if(requestCode == General.GALLERY_ACTIVITY) // Gallery activity code 113
{
CompleteGalleryActivity(resultCode, data);
}
SectionFragment frag = (SectionFragment)sectionsAdapter.GetItem(viewPager.CurrentItem);
int sectionId = frag.Section.ID;
frag.Adapter.SetPicture(questionId, dataAccess.HasPictures(itemId));
}
The SetPicture method in the adapter is as follows:
public void SetPicture(int id, bool hasPics)
{
Item itm = _items.SingleOrDefault(a => a.ID == id);
if (itm != null)
_items.SingleOrDefault(a => a.ID == id).HasPhotos = hasPics;
this.NotifyDataSetChanged();
}
The application works fine when it comes from Camera activity. It updates the record and also the image button is visible as well.
The trouble comes when the MainActivity gains control after GalleryActivity. When I delete all the images in the gallery the SetPicture method is executed which makes the HasPhotos property to false. But the NotifyDataSetChanged part of the adapter doesn't work (when I test it in debugger the GetView method is not fired for the adapter).
So what happens is that my ListView remains in a state where even if the item has no photos the ImageButton (for pictures) is still available.
Can someone please let me know what I am doing wrong?
EDIT
I noticed that if I scroll the list once and return back to the item, the ImageButton is invisible, that means that it is working but why doesn't it work in StartActivityForResult?

VS Android App (Xamarin) Navigation Drawer App - Add Activity

I have used VS 2017 to create an Android App (Xamarin) Navigation Drawer App. I have searched the internet for an example of how to add a new activity to the app that uses the already created Navigation Drawer but have been unsuccessful. Any idea's on how to add an activity?
Thanks
Paul.
public bool OnNavigationItemSelected(IMenuItem item)
{
int id = item.ItemId;
if (id == Resource.Id.nav_camera)
{
// Run a new activity here!
}
else if (id == Resource.Id.nav_gallery)
{
}
else if (id == Resource.Id.nav_slideshow)
{
}
else if (id == Resource.Id.nav_manage)
{
}
else if (id == Resource.Id.nav_share)
{
}
else if (id == Resource.Id.nav_send)
{
}
DrawerLayout drawer = FindViewById<DrawerLayout>(Resource.Id.drawer_layout);
drawer.CloseDrawer(GravityCompat.Start);
return true;
}
From here, you can see this:
If your app switches out content based on which navigation menu item the user selects, you should consider using fragments in the main content area. Swapping fragments when you navigate from the navigation drawer allows for a seamless drawer animation, because the same base layout stays in place.
Official suggest us use fragment in the main content area.
If you want to start a new Activity, you need create a Activity and create a layout for it, like Activity1 :
[Activity(Label = "Activity1")]
public class Activity1 : Activity
{
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
// Create your application here
SetContentView(Resource.Layout.layout1);
}
}
layout1:
<?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">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="new Activity" />
</LinearLayout>
And then start it:
if (id == Resource.Id.nav_camera)
{
Intent intent = new Intent(this, typeof(Activity1));
StartActivity(intent);
}
This will open a new Activity, and in the new Activity, there is no DrawerLayout.

How to change MvxDialogFragment layout

I want to change the default AlertDialog layout size.
I created class:
public class SampleDialog : MvxDialogFragment
{
public override Dialog OnCreateDialog(Bundle savedInstanceState)
{
var dialog = new AlertDialog.Builder(Context);
dialog.SetView(View.Inflate(Context,Resource.Layout.SampleDialog,null));
return dialog.Create();
}
public override void OnStart()
{
if (Dialog == null) { return; }
Dialog.Window.SetLayout(200,460);
base.OnStart();
}
}
and it's layout:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="#string/text1" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="#string/text2" />
</LinearLayout>
And i'm creating dialog from fragment view:
var dialog = new SampleDialog
{
ViewModel = ViewModel,
Cancelable = true
};
dialog.Show(FragmentManager, "");
Tryed to set layout width in OnCreateDialog method,OnStart, or directly by setting LinearLayout.layout_width property, but the result is the same.
How this can be configured?
Inflating the layout
When inflating the layout you can make use of either the default inflator or Mvvmcross BindingInflate which will apply any xml bindings you require.
Default inflator:
public override Dialog OnCreateDialog(Bundle savedInstanceState)
{
base.EnsureBindingContextSet(savedInstanceState);
var dialog = new AlertDialog.Builder(Context);
dialog.SetView(Activity.LayoutInflater.Inflate(Resource.Layout.SampleDialog, null));
return dialog.Create();
}
Mvvmcross xml binding inflator:
public override Dialog OnCreateDialog(Bundle savedInstanceState)
{
base.EnsureBindingContextSet(savedInstanceState);
var dialog = new AlertDialog.Builder(Context);
dialog.SetView(this.BindingInflate(Resource.Layout.SampleDialog, null));
return dialog.Create();
}
Sizing the layout
The Dialog.Window.SetLayout() takes in the pixel values for 200(width) and 460(height) which will vary in physical size with different device screen resolutions.
A better approach would be to use dp and convert it to pixels:
Add the desired size dimensions to your dimens.xml
<resources>
<dimen name="dialog_width">400dp</dimen>
<dimen name="dialog_height">200dp</dimen>
</resources>
Update your SampleDialog
public override void OnStart()
{
base.OnStart();
var width = Resources.GetDimension(Resource.Dimension.dialog_width);
var height = Resources.GetDimension(Resource.Dimension.dialog_height);
Dialog.Window.SetLayout((int)width, (int)height);
}

Resources