Closed MattePozzy closed 1 year ago
Hi, the multiple render calls that you see are not caused by the animation but instead are required by the call to SetState(s => s.Status++): you are actually requesting a new render for every render. RxAnimations require that you use the SetState(s=>..) but the component is refreshed only once just to update the state: the animation works under the hood without re-render the complete component but just updating the dependency property marked with the WithAnimation() method.
Try this:
public override VisualNode Render()
{
Debug.WriteLine("Render called");
return new ContentPage
{
new Grid("auto,auto,auto,auto", "*,*")
{
new Label("Tap on each box").GridColumn(0).GridRow(0).GridColumnSpan(2).VCenter().HCenter(),
new Label("Works --> invalidateComponent TRUE").GridColumn(0).GridRow(1).VCenter().HCenter(),
new BoxView().GridColumn(0).GridRow(2)
.HeightRequest(80).WidthRequest(80)
.Color(Colors.Green)
.Opacity(State.IsExpanded_Green ? 1 : 0.2)
.HeightRequest(State.IsExpanded_Green ? 250 : 80)
.WithAnimation(easing: Easing.Linear, duration: 1000)
.OnTapped(() =>{
SetState(s => s.IsExpanded_Green = !s.IsExpanded_Green);
}),
}
.VCenter().HCenter()
.RowSpacing(25).ColumnSpacing(25)
.Padding(30, 0)
};
}
you should see only one message "Render called".
Regarding the animation from bottom to top, it's a layout issue in your code: setting Auto row height + VCenter() you are asking the grid to unlarge from the center to accommodate its content: try use HStart() on the grid.
Hi, thank you but the render is called on every click on the box. I have used this code:
public override VisualNode Render()
{
Debug.WriteLine("Render called");
//SetState(s => s.Status++);
return new ContentPage
{
new Grid("auto,auto,auto,auto", "*,*")
{
new Label("Tap on each box").GridColumn(0).GridRow(0).GridColumnSpan(2).VCenter().HCenter(),
new Label("Works --> invalidateComponent TRUE").GridColumn(0).GridRow(1).VCenter().HCenter(),
new BoxView().GridColumn(0).GridRow(2)
.HeightRequest(80).WidthRequest(80)
.Color(Colors.Green)
.Opacity(State.IsExpanded_Green ? 1 : 0.2)
.HeightRequest(State.IsExpanded_Green ? 250 : 80)
.WithAnimation(easing: Easing.Linear, duration: 1000)
.OnTapped(() =>{
SetState(s => s.IsExpanded_Green = !s.IsExpanded_Green);
}),
}
.VEnd()
.RowSpacing(25).ColumnSpacing(25)
.Padding(30, 0)
};
}
yes is correct, it's called everytime you call SetState to update IsExpanded_Green; but is not called during animation
yes is correct, it's called everytime you call SetState to update IsExpanded_Green; but is not called during animation
Ok, there is a way to avoid this? I need to set invalidateComponent to false like this, but doesn't work.
SetState(s => s.IsExpanded_Green = !s.IsExpanded_Green, false);
If I use this code, the animation doesn't work.
public override VisualNode Render()
{
Debug.WriteLine("Render called");
//SetState(s => s.Status++);
return new ContentPage
{
new Grid("auto,auto,auto,auto", "*,*")
{
new Label("Tap on each box").GridColumn(0).GridRow(0).GridColumnSpan(2).VCenter().HCenter(),
new Label("invalidateComponent FALSE").GridColumn(0).GridRow(1).VCenter().HCenter(),
new BoxView().GridColumn(0).GridRow(2)
.HeightRequest(80).WidthRequest(80)
.Color(Colors.Green)
.Opacity(() => State.IsExpanded_Green ? 1 : 0.2)
.HeightRequest(() => State.IsExpanded_Green ? 250 : 80)
.WithAnimation(easing: Easing.Linear, duration: 1000)
.OnTapped(() => {
SetState(s => s.IsExpanded_Green = !s.IsExpanded_Green, false);
}),
}
.VEnd()
.RowSpacing(25).ColumnSpacing(25)
.Padding(30, 0)
};
}
I need to avoid the re-render when I set IsExpanded_Green to fire the animation.
No, is not possible (at least using rxAnimation). Maybe you can try the AnimationController but the code I guess would be more complicated. In the end, you can simply fall back to MAUI class animations: Get a reference to the BoxView: new BoxView(boxViewRef => _boxViewRef = boxViewRef) and call _boxViewRef.FadeTo(..) in the OnTappedEvent. Anyway, I'm not sure why you're worried about the render pass: it's optimized enough to handle the majority of the cases.
I have a listview and when I run the animation the app freeze beacause of the re-render. Here a video.
hum, ok, it's hard to say what causes it, seems like the parent component render is called during the animation, can you add a Debug.Writeline("Render") in the render method containing the listview? just to see if it's called many times during the animation
EDIT forget it. the problem is also with only the labels. I can not understand.
Hi!
While i was making an example to send, i found the problem.
I created a custom label which is actually a VStack containing two labels (one for description and one for the data).
Apparently if there are too many VStacks in the list item template the render freezes and also the animations.
think I'll rewrite the itemTemplate using only label.
This is the custom control
public partial class VolosLabel : VStack
{
private readonly Label Descr = new();
private readonly Label Value = new();
public VolosLabel()
{
Descr.Style(Utility.GetResources<MauiControls.Style>("LblDescr"));
Value.Style(Utility.GetResources<MauiControls.Style>("LblValore"));
Value.LineBreakMode(LineBreakMode.WordWrap);
Add(Descr);
Add(Value);
}
public VolosLabel Descrizione(string text)
{
Descr.Text(text);
return this;
}
public VolosLabel Valore(string text)
{
Value.Text(text);
return this;
}
public VolosLabel ValoreTextColorHex(string hexColor)
{
Value.TextColor(Color.FromArgb(hexColor));
return this;
}
public VolosLabel SetLineBreakMode(LineBreakMode type)
{
Descr.LineBreakMode(type);
Value.LineBreakMode(type);
return this;
}
public VolosLabel ValoreAsHtml(bool siNo)
{
if (siNo)
{
Value.TextType(TextType.Html);
Value.FontSize(FontSize.Normal);
Value.FontFamily("Arial");
}
return this;
}
}
and this is the ItemTemplate:
return new ViewCell {
new VStack() {
new Grid("auto,auto,auto,auto,auto", "20,*,*,*,*,*,*,10")
{
#region Riga colorata
new BoxView().GridRow(0).GridColumn(0).GridRowSpan(5).Margin(new Thickness(0,0,10,0)).WidthRequest(10).Color(Color.FromArgb(item.Colore)),
#endregion
#region Numero/Anno Wo + icona offline + icona mio wo
new VolosLabel().GridRow(0).GridColumn(1).GridColumnSpan(6)
.Descrizione(State.TS.Translate("OFL_workorder.LblWO"))
.Valore(item.NumWoAnno),
new Label().GridRow(0).GridColumn(6)
.VerticalTextAlignment(TextAlignment.Center).HorizontalTextAlignment(TextAlignment.End)
.IsVisible(item.Offline).FontFamily(Costanti.Font.MaterialDesignIcons.ToString()).FontSize(FontSize.Large)
.Text(MaterialFontIcons.TabletCellphone).TextColor(Utility.GetResources<Color>("Primary")),
new Label().GridRow(1).GridColumn(6)
.VerticalTextAlignment(TextAlignment.Center).HorizontalTextAlignment(TextAlignment.End)
.IsVisible(item.MioWo).FontFamily(Costanti.Font.MaterialDesignIcons.ToString()).FontSize(FontSize.Large)
.Text(MaterialFontIcons.Crown).TextColor(Utility.GetResources<Color>("Primary")),
#endregion
#region Cliente, Sito e indirizzo completo
new VolosLabel().GridRow(1).GridColumn(1).GridColumnSpan(6)
.Descrizione(State.TS.Translate("OFL_workorder.LblDatiAnagrafici"))
.Valore(item.DatiAnagrafici),
#endregion
#region Macchina + tipo wo
new VolosLabel().GridRow(2).GridColumn(1).GridColumnSpan(2)
.Descrizione(State.TS.Translate("OFL_workorder.LblMacchina"))
.Valore(item.MacchinaFull),
new VolosLabel().GridRow(2).GridColumn(3).GridColumnSpan(4)
.Descrizione(State.TS.Translate("OFL_workorder.LblTipoWO"))
.Valore(item.TipoWo),
#endregion
#region severity + Data apertura + Data scadenza
new VolosLabel().GridRow(3).GridColumn(1).GridColumnSpan(2)
.Descrizione(State.TS.Translate("OFL_workorder.LblSeverity"))
.Valore(item.Severity),
new VolosLabel().GridRow(3).GridColumn(3).GridColumnSpan(2)
.Descrizione(State.TS.Translate("OFL_workorder.LblDataApertura"))
.Valore(item.Data_apertura.Value.ToString(UtenteMobile.UteLoggato.formatoData)),
new VolosLabel().GridRow(3).GridColumn(5).GridColumnSpan(2)
.Descrizione(State.TS.Translate("OFL_workorder.LblDataScadenza"))
.Valore(item.Data_scadenza.Value.ToString(UtenteMobile.UteLoggato.formatoData)),
#endregion
#region Passaggio Successivo
new VolosLabel().GridRow(4).GridColumn(1).GridColumnSpan(6)
.Descrizione(State.TS.Translate("OFL_workorder.LblProgressStatus"))
.Valore(item.ProgressStatus)
#endregion
},
new BoxView().HeightRequest(1).Color(Utility.GetResources<Color>("Gray400"))
},
new MenuFlyout
{
MenuPerWin(State.TS.Translate("OFL_allegato.Allegati"), MaterialFontIcons.Paperclip),
MenuPerWin(State.TS.Translate("OFL_WorkOrder.Note"), MaterialFontIcons.NoteMultiple),
MenuPerWin(State.TS.Translate("OFL_WorkOrder.impostaCoordinateWo"), MaterialFontIcons.MapMarker).OnClicked(async (obj, evt) => await ImpostaCoordinate(obj, evt)),
MenuPerWin(State.TS.Translate("OFL_WorkOrder.anteprimaStampa"), MaterialFontIcons.Printer),
MenuPerWin(State.TS.Translate("ofl_nota.Attivita"), MaterialFontIcons.TagMultiple),
MenuPerWin(State.TS.Translate("OFL_MARKETING.titoloDialogWarningOpportunities"), MaterialFontIcons.Briefcase),
MenuPerWin(State.TS.Translate("OFL_WorkOrder.impostaOffline"), MaterialFontIcons.TabletCellphone).OnClicked(async (obj, evt) =>
{
MauiControls.MenuFlyoutItem menu = obj as MauiControls.MenuFlyoutItem;
WorkOrderLista woSel = menu.BindingContext as WorkOrderLista;
await ImpostaOffline(woSel);
}),
}
};
I have solved by using a method like this one:
new BoxView(boxViewRef => _boxViewRef = boxViewRef) and call _boxViewRef.FadeTo(..)
Hi, I have created a basic animation but to make it works I need to invalidate all the components and re-render the enteire page, otherwise the animation doesn't work.
I have many heavy load pages, so I can' re-render the page.
There is a way to set invalidateComponent to false and make the animation works?
Here a sample.
Thank you.
EDIT: also there is a way to animate the height only in one direction (eg. only from bottom to top)? Thank you.