I am working on a xamarin.forms application where I need to measure the height of the navigation bar. How do I measure it? I tried to follow this but I don't understand where to insert this code:
Resources resources = context.getResources();
int resourceId = resources.getIdentifier("navigation_bar_height", "dimen", "android");
if (resourceId > 0) {
return resources.getDimensionPixelSize(resourceId);
}
return 0;
You are on the right track. The problem is that your app is cross platform and the navigation bar is a Android-only feature. Because you probably need to access the navigation bar height from within the shared code, you will need to use Dependency injection. This is thoroughly described here.
In short you will have to write a namespace like this:
public interface INavigationBarInfo
{
int Height { get; };
}
And implement it for Android within the Android project:
public class NavigationBarInfo : INavigationBarInfo
{
public int Height => RetrieveHeight();
private void RetrieveHeight()
{
//android specific code to retrieve the navigation bar height
//you can use the answer you linked to in your question
}
}
Related
I need to get DPI device in my Xamarin class PCL. I do not want to use Xamarin.Essentials. Can I do this using Native interfaces, if its possible, how can I do it?
in your pcl create a new interface called IDisplayInfo:
public interface IDisplayInfo
{
int GetDisplayWidth();
int GetDisplayHeight();
int GetDisplayDpi();
}
In your android implementation, add a new class:
[assembly: Dependency(typeof(DisplayInfo))]
namespace YourAppNamespace.Droid
{
public class DisplayInfo : IDisplayInfo
{
public int GetDisplayWidth()
{
return (int)Android.App.Application.Context.Resources.DisplayMetrics.WidthPixels;
}
public int GetDisplayHeight()
{
return (int)Android.App.Application.Context.Resources.DisplayMetrics.HeightPixels;
}
public int GetDisplayDpi()
{
return (int)Android.App.Application.Context.Resources.DisplayMetrics.DensityDpi;
}
}
}
and in the iOS implementation, add the same class:
[assembly: Dependency(typeof(DisplayInfo))]
namespace YourNamespace.iOS
{
public class DisplayInfo : IDisplayInfo
{
public int GetDisplayWidth()
{
return (int)UIScreen.MainScreen.Bounds.Width;
}
public int GetDisplayHeight()
{
return (int)UIScreen.MainScreen.Bounds.Height;
}
public int GetDisplayDpi()
{
return (int)(int)UIScreen.MainScreen.Scale;
}
}
}
Now in your shared code, you can call
int dpi = DependencyService.Get<IDisplayInfo>().GetDisplayDpi();
and should be good to go. Note that i also added methods for getting screen width and height, basically because i already had them in my code and since you are probably going to need them sooner or later anyways.
Currently Device Display Information available via official Xamarin.Essentials nuget package, see:
https://learn.microsoft.com/en-us/xamarin/essentials/device-display
// Get Metrics
var mainDisplayInfo = DeviceDisplay.MainDisplayInfo;
// Orientation (Landscape, Portrait, Square, Unknown)
var orientation = mainDisplayInfo.Orientation;
// Rotation (0, 90, 180, 270)
var rotation = mainDisplayInfo.Rotation;
// Width (in pixels)
var width = mainDisplayInfo.Width;
// Height (in pixels)
var height = mainDisplayInfo.Height;
// Screen density
var density = mainDisplayInfo.Density;
I think it will help you. Here you have it described
enter link description here
I have a static class Core to store some shared stuff defined the shared code.
On app start it's receiving values for later use everywhere:
Android MainActivity OnCreate:
Core.IsAndroid = true;
Core.DisplayDensity = Resources.DisplayMetrics.Density;
iOS AppDelegate FinishedLaunching:
Core.IsIOS = true;
Core.DisplayDensity = (float)(UIScreen.MainScreen.NativeBounds.Width / UIScreen.MainScreen.Bounds.Width);
This is my code
ToolbarItems.Add(new ToolbarItem("User", "userAvatar.png", async () => {
await Navigation.PopToRootAsync();
}));
It's not working. It's place a masked single color image instead a png in colors.
I'm trying to archive something like this...
Any clue ?
I was going mad about this issue once, too (my situation was a bit more subtle, I had a plain and a colored verion of the icon and was wondering why the heck the colored icon would not be loaded) and unfortunately it's not that easy to overcome.
The icons being monochrome is the default behavior for iOS apps and Xamarin.Forms implements this behavior. According to this post you'll need a custom renderer to show colored icons in the navigation bar.
Edit
According to this post, you'll have to set the UIImageRenderingMode for the respective images in your custom renderer
image = image.ImageWithRenderingMode(UIImageRenderingMode.AlwaysOriginal);
using the renderer implementation from this answer, it should be something in the line of
using UIKit;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
[assembly: ExportRenderer(typeof(TabbedPage), typeof(MyProject.iOS.Renderers.IosMainMenuRenderer))]
namespace MyProject.iOS.Renderers {
public class IosMainMenuRenderer : TabbedRenderer {
public override void ViewWillAppear(bool animated)
{
base.ViewWillAppear(animated);
var items = TabBar.Items;
for (var i = 0; i < items.Length; i++) {
items[i].Image = items[i].ImageWithRenderingMode(UIImageRenderingMode.AlwaysOriginal);
items[i].SelectedImage = items[i].SelectedImage.ImageWithRenderingMode(UIImageRenderingMode.AlwaysOriginal);
}
}
}
}
but I have not tested this!
For a color logo on the right on all my navigation pages I used this custom renderer:
[assembly: ExportRenderer(typeof(CustomNavigationPage), typeof(CustomNavigationRenderer))]
namespace App.iOS
{
public class CustomNavigationRenderer : NavigationRenderer
{
public override void ViewDidLayoutSubviews()
{
base.ViewDidLayoutSubviews();
var logo = NavigationBar.TopItem.RightBarButtonItem.Image;
if (logo == null) return;
if (logo.RenderingMode == UIImageRenderingMode.AlwaysOriginal)
{
return;
}
NavigationBar.TopItem.RightBarButtonItem.Image = logo.ImageWithRenderingMode(UIImageRenderingMode.AlwaysOriginal);
}
}
}
The easiest way is to not use a toolbar, instead, set the type of this page as a Modal when navigating to it(Using Navigation.PushModal()). And add a horizontal LinearLayout that will act as the toolbar.
I'd like to animate my Score-GUI text counting up to a variable value but there are two things in my way:
1: How can I animate to a variable instead of a fixed value?
2: Why can't I add own properties (like int) to my script and animate them?
For #2 I created a property in my script. Yet the editor won't show it in the AddProperty-dialog (as shown below):
public int currentScore = 0;
public int score {
get { return currentScore; }
set { this.currentScore += value; }
}
EDIT: The animator is set up in the most basic way:
Since you only have 1 Animation. An Animator is irrelevant to the solution. This is tested and working. Now you need to make the Animation a Legacy type to get this working because we are not going to use the Animator.
Click the Animation on the Project -> look at the upper right section of the Inspector view, there is a little button there which will drop down a selection. "Debug" then Check the Legacy.
Set your Animation to whatever you want. I force the WrapMode in the script to be wrap mode once. So it will only play once.
Now in the Animation Component make sure you select the Animation that you want by default or it wont work. Cause we only use anim.Play(); Without parameters meaning, run the default animation that is set.
I created a Text UI and added an Animation that alpha is 0 from the start and at the end point making it 1. You have to do that on your own.
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
public class MyScore : MonoBehaviour {
// Use this for initialization
public int currentScore = 0;
public GameObject Myscore; // Drag the GameObject that has the Animation for your score.
public Text myScoreText; //Drag in the Inspector the Text object to reference
public Animation anim;
public int score
{
get { return currentScore; }
set { this.currentScore += value; }
}
void Start()
{
anim = Myscore.GetComponent<Animation>(); // Reference the Animation Component.
anim.wrapMode = WrapMode.Once; // Legacy animation Set to play once
AddScore();
}
public void AddScore()
{
score += 10;
myScoreText.text = score.ToString();
anim.Play();
Debug.Log("Current Score is "+ score);
Invoke("AddScore", 2);
}
}
Good luck.
I'm trying to implement a vertical StackPanel equivalent in my MonoMac project and am having trouble figuring it out. I am not using interface builder but creating the controls programmatically (this is a restriction). I tried using a CollectionView but all items in that control are sizing to the same height.
Looking around the internet, it seems NSTableView is the way to go, but I'm not sure how to do it, especially with C# while using a MonoMac target. CollectionView was somewhat more straightforward with the CollectionViewItem.ItemPrototype allowing me to create the views I want to render. But with an NSTableView, it seems like I can only specify a data source that returns the NSObjects I want to display. How do I grab this data and then bind them to the view I want to stick in there?
I would prefer C# code but I'm at a stage where I'll accept any and all help!
I was finally able to get it working. Here is some code for anyone who wants to try it out. Basically, we need to write NSTableViewDelegates for the required functions. This implementation also doesn't cache the controls or anything. The Cocoa API documentation mentioned using an identifier to reuse the control, or something, but the identifier field is get-only in MonoMac.
I also ended up implementing my NSTableViewDelegate functions in my data-source itself which I am sure is not kosher at all, but I'm not sure what the best practice is.
Here's the data source class:
class MyTableViewDataSource : NSTableViewDataSource
{
private NSObject[] _data;
// I'm coming from an NSCollectionView, so my data is already in this format
public MyTableViewDataSource(NSObject[] data)
{
_data = data;
}
public override int GetRowCount(NSTableView tableView)
{
return _data.Length;
}
#region NSTableViewDelegate Methods
public NSView GetViewForItem(NSTableView tableView, NSTableColumn tableColumn, int row)
{
// MyViewClass extends NSView
MyViewClass result = tableView.MakeView("MyView", this) as MyViewClass;
if (result == null)
{
result = new MyViewClass(_data[row]);
result.Frame = new RectangleF(0, 0, tableView.Frame.Width, 100); // height doesn't matter since that is managed by GetRowHeight
result.NeedsDisplay = true;
// result.Identifier = "MyView"; // this line doesn't work because Identifier only has a getter
}
return result;
}
public float GetRowHeight(NSTableView tableView, int row)
{
float height = FigureOutHeightFromData(_data[row]); // run whatever algorithm you need to get the row's height
return height;
}
#endregion
}
And here's the snippet that programmatically creates the table:
var tableView = new NSTableView();
var dataSource = new MyTableViewDataSource();
tableView.DataSource = dataSource;
tableView.HeaderView = null; // get rid of header row
tableView.GetViewForItem = dataSource.GetViewForItem;
tableView.GetRowHeight = dataSource.GetRowHeight;
AddSubView(tableView);
So, it is not a perfect StackPanel because one needs to manually calculate row heights, but it's better than nothing.
Trying to create a custom cyclical horizontal manager which will work as follows. It will control several field buttons where the buttons will always be positioned so that the focused button will be in the middle of the screen. As it is a cyclical manager once the focus moves to the right or left button, it will move to the center of the screen and all the buttons will move accordingly (and the last button will become the first to give it an cyclic and endless list feeling)
Any idea how to address this?
I tried doing this by implementing a custom manager which aligns the buttons according to the required layout. Each time moveFocus() is called I remove all fields (deleteAll() ) and add them again in the right order.
Unfortunately this does not work.
Using HorizontalButtonFieldSet class from KB How to - Implement advanced buttons, fields, and managers:
class CentricHManager extends HorizontalButtonFieldSet {
int focusedFieldIndex = 0;
public void focusChangeNotify(int arg0) {
super.focusChangeNotify(arg0);
int focusedFieldIndexNew = getFieldWithFocusIndex();
if (focusedFieldIndexNew != focusedFieldIndex) {
if (focusedFieldIndexNew - focusedFieldIndex > 0)
switchField(0, getFieldCount() - 1);
else
switchField(getFieldCount() - 1, 0);
}
}
private void switchField(int prevIndex, int newIndex) {
Field field = getField(prevIndex);
delete(field);
insert(field, newIndex);
}
public void add(Field field) {
super.add(field);
focusedFieldIndex = getFieldCount() / 2;
setFieldWithFocus(getField(focusedFieldIndex));
}
}