In my xamarin android app, AltBeacon DidEnterRegion not triggered when MainLauncher attribute set to false for MainActivity. As I have SplashActivity for showing splash screen, I have to make the SplashActivity as my MainLauncher. I am using foreground service to detect iBeacon.
Code of my Application class:
namespace AltBeaconLibrarySample.Droid
{
#if DEBUG
[Application(Debuggable = true)]
#else
[Application(Debuggable = false)]
#endif
public class MainApplication : Application, IBootstrapNotifier
{
private RegionBootstrap regionBootstrap;
private BackgroundPowerSaver backgroundPowerSaver;
Region _generalRegion;
public bool IsStartedRanging { get; set; }
string foregroundServiceChannelId = "foregroundService";
string channelName = "ForegroundService";
int pendingIntentId = 1;
public MainApplication(IntPtr handle, JniHandleOwnership transer)
: base(handle, transer)
{
}
public void DidDetermineStateForRegion(int p0, Region p1)
{
}
public void DidEnterRegion(Region p0)
{
var beaconService = Xamarin.Forms.DependencyService.Get<IAltBeaconService>();
if (!IsStartedRanging)
{
beaconService.StartRanging();
IsStartedRanging = true;
}
}
public void DidExitRegion(Region p0)
{
var beaconService = Xamarin.Forms.DependencyService.Get<IAltBeaconService>();
if (IsStartedRanging)
{
beaconService.StopRanging();
IsStartedRanging = false;
}
beaconService.DidExitRegion();
}
public override void OnCreate()
{
base.OnCreate();
CrossCurrentActivity.Current.Init(this);
BeaconManager bm = BeaconManager.GetInstanceForApplication(this);
CreateNotificationChannel();
bm.EnableForegroundServiceScanning(GetForegroundServiceNotification(), 456);
bm.SetEnableScheduledScanJobs(false);
_generalRegion = new Org.Altbeacon.Beacon.Region/* AltBeaconOrg.BoundBeacon.Region*/("myEmptyBeaconId", Identifier.Parse("23A01AF0-232A-4518-9C0E-323FB773F5EF"), null, null);
regionBootstrap = new RegionBootstrap(this, _generalRegion);
backgroundPowerSaver = new BackgroundPowerSaver(this);
}
void CreateNotificationChannel()
{
if (Build.VERSION.SdkInt < BuildVersionCodes.O)
{
// Notification channels are new in API 26 (and not a part of the
// support library). There is no need to create a notification
// channel on older versions of Android.
return;
}
var channelDescription = "Foreground Sertrvice";
var channel = new NotificationChannel(foregroundServiceChannelId, channelName, NotificationImportance.High)
{
Description = channelDescription
};
var notificationManager = (NotificationManager)GetSystemService(NotificationService);
notificationManager.CreateNotificationChannel(channel);
}
public Notification GetForegroundServiceNotification()
{
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, foregroundServiceChannelId);
builder.SetSmallIcon(Resource.Drawable.xamagonBlue);
builder.SetContentTitle("Scanning for Beacons");
Intent intent = new Intent(this, typeof(MainActivity));
PendingIntent pendingIntent = PendingIntent.GetActivity(this, pendingIntentId, intent, PendingIntentFlags.UpdateCurrent);
builder.SetContentIntent(pendingIntent);
return builder.Build();
}
}
}
Code of SplashActivity class:
namespace AltBeaconLibrarySample.Droid
{
[Activity(Label = "AltBeaconLibrarySample.Droid",
Icon = "#mipmap/icon",
Theme = "#style/MainTheme",
MainLauncher = true,
NoHistory = true,
LaunchMode = Android.Content.PM.LaunchMode.SingleInstance,
ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
public class SplashActivity : Activity
{
static readonly string TAG = "X:" + typeof(SplashActivity).Name;
public override void OnCreate(Bundle savedInstanceState, PersistableBundle persistentState)
{
base.OnCreate(savedInstanceState, persistentState);
}
// Launches the startup task
protected override void OnResume()
{
base.OnResume();
Task startupWork = new Task(() => { SimulateStartup(); });
startupWork.Start();
}
// Simulates background work that happens behind the splash screen
async void SimulateStartup()
{
Log.Debug(TAG, "Performing some startup work that takes a bit of time.");
await Task.Delay(50); // Simulate a bit of startup work.
Log.Debug(TAG, "Startup work is finished - starting MainActivity.");
StartActivity(new Intent(Application.Context, typeof(MainActivity)));
}
}
}
Android manifest:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="com.companyname.AltBeaconLibrarySample" android:installLocation="auto">
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="28" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<application android:label="AltBeaconLibrarySample.Android">
<service android:enabled="true" android:exported="false" android:isolatedProcess="false" android:label="Beacon" android:name="org.altbeacon.beacon.service.BeaconService"></service>
<service android:enabled="true" android:exported="false" android:name="org.altbeacon.beacon.BeaconIntentProcessor"></service>
<receiver android:name="org.altbeacon.beacon.startup.StartupBroadcastReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.ACTION_POWER_CONNECTED" />
<action android:name="android.intent.action.ACTION_POWER_DISCONNECTED" />
</intent-filter>
</receiver>
</application>
</manifest>
Code of MainActivity class:
namespace AltBeaconLibrarySample.Droid
{
[Activity(Label = "AltBeaconLibrarySample.Droid",
Icon = "#mipmap/icon",
Theme = "#style/MainTheme",
MainLauncher = false,
LaunchMode = Android.Content.PM.LaunchMode.SingleInstance,
ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity, IBeaconConsumer
{
protected override void OnCreate(Bundle savedInstanceState)
{
TabLayoutResource = Resource.Layout.Tabbar;
ToolbarResource = Resource.Layout.Toolbar;
base.OnCreate(savedInstanceState);
Xamarin.Essentials.Platform.Init(this, savedInstanceState); // add this line to your code, it may also be called: bundle
CrossCurrentActivity.Current.Init(this, savedInstanceState);
global::Xamarin.Forms.Forms.Init(this, savedInstanceState);
LoadApplication(new App());
}
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults)
{
Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);
PermissionsImplementation.Current.OnRequestPermissionsResult(requestCode, permissions, grantResults);
base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
}
#region IBeaconConsumer Implementation
public void OnBeaconServiceConnect()
{
}
#endregion
protected override void OnDestroy()
{
base.OnDestroy();
DependencyService.Get<IAltBeaconService>().OnDestroy();
}
protected override void OnResume()
{
base.OnResume();
}
protected override void OnPause()
{
base.OnPause();
}
}
}
BeaconManager code snippet:
BeaconManager bm = BeaconManager.GetInstanceForApplication(Plugin.CurrentActivity.CrossCurrentActivity.Current.Activity);
var iBeaconParser = new BeaconParser();
// Estimote > 2013
iBeaconParser.SetBeaconLayout("m:2-3=0215,i:4-19,i:20-21,i:22-23,p:24-24");
bm.BeaconParsers.Add(iBeaconParser);
bm.BackgroundMode = false;
bm.Bind((IBeaconConsumer)Plugin.CurrentActivity.CrossCurrentActivity.Current.Activity);
Related
I have a problem when dragging an image from the camera and making it wallpaper to ImageView.
I used the following code and it works on versions less than 5 .
But versions 5 or higher code does not work permanently and the camera does not work at all
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="com.companyname.camera_capimage" android:grantUriPermissions="true" android:installLocation="auto">
<uses-sdk android:minSdkVersion="17" android:targetSdkVersion="28" />
<application android:allowBackup="true" android:icon="#mipmap/ic_launcher" android:label="#string/app_name" android:roundIcon="#mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="#style/AppTheme"></application>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-feature android:name="android.hardware.camera" />
</manifest>
MainActivity
public class MainActivity : AppCompatActivity
{
public static Bitmap bitmap;
public static string fileName;
public ImageView _imageView;
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
Xamarin.Essentials.Platform.Init(this, savedInstanceState);
// Set our view from the "main" layout resource
SetContentView(Resource.Layout.activity_main);
Button button = FindViewById<Button>(Resource.Id.button1);
_imageView = FindViewById<ImageView>(Resource.Id.imageView1);
button.Click += BtnCamera_Click;
}
protected override void OnActivityResult(int requestCode, [GeneratedEnum] Result resultCode, Intent data)
{
base.OnActivityResult(requestCode, resultCode, data);
Bitmap bitmap = (Bitmap)data.Extras.Get("data");
_imageView.SetImageBitmap(bitmap);
}
private void BtnCamera_Click(object sender, System.EventArgs e)
{
Intent intent = new Intent(MediaStore.ActionImageCapture);
StartActivityForResult(intent, 0);
}
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults)
{
Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);
base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
error message
Java.Lang.SecurityException: 'Permission Denial: starting Intent { act=android.media.action.IMAGE_CAPTURE cmp=com.android.camera/.Camera } from ProcessRecord{273b163 23970:com.companyname.camera_capimage/u0a98} (pid=23970, uid=10098) with revoked permission android.permission.CAMERA'
Experiencing two issues with a custom map/renderer on iOS.
Demo video: https://ufile.io/pscn3
I have a custom map class with a circle that is placed on the map.
A slider control resizes the circle from a bindable property.
When slider value changes, the circle's radius property gets updated with the selected value. But as you can see, it's not updating the radius on map, instead it moves the circle to new positions within a curve.
When the circle is moved outside x pixels, it disappears or gets cut off outside the visible bounds.
These are the classes being used:
Page.xaml:
<?xml version="1.0" encoding="UTF-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:vm="clr-namespace:CompanyName.Data.ViewModels.MapWithCircleSlider;assembly=CompanyName"
xmlns:local="clr-namespace:CompanyName.UI;assembly=CompanyName"
x:Class="CompanyName.UI.Pages.MapWithCircleSlider"
Title="{Binding Title}">
<ContentPage.BindingContext>
<vm:MapWithCircleSliderViewModel></vm:MapWithCircleSliderViewModel>
</ContentPage.BindingContext>
<ContentPage.Content>
<ScrollView>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<local:CircleMap Grid.Row="0" CircleRadius="{Binding CircleRadius}" Latitude="{Binding Latitude}" Longitude="{Binding Longitude}" MapRadius="{Binding MapRadius}" IsShowingUser="true" HasZoomEnabled="true" />
<!--<Image Grid.Row="0" HorizontalOptions="Center" VerticalOptions="Center" Source="ic_place_green_48dp.png" />-->
<StackLayout Grid.Row="1" Padding="32,16">
<Entry VerticalOptions="Start" Placeholder="navn *" Text="{Binding Name}">
<Entry.Style>
<OnPlatform x:TypeArguments="Style">
<On Platform="iOS" Value="{x:Static local:Styling.IosEntryStyle}" />
</OnPlatform>
</Entry.Style>
</Entry>
<Label VerticalOptions="Center" HorizontalOptions="Center" Text="{Binding CircleRadius, StringFormat='{0}m'}" />
<Slider VerticalOptions="End" Maximum="{Binding Maximum}" Minimum="{Binding Minimum}" Value="{Binding CircleRadius}" />
<!-- NB: Maximum must be set before Minimum, ref: https://bugzilla.xamarin.com/show_bug.cgi?id=23665 -->
</StackLayout>
</Grid>
</ScrollView>
</ContentPage.Content>
</ContentPage>
Pages ViewModel:
using System;
using CompanyName.ViewModels;
namespace CompanyName.Data.ViewModels.MapWithCircleSlider
{
public class MapWithCircleSliderViewModel : ViewModelBase
{
private string name;
private int circleRadius;
private float latitude;
private float longitude;
private int mapRadius;
public MapWithCircleSliderViewModel()
{
Name = "Labs";
CircleRadius = 200;
MapRadius = 200;
Latitude = 58.9698634f;
Longitude = 5.7331874f;
}
public int Maximum => 1000;
public int Minimum => 100;
public string Id { get; set; }
public bool IsEditMode { get; set; }
public string Title { get; set; }
public string Name
{
get => name;
set
{
if (name == value) return;
name = value;
OnPropertyChanged("Name");
}
}
public int CircleRadius
{
get => circleRadius;
set
{
if (circleRadius == value) return;
circleRadius = value;
OnPropertyChanged("CircleRadius");
}
}
public float Latitude
{
get => latitude;
set
{
if (Math.Abs(latitude - value) < float.Epsilon) return;
latitude = value;
OnPropertyChanged("Latitude");
}
}
public float Longitude
{
get => longitude;
set
{
if (Math.Abs(longitude - value) < float.Epsilon) return;
longitude = value;
OnPropertyChanged("Longitude");
}
}
public int MapRadius
{
get => mapRadius;
set
{
if (mapRadius == value) return;
mapRadius = value;
OnPropertyChanged("MapRadius");
}
}
}
}
CircleMap.cs
using System.Diagnostics;
using Xamarin.Forms;
using Xamarin.Forms.Maps;
namespace CompanyName.UI
{
public class CircleMap : Map
{
private const int DefaultCircleRadius = 100;
private const float DefaultLatitude = 58.8523208f;
private const float DefaultLongitude = 5.7326743f;
private const int DefaultMapRadius = 150;
public static readonly BindableProperty CircleRadiusProperty = BindableProperty.Create("CircleRadius", typeof(int), typeof(CircleMap), DefaultCircleRadius, BindingMode.TwoWay, propertyChanged: OnCircleRadiusPropertyChanged);
public static readonly BindableProperty LatitudeProperty = BindableProperty.Create("Latitude", typeof(float), typeof(CircleMap), DefaultLatitude, BindingMode.TwoWay, propertyChanged: OnLatitudePropertyChanged);
public static readonly BindableProperty LongitudeProperty = BindableProperty.Create("Longitude", typeof(float), typeof(CircleMap), DefaultLongitude, BindingMode.TwoWay, propertyChanged: OnLongitudePropertyChanged);
public static readonly BindableProperty MapRadiusProperty = BindableProperty.Create("MapRadius", typeof(int), typeof(CircleMap), DefaultMapRadius, BindingMode.TwoWay, propertyChanged: OnMapRadiusPropertyChanged);
public CircleMap() : base(MapSpan.FromCenterAndRadius(new Position(DefaultLatitude, DefaultLongitude), Distance.FromMeters(DefaultMapRadius))) { }
public int CircleRadius
{
get => (int)GetValue(CircleRadiusProperty);
set => SetValue(CircleRadiusProperty, value);
}
public float Latitude
{
get => (float)GetValue(LatitudeProperty);
set => SetValue(LatitudeProperty, value);
}
public float Longitude
{
get => (float)GetValue(LongitudeProperty);
set => SetValue(LongitudeProperty, value);
}
public int MapRadius
{
get => (int)GetValue(MapRadiusProperty);
set => SetValue(MapRadiusProperty, value);
}
private static void OnCircleRadiusPropertyChanged(BindableObject bindable, object oldValue, object newValue)
{
var circleMap = (CircleMap)bindable;
circleMap.CircleRadius = (int)newValue;
}
private static void OnLatitudePropertyChanged(BindableObject bindable, object oldValue, object newValue)
{
var circleMap = (CircleMap)bindable;
circleMap.Latitude = (float)newValue;
MoveToRegion(circleMap);
}
private static void OnLongitudePropertyChanged(BindableObject bindable, object oldValue, object newValue)
{
var circleMap = (CircleMap)bindable;
circleMap.Longitude = (float)newValue;
MoveToRegion(circleMap);
}
private static void OnMapRadiusPropertyChanged(BindableObject bindable, object oldValue, object newValue)
{
var circleMap = (CircleMap)bindable;
circleMap.MapRadius = (int)newValue;
MoveToRegion(circleMap);
}
private static void MoveToRegion(CircleMap circleMap)
{
circleMap.MoveToRegion(MapSpan.FromCenterAndRadius(new Position(circleMap.Latitude, circleMap.Longitude), Distance.FromMeters(circleMap.MapRadius)));
}
}
}
CustomMapRenderer.cs (iOS):
using CompanyName.UI;
using MapKit;
using ObjCRuntime;
using System;
using System.ComponentModel;
using System.Linq;
using Xamarin.Forms;
using Xamarin.Forms.Maps.iOS;
using Xamarin.Forms.Platform.iOS;
using CompanyName.iOS.Renderers.CustomRenderer;
using CompanyName.Utilities;
[assembly: ExportRenderer(typeof(CircleMap), typeof(CustomMapRenderer))]
namespace CompanyName.iOS.Renderers.CustomRenderer
{
/// <remarks>
/// https://developer.xamarin.com/guides/xamarin-forms/application-fundamentals/custom-renderer/map/circle-map-overlay/#Creating_the_Custom_Renderer_on_iOS
/// </remarks>
public class CustomMapRenderer : MapRenderer
{
private CircleMap circleMap;
private MKCircleRenderer circleRenderer;
private MKMapView NativeMap => Control as MKMapView;
protected override void OnElementChanged(ElementChangedEventArgs<View> e)
{
try
{
base.OnElementChanged(e);
if (e.OldElement != null)
{
if (Control is MKMapView nativeMap)
{
nativeMap.RemoveOverlays(nativeMap.Overlays);
nativeMap.OverlayRenderer = null;
circleRenderer = null;
}
}
if (e.NewElement != null)
{
circleMap = (CircleMap)e.NewElement;
NativeMap.OverlayRenderer = GetOverlayRenderer;
AddOverlay();
}
}
catch (Exception ex)
{
//Logger.LogException(ex, GetType().Name);
}
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (sender == null) return;
circleMap = (CircleMap)sender;
if (e.PropertyName == "VisibleRegion") OnVisibleRegionChanged();
if (e.PropertyName == CircleMap.CircleRadiusProperty.PropertyName) RedrawOverlay();
}
private MKOverlayRenderer GetOverlayRenderer(MKMapView mapView, IMKOverlay overlayWrapper)
{
if (circleRenderer == null && !Equals(overlayWrapper, null))
{
var overlay = Runtime.GetNSObject(overlayWrapper.Handle) as IMKOverlay;
circleRenderer = new MKCircleRenderer(overlay as MKCircle)
{
Alpha = 0.15f,
FillColor = CompanyName.Constants.Colors.Skobeloff500.ToUIColor(),
LineWidth = 1,
StrokeColor = CompanyName.Constants.Colors.Skobeloff500.ToUIColor()
};
}
return circleRenderer;
}
private void OnVisibleRegionChanged()
{
SetNewCoordinates();
RedrawOverlay();
}
private void SetNewCoordinates()
{
circleMap.Latitude = (float)circleMap.VisibleRegion.Center.Latitude;
circleMap.Longitude = (float)circleMap.VisibleRegion.Center.Longitude;
circleMap.MapRadius = (int)circleMap.VisibleRegion.Radius.Meters;
}
private void RedrawOverlay()
{
RemoveOverlays();
AddOverlay();
}
private void RemoveOverlays()
{
if (NativeMap?.Overlays == null) return;
if (NativeMap.Overlays.Any()) NativeMap.RemoveOverlays(NativeMap.Overlays);
}
private void AddOverlay()
{
var circleOverlay = MKCircle.Circle(new CoreLocation.CLLocationCoordinate2D(circleMap.Latitude, circleMap.Longitude), circleMap.CircleRadius);
NativeMap.AddOverlay(circleOverlay);
}
}
}
Any feedback / suggestions are highly appreciated!
You can try to refresh the circleRenderer to achieve your effect like:
private void RemoveOverlays()
{
if (NativeMap?.Overlays == null) return;
if (NativeMap.Overlays.Any())
{
NativeMap.RemoveOverlays(NativeMap.Overlays);
circleRenderer = null;
NativeMap.OverlayRenderer = GetOverlayRenderer;
}
}
I am using a spinner and an arrayAdapter to populate items in spinner. I want to make the spinner multi-selectable but I just select one row.I searched in Google, solution available in Java, but i don't have any idea on how to implement it in Xamarin.
My code is as below,
adapterList= new ArrayAdapter<string>(this, Android.Resource.Layout.SimpleListItemMultipleChoice);
spnMultiTest.Adapter = adapterList;
I want to make the spinner multi-selectable but I just select one row.I searched in Google, solution available in Java, but i don't have any idea on how to implement it in Xamarin.
Basically I just translate the codes from Android Spinner with multiple choice
to Xamarin codes. I have tested it and it works fine:
MultiSpinner.cs:
public interface MultiSpinnerListener
{
void onItemsSelected(bool[] selected);
}
public class MultiSpinner : Spinner, IDialogInterfaceOnMultiChoiceClickListener, IDialogInterfaceOnCancelListener
{
Context _context;
private List<String> items;
private bool[] selected;
private String defaultText;
private MultiSpinnerListener listener;
public MultiSpinner(Context context) : base(context)
{
_context = context;
}
public MultiSpinner(Context context, IAttributeSet arg1) : base(context, arg1)
{
_context = context;
}
public MultiSpinner(Context context, IAttributeSet arg1, int arg2) : base(context, arg1, arg2)
{
_context = context;
}
public void OnClick(IDialogInterface dialog, int which, bool isChecked)
{
if (isChecked)
selected[which] = true;
else
selected[which] = false;
}
public override void OnClick(IDialogInterface dialog, int which)
{
dialog.Cancel();
}
public override bool PerformClick()
{
AlertDialog.Builder builder = new AlertDialog.Builder(_context);
builder.SetMultiChoiceItems(
items.ToArray(), selected, this);
builder.SetPositiveButton("OK",this);
builder.SetOnCancelListener(this);
builder.Show();
return true;
}
public void SetItems(List<String> items, String allText,
MultiSpinnerListener listener)
{
this.items = items;
this.defaultText = allText;
this.listener = listener;
// all selected by default
selected = new bool[items.Count];
for (int i = 0; i < selected.Length; i++)
selected[i] = true;
ArrayAdapter<string> adapter = new ArrayAdapter<string>(_context,Resource.Layout.simple_spinner_item,Resource.Id.tv_item,new string[] { allText });
// all text on the spinner
//ArrayAdapter<String> adapter = new ArrayAdapter<String>(_context,Resource.Layout.simple_spinner_item, new String[] { allText });
Adapter = adapter;
}
public void OnCancel(IDialogInterface dialog)
{
Java.Lang.StringBuffer spinnerBuffer = new Java.Lang.StringBuffer();
bool someUnselected = false;
for (int i = 0; i < items.Count; i++)
{
if (selected[i] == true)
{
spinnerBuffer.Append(items[i]);
spinnerBuffer.Append(", ");
}
else
{
someUnselected = true;
}
}
String spinnerText;
if (someUnselected)
{
spinnerText = spinnerBuffer.ToString();
if (spinnerText.Length > 2)
spinnerText = spinnerText.Substring(0, spinnerText.Length - 2);
}
else
{
spinnerText = defaultText;
}
ArrayAdapter<String> adapter = new ArrayAdapter<String>(_context,Resource.Layout.simple_spinner_item,Resource.Id.tv_item,new string[] { spinnerText });
Adapter = adapter;
if (listener != null)
{
listener.onItemsSelected(selected);
}
}
}
simple_spinner_item.xml:
<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:id="#+id/tv_item"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
</LinearLayout>
Update:
Here is the codes for using this MultiSpinner:
Main.axml:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<MultiSpinner.MultiSpinner
android:id="#+id/mSpinner"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
</LinearLayout>
MainActivity.cs:
public class MainActivity : Activity
{
MultiSpinner mSpinner;
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
// Set our view from the "main" layout resource
SetContentView(Resource.Layout.Main);
mSpinner = FindViewById<MultiSpinner>(Resource.Id.mSpinner);
List<string> items = new List<string> {
"Android",
"iOS",
"UWP"
};
mSpinner.SetItems(items, "AllText", null);
}
}
I am using custom TabbedPage in my project for showing a badges(count) in Tab bar. I am using the below code for showing a custom tab bar but it is always returning null value in CustomTabbedPageRenderer.cs class OnWindowVisibilityChanged method's activity.ActionBar. I have tried the many workaround like changing the Theme as Theme.AppCompat.Light.DarkActionBar and added the below line in Window.RequestFeature(WindowFeatures.ActionBar); MainActivity.cs , but unfortunately these didn't help me.
MainActivity.cs
namespace Bakery.Droid
{
[Activity(Label = "Bakery.Droid", Icon = "#mipmap/icon_launcher", Theme = "#style/MyTheme", MainLauncher = false, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation, ScreenOrientation = ScreenOrientation.Portrait)]
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
{
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
global::Xamarin.Forms.Forms.Init(this, bundle);
LoadApplication(new App());
}
}
}
Home.cs
using System;
using System.Collections.Generic;
using Xamarin.Forms;
namespace Bakery
{
public class Home : CustomTabPage
{
public Home()
{
NavigationPage.SetHasNavigationBar(this, false);
var burger = new NavigationPage(new Burger());
burger.Icon = "burger.png";
burger.Title = "Burger";
var sandwich = new NavigationPage(new Sandwich());
sandwich.Icon = "sandwich.png";
sandwich.Title = "Sandwich";
var pizza = new NavigationPage(new Pizza());
pizza.Icon = "pizza.png";
pizza.Title = "Pizza";
var roll = new NavigationPage(new Roll());
roll.Icon = "roll.png";
roll.Title = "Roll";
//adding childrens into the tab
Children.Clear();
Children.Add(burger);
Children.Add(sandwich);
Children.Add(pizza);
Children.Add(roll);
}
protected override void OnAppearing()
{
base.OnAppearing();
}
}
}
CustomTabPage.cs
using System;
using Xamarin.Forms;
namespace Bakery
{
public class CustomTabPage : TabbedPage
{
}
}
styles.xml
<?xml version="1.0" encoding="UTF-8"?>
<resources>
<style name="MyTheme" parent="MyTheme.Base">
</style>
<style name="MyTheme.Base" parent="Theme.AppCompat.Light.DarkActionBar">
<item name="windowNoTitle">true</item>
<item name="windowActionBar">true</item>
<item name="windowActionModeOverlay">true</item>
</style>
</resources>
CustomTabbedPageRenderer.cs
[assembly: ExportRenderer(typeof(TabbedPage), typeof(CustomTabbedPageRenderer))]
namespace Bakery.Droid
{
public class CustomTabbedPageRenderer : TabbedRenderer
{
Activity activity;
List<string> filenames;
protected override void OnElementChanged(ElementChangedEventArgs<TabbedPage> e)
{
base.OnElementChanged(e);
filenames = e.NewElement.Children.Select(t => t.Icon.File).ToList();
}
protected override void OnElementPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
activity = this.Context as Activity;
}
protected override void OnWindowVisibilityChanged(Android.Views.ViewStates visibility)
{
try
{
base.OnWindowVisibilityChanged(visibility);
var actionBar = activity.ActionBar;
var colorDrawable = new ColorDrawable(Android.Graphics.Color.Yellow);
actionBar.SetStackedBackgroundDrawable(colorDrawable);
System.Diagnostics.Debug.WriteLine("Onwindow visible");
ActionBarTabsSetup(actionBar);
}
catch (Exception Exception)
{
System.Diagnostics.Debug.WriteLine("Exception: " + Exception.ToString());
}
}
void ActionBarTabsSetup(ActionBar actionBar)
{
for (var i = 0; i < actionBar.NavigationItemCount; ++i)
{
var tab = actionBar.GetTabAt(i);
var id = GetImageFromFilename(i);
if (id != 0)
TabSetup(tab, id);
}
}
void TabSetup(ActionBar.Tab tab, int resID)
{
var relLay = new Android.Widget.RelativeLayout(activity)
{
LayoutParameters = new LayoutParams(LayoutParams.WrapContent, 180)
};
var linLay = new LinearLayout(activity)
{
LayoutParameters = new LayoutParams(LayoutParams.WrapContent, 180),
Orientation = Orientation.Vertical,
};
linLay.SetHorizontalGravity(Android.Views.GravityFlags.Center);
var imageView = new ImageView(activity);
imageView.SetImageResource(resID);
imageView.SetPadding(-35, 4, -35, 0);
imageView.SetMinimumWidth(60);
var textView = new TextView(activity)
{
Text = tab.Text
};
linLay.AddView(imageView);
linLay.AddView(textView);
relLay.AddView(linLay);
var badgeView = new TextView(activity)
{
Text = "2"
};
var badgeImageView = new ImageView(activity);
badgeImageView.SetImageResource(Resource.Drawable.red);
badgeImageView.SetMinimumWidth(5);
badgeImageView.SetMinimumHeight(5);
badgeImageView.SetPadding(77, 5, 0, 0);
badgeView.SetPadding(85, 0, 0, 0);
relLay.AddView(badgeImageView);
relLay.AddView(badgeView);
tab.SetCustomView(relLay);
}
int GetImageFromFilename(int n)
{
var filename = filenames[n].Split('.');
var id = Resources.GetIdentifier(filename[0], "drawable", activity.PackageName);
return id;
}
}
}
I am using the below code for showing a custom tab bar but it is always returning null value in CustomTabbedPageRenderer.cs class OnWindowVisibilityChanged method's activity.ActionBar.
The ActionBar is null because it is never set in the activity. You need to set the actionbar first:
In MainActivity.cs Set the SupportActionBar like below:
//Create your own theme "MyTheme"
[Activity(Label = "CustomTabbedPageDemo", Icon = "#drawable/icon", Theme = "#style/MyTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
{
protected override void OnCreate(Bundle bundle)
{
//Get the ActionBar Layout in Resouce\layout\Toolbar.axml
var toolbar=(Toolbar)LayoutInflater.Inflate(Resource.Layout.Toolbar, null);
//Set the Support ActionBar
SetSupportActionBar(toolbar);
SupportActionBar.Title = "My ActionBar";
base.OnCreate(bundle);
global::Xamarin.Forms.Forms.Init(this, bundle);
LoadApplication(new App());
}
}
Create your own theme to replace the default one. Xamarin.Forms requires theme inherited from Theme.AppCompat.XXX.XXX:
<style name="MyTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<item name="windowNoTitle">true</item>
<item name="windowActionBar">false</item>
<item name="colorPrimary">#5A8622</item>
</style>
Basic Toolbar.axml example:
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.Toolbar
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/colorPrimary"
android:theme="#style/ThemeOverlay.AppCompat.Dark.ActionBar"/>
Modify your CustomTabPageRenderer.cs change the namespace of ActionBar from Android.App to using Android.Support.V7.App and retrieve the ActionBar through activity.SupportActionBar:
using Android.Support.V7.App;
...
protected override void OnWindowVisibilityChanged(Android.Views.ViewStates visibility)
{
try
{
base.OnWindowVisibilityChanged(visibility);
//get the support actionbar
var actionBar = activity.SupportActionBar;
var colorDrawable = new ColorDrawable(Android.Graphics.Color.Yellow);
actionBar.SetStackedBackgroundDrawable(colorDrawable);
System.Diagnostics.Debug.WriteLine("Onwindow visible");
ActionBarTabsSetup(actionBar);
}
catch (Exception Exception)
{
System.Diagnostics.Debug.WriteLine("Exception: " + Exception.ToString());
}
}
I am developing a Windows Universal App which is hosting a web app using webview.
steps are followed as like.
Creating a Blank universal window app. Creating a Splash screen. Set
splash screen as starting page. After all activity i would like to
navigate the Main page which is having a web view control.
Setting a url example "http:www.google.come" as source for the web view. everything it works a fine but the main page takes time, where i would like to see the same splash screen till it loads.
Code for Navigation i am using
this.Frame.Navigate(typeof(MainPage));
full source code
public sealed partial class ExtentedSpash : Page
{
public ProgressMessage Progress;
public ExtentedSpash()
{
this.InitializeComponent();
Progress = ProgressMessage.GetMessage();
DataContext = Progress;
Window.Current.Activate();
Loaded += Splash_Loaded;
}
private async void Splash_Loaded(object sender, RoutedEventArgs e)
{
await Initialize();
Window.Current.Activate();
await ClearBrowserCache();
Window.Current.Activate();
//Task.WaitAll(TaskList.ToArray());
await StartApplication();
}
public async Task Initialize()
{
Progress.ActionMessage = "Initialize the controls";
await Task.Delay(TimeSpan.FromSeconds(10));
}
public async Task ClearBrowserCache()
{
Progress.ActionMessage = "Clear Browser Cache";
await Task.Delay(TimeSpan.FromSeconds(10));
}
public async Task StartApplication()
{
Progress.ActionMessage = "Loading";
await Task.Delay(TimeSpan.FromSeconds(10));
this.Frame.Navigate(typeof(MainPage));
}
private void btnMain_Click(object sender, RoutedEventArgs e)
{
}
}
public class ProgressMessage : INotifyPropertyChanged
{
private string statusMessage;
public string StatusMessage
{
get { return statusMessage; }
set
{
statusMessage = value;
RaiseProperChanged();
}
}
private string actionMessage;
public string ActionMessage
{
get { return actionMessage; }
set
{
actionMessage = value;
RaiseProperChanged();
}
}
private bool showProgress;
public bool ShowProgress
{
get { return showProgress; }
set { showProgress = value;
RaiseProperChanged();
}
}
public static ProgressMessage GetMessage()
{
var msg = new ProgressMessage()
{
StatusMessage = "Initializing Application",
ActionMessage = "One moment please...",
showProgress = true
};
return msg;
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaiseProperChanged(
[CallerMemberName] string caller = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(caller));
}
}
}
}
I want "On Loading" message should show til it fully loads the application.
As we've discussed, if you just want to cover the WebView when it's source is not complete loaded, you can do it like this:
<Page.Resources>
<Storyboard x:Key="MyTextSTD" x:Name="MyTextSTD" RepeatBehavior="Forever">
<ColorAnimationUsingKeyFrames Storyboard.TargetName="tbbrush" Storyboard.TargetProperty="Color" Duration="0:0:10">
<DiscreteColorKeyFrame KeyTime="0:0:0" Value="Red" />
<LinearColorKeyFrame KeyTime="0:0:5" Value="Blue" />
<LinearColorKeyFrame KeyTime="0:0:10" Value="Purple" />
</ColorAnimationUsingKeyFrames>
</Storyboard>
</Page.Resources>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<WebView Source="https://msdn.microsoft.com/library/windows/apps/xaml/mt244352.aspx" NavigationCompleted="WebView_NavigationCompleted">
</WebView>
<Grid x:Name="loadingGrid" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Visibility="Visible">
<TextBlock Text="On Loading..." FontSize="50" VerticalAlignment="Center" HorizontalAlignment="Center">
<TextBlock.Foreground>
<SolidColorBrush x:Name="tbbrush" />
</TextBlock.Foreground>
</TextBlock>
</Grid>
</Grid>
And code behind:
public MainPage()
{
this.InitializeComponent();
MyTextSTD.Begin();
}
private void WebView_NavigationCompleted(WebView sender, WebViewNavigationCompletedEventArgs args)
{
loadingGrid.Visibility = Visibility.Collapsed;
}
Here is quite simple, I use a TextBlock and some color animation to show the message. And this message will disappear when the source of WebView is fully loaded.