I have a main View Page, with a frame & image (inside drawable folder). on Back-end i am doing simple animation for 3 seconds
My Question:
How am I doing too much work by simple animation and image is 500x500. How to fix this error?
By the way, it works fine, if i remove animation code Task.WhenAll
Thread started: #5
[Choreographer] Skipped 1191 frames! The application may be doing too much work on its main thread.
[ViewRootImpl#5028d58[MainActivity]] dispatchAttachedToWindow
[me.idcardwalle] Explicit concurrent copying GC freed 1534(155KB) AllocSpace objects, 0(0B) LOS objects, 69% free, 2MB/8MB, paused 53us total 16.620ms
[Mono] GC_TAR_BRIDGE bridges 168 objects 168 opaque 0 colors 168 colors-bridged 168 colors-visible 168 xref 0 cache-hit 0 cache-semihit 0 cache-miss 0 setup 0.05ms tarjan 0.04ms scc-setup 0.05ms gather-xref 0.00ms xref-setup 0.00ms cleanup 0.03ms
[Mono] GC_BRIDGE: Complete, was running for 17.71ms
[Mono] GC_MINOR: (Nursery full) time 3.13ms, stw 4.30ms promoted 158K major size: 3984K in use: 3218K los size: 4096K in use: 3634K
...
[Choreographer] Skipped 40 frames! The application may be doing too much work on its main thread.
[ViewRootImpl#5028d58[MainActivity]] MSG_RESIZED: frame=Rect(0, 0 - 1080, 2220) ci=Rect(0, 63 - 0, 126) vi=Rect(0, 63 - 0, 126) or=1
[InputMethodManager] prepareNavigationBarInfo() DecorView#a5c60a8[MainActivity]
[InputMethodManager] getNavigationBarColor() -855310
Animation code
protected override async void OnAppearing()
{
base.OnAppearing();
ssImage.Opacity = 50;
await Task.WhenAll(
ssImage.FadeTo(1, 3000),
ssImage.ScaleTo(1.2, 3000)
);
var route = $"{ nameof(NewPage)}";
await Shell.Current.GoToAsync(route);
}
UI Code
<Frame VerticalOptions="FillAndExpand"
HorizontalOptions="FillAndExpand"
Padding="0" Margin="0">
<Frame.Background>
<LinearGradientBrush>
<GradientStop Color="#0087c8" Offset="0.1" />
<GradientStop Color="#005783" Offset="1.0" />
</LinearGradientBrush>
</Frame.Background>
<Image x:Name="ssImage" Source="ssImage.png"
VerticalOptions="CenterAndExpand"
HorizontalOptions="CenterAndExpand"
HeightRequest="300"
WidthRequest="300" />
</Frame>
Update: I also tried asysn task but same error
await Task.Run(async () => {
await ssImage.FadeTo(1, 3000);
await ssImage.ScaleTo(1.2, 3000);
});
OnAppearing happens just before the page is diplayed.
To get smooth animation, you want the page to finish displaying before the animation starts. This requires the following two code changes:
DO NOT WAIT for the animation to finish - let OnAppearing return.
Delay the start of the animation.
Code:
using Xamarin.Essentials;
...
// Start a background thread, so can delay without pausing `OnAppearing`.
// DO NOT have `await`/`async` here.
Task.Run(() => {
// Give UI thread some time to layout/begin-displaying the page.
// (Without this, a complex page might still be in layout,
// so the animation might start, then hiccup, like the original code.)
Task.Delay(200);
// If UI thread is in middle of displaying the page,
// that is okay; this will run once UI thread is available.
MainThread.BeginInvokeOnMainThread(async () =>
{
// Now we are back on the main thread.
await ssImage.FadeTo(1, 3000);
await ssImage.ScaleTo(1.2, 3000);
});
});
Related
I am working on a Xamarin.forms project which has a ScrollView with a child of a StackLayout, whose child is a Label.
<ScrollView Grid.Row="0" Grid.Column="0"
x:Name="MessageScroll" BackgroundColor="Gainsboro">
<StackLayout BackgroundColor="Beige" Margin="2"
x:Name="MessageStack" Spacing="0">
<Label Text="Messages"/>
</StackLayout>
</ScrollView>
I want this to be a message window.
I will add onto this StackLayout new labels with messages.
I made a label with a new message(e.Message), added it onto Stacklayout, and then, scroll down the ScrollView show the last added label at the bottom of the ScollView.
Label newLabel = new Label();
newLabel.Text = e.Message;
MessageStack.Children.Add(newLabel);
await MessageScroll.ScrollToAsync(newLabel, ScrollToPosition.End, false);
the problem is ScrollToAsync doesn't ensure the last added label to be at the bottom when I added multiple message labels consecutively in a short period of time.
It seems because each call of ScrollToAsync is executed asynchronously so the last call for it wouldn't complete last.
How can I make this to work as what I want?
If you await the ScrollToAsync it will wait for scroll action to be completed. Anyway, here is a solution to scroll to bottom, you can give it a try.
async Task AddLabel(MessageEventArgs e)
{
Label newLabel = new Label();
newLabel.Text = e.Message;
MessageStack.Children.Add(newLabel);
await MessageScroll.ScrollToAsync(0, MessageStack.Height, false);
}
I have a StackLayout and a number of elements inside (buttons, texts etc).
I want the ActivityIndicator to overlay the entire screen and make it not able to do anything to those elements.
I have put ActivityIndicator inside the StackLayout but wrapped it with AbsoluteLayout thinking that AbsoluteLayout can easitly overlap everything:
<StackLayout>
<AbsoluteLayout>
<ActivityIndicator ... />
</AbsoluteLayout>
<...other elements...>
</StackLayout>
Instead activity indicator is displayed at the top of the StackLayout and other elements are available for affecting. I'm new in Xamarin and layouts, what am I doing wrong? All samples in the Internet have single ActivityIndicator per page...
It is better said that an AbsoluteLayout's children can easily overlap each other. Just as a StackLayout lets you stack controls inside , vertically or horizontally, an AbsoluteLayout lets you position controls inside using absolute or proportional values, thus if two controls have the same absolute positioning set, they will overlap 100%.
Therefore, you want to wrap your StackLayout and another StackLayout that has your ActivityIndicator inside an AbsoluteLayout using proportional sizing, e.g:
<AbsoluteLayout>
<StackLayout
x:Name="mainLayout"
AbsoluteLayout.LayoutBounds="0,0,1,1"
AbsoluteLayout.LayoutFlags="All" >
<Label Text="Welcome to Xamarin.Forms!"
HorizontalOptions="Center"
VerticalOptions="CenterAndExpand" />
<Button Text="Do Something"
Clicked="DoSomethingBtn_Clicked" />
</StackLayout>
<StackLayout
x:Name="aiLayout"
IsVisible="False"
AbsoluteLayout.LayoutBounds="0,0,1,1"
AbsoluteLayout.LayoutFlags="All"
BackgroundColor="Gray" Opacity="0.5">
<ActivityIndicator
x:Name="ai"
IsRunning="False"
HorizontalOptions="CenterAndExpand"
VerticalOptions="CenterAndExpand"
Color="Black"/>
</StackLayout>
</AbsoluteLayout>
The above sets the two StackLayouts to both take up the full size of the parent container of the AbsoluteLayout, which is presumably a Page. The StackLayout that has the indicator is initially hidden. IN the page code behind for the above example, I show the second StackLayout and start the activity indicator and show it for 2 seconds, and then hide it again:
private async void DoSomethingBtn_Clicked(object sender, EventArgs e)
{
ai.IsRunning = true;
aiLayout.IsVisible = true;
await Task.Delay(2000);
aiLayout.IsVisible = false;
ai.IsRunning = false;
}
Here is what it looks like:
And since the second StackLayout completely covers the first, none of the controls in the first StackLayout are clickable.
Might be worth going over the docs for the AbsoluteLayout to understand the AbsoluteLayout.LayoutBounds and AbsoluteLayout.LayoutFlags:
https://learn.microsoft.com/en-us/xamarin/xamarin-forms/user-interface/layouts/absolute-layout
If you want to "overlap", you need to be outside of the StackLayout. A Grid is the most common control for this:
<Grid>
<StackLayout>
<...other elements...>
</StackLayout>
<ActivityIndicator ... />
</Grid>
Here's a hacked-up control for making things full-screen via the horribly-named RelativeLayout (tested in Android only)
[ContentProperty("ContentInner")]
public class FullScreenLayout : ContentView
{
public View ContentInner
{
get => ((RelativeLayout) Content).Children[0];
set
{
var display = DeviceDisplay.MainDisplayInfo;
var screenWidth = display.Width / display.Density;
var screenHeight = display.Height / display.Density;
var wrapper = new RelativeLayout();
wrapper.Children.Add(value, () => new Rectangle(0, 0, screenWidth, screenHeight));
Content = wrapper;
}
}
}
It can be used like this:
<controls:FullScreenLayout>
<!-- Anything you want fullscreen here -->
</controls:FullScreenLayout>
Unfortunately, if you use NavigationPage, this won't overlap the navigation bar. Every other solution currently on this page has the same issue. According to this question, it's not possible to solve this without using platform-specific customer renderers. Ugh.
If you don't mind the page being dimmed, you can use Rg.Plugins.Popup which implements the custom renderers needed.
I ended up solving my similar problem (dimming most of the screen) by implementing a custom renderer for the navigation page itself.
I have got a Map page which has got background Image, source of image is set to random image api
<Image x:Name="backgroundImage" Source="https://someImageApi" AbsoluteLayout.LayoutBounds="0,0,1,1" AbsoluteLayout.LayoutFlags="All" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" Aspect="AspectFill"/>
I would like to refresh the image without any button click in Xamarin Forms. I have tried something below, but this is not working.
public Map()
{
InitializeComponent();
MessagingCenter.Send(this, "RefreshSchedulePage");
MessagingCenter.Subscribe<Map>(this, "RefreshSchedulePage", async (sender) =>
{
Navigation.InsertPageBefore(new Map(), this);
await Navigation.PopAsync();
});
}
You should Subscribe the notification before you Send it
InitializeComponent();
MessagingCenter.Subscribe<Map>(this, "RefreshSchedulePage", async (sender) =>
{
Navigation.InsertPageBefore(new Map(), this);
await Navigation.PopAsync();
});
And send the message in OnAppearing.
Why don't you use OnPropertyChanged? (And maybe Device.StartTimer? - In case you need a recurring pattern.) OnPropertyChanged should draw in all bindings again, meaning it should reach out to the api for the random image again.
InitializeComponent();
int interval = 1000;
Device.StartTimer(interval, () => {
backgroundImage.OnPropertyChanged("Source");
}
If you don't need an interval, simply use OnPropertyChanged on its own.
I am using MediaManager Plugin with latest version to play a video. everything works fine when i run application in debug mode, but when i create a package for window, video is not showing only voice is heard.
I am using below Package
Plugin.MediaManager.Forms
This is My XAML page
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Digi_Sign.Views.MediaScreen"
xmlns:forms="clr-namespace:MediaManager.Forms;assembly=MediaManager.Forms"
BackgroundColor="Black">
<ContentPage.Content>
<Grid x:Name="stkLayout" VerticalOptions="CenterAndExpand" HorizontalOptions="CenterAndExpand" BackgroundColor="Black">
<forms:VideoView VideoAspect="AspectFill" x:Name="videoPlayer" ShowControls="False" />
</Grid>
</ContentPage.Content>
</ContentPage>
CS Page
CrossMediaManager.Current.Play(fileName);
No Errors Found in package error log, as i mentioned everything works fine when it is debug mode, but not working in release mode
Basically there is no way to initialize renderer for video in native code for xamrin.UWP, so we need to manually load render assembly in initialize it in App.xaml.cs of UWP platform
Below is my code where i have load assembly files inside OnLaunched() and replace existing Xamarin.Forms.Forms.Init()
protected override void OnLaunched(LaunchActivatedEventArgs e)
{
Windows.UI.Xaml.Controls.Frame rootFrame = Window.Current.Content as Windows.UI.Xaml.Controls.Frame;
if (rootFrame == null)
{
rootFrame = new Windows.UI.Xaml.Controls.Frame();
rootFrame.NavigationFailed += OnNavigationFailed;
List<Assembly> assembliesToAdd = new List<Assembly>();
assembliesToAdd.Add(typeof(VideoViewRenderer).GetTypeInfo().Assembly);
Xamarin.Forms.Forms.Init(e, assembliesToAdd);
// Place the frame in the current Window
Window.Current.Content = rootFrame;
}
if (rootFrame.Content == null)
{
rootFrame.Navigate(typeof(MainPage), e.Arguments);
}
Window.Current.Activate();
}
for more details, here is the link. where you can find more explaination.
https://forums.xamarin.com/discussion/119451/crossmedia-works-in-debug-but-not-in-release-for-uwp-error-msg-about-onecore
Most likely from the behavior you are describing (and looking at your code), it sounds like it's because the Video is not being run on the UI thread, and this causes the app to play the audio but not the video. So change it to the following:
Device.BeginInvokeOnMainThread(() => { CrossMediaManager.Current.Play(fileName); });
How can i perform triple tap in Xamarin on a specific element?
I tried with for loop performing a single tap 3 times, but it looks like it is slow cause it's not working.
public void AppLaunches()
{
for(int i = 0; i <= 3;i++)
{
app.Tap(x => x.Css(".app-logo"));
}
You can add TapGestureRecognizer in code or XAML and set the number of taps required to trigger the associated event or command:
XAML using event handler:
<Label Text="Welcome to Xamarin Forms!" VerticalOptions="Center" HorizontalOptions="Center" >
<Label.GestureRecognizers>
<TapGestureRecognizer NumberOfTapsRequired="3" Tapped="Handle_Tapped"/>
</Label.GestureRecognizers>
</Label>
The Event handler:
void Handle_Tapped(object sender, System.EventArgs e)
{
System.Diagnostics.Debug.WriteLine($"Triple tap on {sender}");
}
Re: Adding a Tap Gesture Gesture Recognizer
Note: Triple taps work fine on iOS, but Android does not support triple tabs as a standard feature (it does support double taps).
Re: https://bugzilla.xamarin.com/show_bug.cgi?id=32874
If you need triple tabs on Android, you can capture each tap individually (NumberOfTapsRequired="1"). If it is the first tap, set a local variable to 1 and start a background timer that expires in X milliseconds, if the next tab occurs before the timer has expired, restart the timer and 1 to your local variable, repeat until you get three taps. If the timer expires have it reset the local variable back to 0.