Skip to content

Commit 8d7daae

Browse files
authored
Merge pull request #9 from xtuzy/Modify
add a animation api
2 parents 0a56461 + 3cc265a commit 8d7daae

File tree

7 files changed

+341
-31
lines changed

7 files changed

+341
-31
lines changed
Lines changed: 44 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
using SharpConstraintLayout.Maui.Widget;
2+
using static SharpConstraintLayout.Maui.Widget.FluentConstraintSet;
23

34
namespace SharpConstraintLayout.Maui.Example.Pages
45
{
56

67
public partial class ShowAnimationView : ContentView
78
{
89
private Button button1;
10+
private Button button2;
11+
private ContentView backgroundView;
912
private ConstraintLayout layout;
1013

1114
public ShowAnimationView()
@@ -15,41 +18,53 @@ public ShowAnimationView()
1518
layout = new ConstraintLayout();
1619
this.Content = layout;
1720
button1 = new Button() { Text = "Button1" };
18-
layout.Add(button1);
19-
button1.Clicked += Button_Clicked;
20-
using (var startset = new FluentConstraintSet())
21-
{
22-
startset.Clone(layout);
23-
startset.Select(button1).CenterYTo().LeftToLeft();
24-
startset.ApplyTo(layout);
25-
}
21+
button1.Clicked += Button1_Clicked;
22+
button2 = new Button() { Text = "Button2" };
23+
button2.Clicked += Button2_Clicked;
24+
backgroundView = new ContentView() { BackgroundColor = Colors.Green };
25+
layout.AddElement(backgroundView, button1, button2);
26+
27+
var startset = new FluentConstraintSet();
28+
startset.Clone(layout);
29+
startset.Select(button1).CenterYTo().LeftToLeft()
30+
.Select(backgroundView).TopToTop().EdgesXTo().Width(SizeBehavier.MatchConstraint).PercentHeight(0.5f).Height(SizeBehavier.MatchConstraint)
31+
.Select(button2).BottomToBottom(backgroundView, 20).RightToRight(backgroundView, 20);
32+
startset.ApplyTo(layout);
33+
2634
}
2735

28-
private void Button_Clicked(object sender, EventArgs e)
36+
bool isExpaned = true;
37+
private void Button2_Clicked(object sender, EventArgs e)
2938
{
30-
//button.ScaleTo(1.5, 1000, Easing.CubicIn);
31-
//var animation = new Animation(v => button1.Scale = v, 1, 2);
32-
//animation.Commit(this, "SimpleAnimation", 16, 2000, Easing.Linear, (v, c) => button1.Scale = 1, () => false);
33-
var animation2 = new Animation(v =>
39+
layout.AbortAnimation("ConstrainTo");
40+
if (isExpaned)//ÐèÒªÊÕËõ
3441
{
35-
using (var set = new FluentConstraintSet())
36-
{
37-
set.Clone(layout);
38-
set.Select(button1).CenterTo().XBias((float)v).Rotation((float)v * 360).Scale((float)v * 2).Alpha((float)v)
39-
;
40-
set.ApplyTo(layout);
41-
}
42-
}, 0, 1);
43-
animation2.Commit(this, "SimpleAnimation", 16, 1000, Easing.CubicInOut, (v, c) =>
42+
var finish = new FluentConstraintSet();
43+
finish.Clone(layout);
44+
finish.Select(backgroundView).Clear().TopToTop().LeftToLeft().Height(20).Width(SizeBehavier.MatchParent)
45+
.Select(button2).Clear().BottomToBottom(null, 20).RightToRight(null, 20);
46+
layout.LayoutToWithAnim(finish, "ConstrainTo", 16, 1200, Easing.SpringOut);
47+
}
48+
else
4449
{
45-
using (var finalSet = new FluentConstraintSet())
46-
{
47-
finalSet.Clone(layout);
48-
finalSet.Select(button1).CenterYTo().RightToRight();
49-
finalSet.ApplyTo(layout);
50-
}
51-
}, () => false);
50+
var start = new FluentConstraintSet();
51+
start.Clone(layout);
52+
start.Select(backgroundView).Clear().TopToTop().EdgesXTo().Width(SizeBehavier.MatchConstraint).PercentHeight(0.5f).Height(SizeBehavier.MatchConstraint)
53+
.Select(button2).Clear().BottomToBottom(backgroundView, 20).RightToRight(backgroundView, 20);
54+
layout.LayoutToWithAnim(start, "ConstrainTo", 16, 3000, Easing.SpringOut);
55+
}
56+
isExpaned = !isExpaned;
57+
}
5258

59+
private void Button1_Clicked(object sender, EventArgs e)
60+
{
61+
var start = new FluentConstraintSet();
62+
start.Clone(layout);
63+
start.Select(button1).Clear().CenterYTo().LeftToLeft();
64+
var finish = new FluentConstraintSet();
65+
finish.Clone(layout);
66+
finish.Select(button1).Clear().CenterYTo().RightToRight();
67+
layout.LayoutToWithAnim(finish, "ConstrainTo", 16, 1200, Easing.SpringOut, (v, b) => { start.ApplyTo(layout); });
5368
}
5469
}
5570
}

SharpConstraintLayout.Maui.Native/Widget/ConstraintSet.Core.cs

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,12 +274,177 @@ public virtual void ApplyTo(ConstraintLayout constraintLayout)
274274
}
275275

276276
constraintLayout.mConstraintSet.IsChanged = true;
277+
constraintLayout.mConstraintSet.IsForAnimation = false;
277278

278279
UIThread.Invoke(() =>
279280
{
280281
constraintLayout.RequestReLayout();
281282
}, constraintLayout);
283+
}
284+
285+
/// <summary>
286+
/// 为动画获得坐标,不造成重新Layout
287+
/// </summary>
288+
/// <param name="constraintLayout"></param>
289+
/// <exception cref="Exception"></exception>
290+
/// <exception cref="NotImplementedException"></exception>
291+
public virtual void ApplyToForAnim(ConstraintLayout constraintLayout)
292+
{
293+
int parentID = constraintLayout.GetId();
294+
295+
int count = constraintLayout.ChildCount;
296+
List<int> used = mConstraints.Keys.ToList();//已经设置了约束的id
297+
if (count != used.Count) Debug.WriteLine("The count of ConstraintLayout children is not equal to temprary constraints list, maybe you not use clone.", TAG);
298+
for (int i = 0; i < count; i++)//查看layout的child
299+
{
300+
View view = constraintLayout.GetChildAt(i);
301+
302+
int id = view.GetId();
303+
if (!mConstraints.ContainsKey(id))
304+
{
305+
Debug.WriteLine($"id unknown {view}", TAG);
306+
continue;
307+
}
308+
309+
if (mForceId && id == -1)
310+
{
311+
throw new Exception("All children of ConstraintLayout must have ids to use ConstraintSet");
312+
}
313+
if (id == -1)
314+
{
315+
continue;
316+
}
317+
318+
if (mConstraints.ContainsKey(id))
319+
{
320+
used.Remove(id);
321+
Constraint constraint = mConstraints[id];
322+
if (constraint == null)
323+
{
324+
continue;
325+
}
326+
if (view is Barrier)
327+
{
328+
constraint.layout.mHelperType = BARRIER_TYPE;
329+
Barrier barrier = (Barrier)view;
330+
//barrier.Id = id;
331+
barrier.ConstrainType = constraint.layout.mBarrierDirection;
332+
barrier.ConstrainMargin = constraint.layout.mBarrierMargin;
333+
334+
barrier.AllowsGoneWidget = constraint.layout.mBarrierAllowsGoneWidgets;
335+
if (constraint.layout.mReferenceIds != null)
336+
{
337+
barrier.ReferencedIds = constraint.layout.mReferenceIds;
338+
}
339+
}
340+
341+
//将新的约束更新到ConstraintLayout的ConstraintSet中
342+
var param = constraintLayout.mConstraintSet.Constraints[id];
343+
param.layout.Validate();
344+
constraint.ApplyTo(param.layout);//Android源码ApplyTo是应用到Params,Params中具有负责View布局的属性,其中Margin,Width,Height是ViewGroup自带的,其他是ConstraintLayout中新增的,也就是说,这里使用ConstraintSet替代Params,需要添加ViewGroup的属性
345+
346+
//设置原本在ViewGroup.Params中的属性,这些属性影响Android中View的Measure值,因此我们需要在其他平台单独设置
347+
#if __MAUI__
348+
//view.SetWidth(constraint.layout.mWidth);
349+
view.SetWidth(ConstraintSet.Unset);
350+
//view.SetHeight(constraint.layout.mHeight);
351+
view.SetHeight(ConstraintSet.Unset);
352+
//view.SetMinWidth(ConstraintSet.Unset);
353+
//view.SetMinHeight(constraint.layout.matchConstraintMinHeight);
354+
//view.SetMaxWidth(constraint.layout.matchConstraintMaxWidth);
355+
//view.SetMaxHeight(constraint.layout.matchConstraintMaxHeight);
356+
#else
357+
//view.SetSizeAndMargin(constraint.layout.mWidth, constraint.layout.mHeight, constraint.layout.matchConstraintMinWidth, constraint.layout.matchConstraintMinHeight, constraint.layout.matchConstraintMaxWidth, constraint.layout.matchConstraintMaxHeight, constraint.layout.leftMargin, constraint.layout.topMargin, constraint.layout.rightMargin, constraint.layout.bottomMargin);
358+
#endif
359+
360+
if (view is Guideline)//不像Android一样用户提供指定约束是isGuideline就创建Guideline,必须通过Guideline控件实现
361+
{
362+
if (constraint.layout.orientation != Unset)//如果用户设置了新的orientation
363+
{
364+
//Orientation在这里设置的原因是在Measure中当约束update到widget时,其他的widget需要对齐Guideline的widget,如果guideline没有设置orientation,那么widget找不到对应的边的Anchor,因为Guideline需要方向才有正确的Anchor
365+
(view as Guideline).mGuideline.Orientation = constraint.layout.orientation;
366+
}
367+
}
368+
369+
/*if (applyPostLayout)
370+
{
371+
ConstraintAttribute.setAttributes(view, constraint.mCustomConstraints);
372+
}*/
373+
374+
if (constraint.propertySet.mVisibilityMode == VisibilityModeNormal)
375+
{
376+
//view.Visibility = constraint.propertySet.visibility;
377+
param.propertySet.visibility = constraint.propertySet.visibility;//这里我变成设置constraint
378+
view.SetViewVisibility(constraint.propertySet.visibility);
379+
}
380+
if (constraint.propertySet.visibility != ConstraintSet.Invisible)//在可见性为Invisible时,设置可见性时会将Alpha设置为0,再设置Alpha会造成冲突
381+
view.SetAlphaProperty(constraint.propertySet);
382+
view.SetTransform(constraint.transform);
383+
}
384+
else
385+
{
386+
Debug.WriteLine(TAG, "WARNING NO CONSTRAINTS for view " + id);
387+
}
388+
}
389+
390+
foreach (int id in used)//剩下的约束不在ConstraintLayout的Children中,找出特殊的,如Barrier,Guideline,但是这里我们不弄,因为添加新的View有新的Id,那么就需要更新全部的约束,费时
391+
{
392+
if (id == ConstraintSet.ParentId || id == constraintLayout.GetId())//还要处理下layout
393+
mConstraints[id].ApplyTo(constraintLayout.mConstraintSet.Constraints[id].layout);
394+
else
395+
throw new NotImplementedException("如果还有约束身下而且是重要的,那么需要新建View插入原来的布局中,那么但是id怎么办,新建的有新的哈希,而约束是相对旧的哈希.");
396+
/*Constraint constraint = mConstraints[id];
397+
if (constraint == null)
398+
{
399+
continue;
400+
}
401+
if (constraint.layout.mHelperType == BARRIER_TYPE)
402+
{
403+
Barrier barrier = new Barrier();
404+
barrier.Id = id;
405+
if (constraint.layout.mReferenceIds != null)
406+
{
407+
barrier.ReferencedIds = constraint.layout.mReferenceIds;
408+
}
409+
else if (!string.ReferenceEquals(constraint.layout.mReferenceIdString, null))
410+
{
411+
constraint.layout.mReferenceIds = convertReferenceString(barrier, constraint.layout.mReferenceIdString);
412+
barrier.ReferencedIds = constraint.layout.mReferenceIds;
413+
}
414+
barrier.Type = constraint.layout.mBarrierDirection;
415+
barrier.Margin = constraint.layout.mBarrierMargin;
416+
LayoutParams param = constraintLayout.generateDefaultLayoutParams();
417+
barrier.validateParams();
418+
constraint.applyTo(param);
419+
constraintLayout.addView(barrier, param);
420+
}
421+
if (constraint.layout.mIsGuideline)
422+
{
423+
Guideline g = new Guideline(constraintLayout.Context);
424+
g.Id = id;
425+
ConstraintLayout.LayoutParams param = constraintLayout.generateDefaultLayoutParams();
426+
constraint.applyTo(param);
427+
constraintLayout.addView(g, param);
428+
}*/
429+
}
430+
for (int i = 0; i < count; i++)
431+
{
432+
View view = constraintLayout.GetChildAt(i);
433+
434+
if (view is ConstraintHelper)
435+
{
436+
ConstraintHelper constraintHelper = (ConstraintHelper)view;
437+
constraintHelper.applyLayoutFeaturesInConstraintSet(constraintLayout);
438+
}
439+
}
440+
441+
constraintLayout.mConstraintSet.IsChanged = true;
442+
constraintLayout.mConstraintSet.IsForAnimation = true;
282443

444+
//UIThread.Invoke(() =>
445+
//{
446+
// constraintLayout.RequestReLayout();
447+
//}, constraintLayout);
283448
}
284449

285450
/// <summary>

SharpConstraintLayout.Maui.Native/Widget/ConstraintSet.Data.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ public partial class ConstraintSet : IDisposable
2727
/// 只对ConstraintLayout中的有效,用户设置中使用的ConstraintSet不使用该Tag
2828
/// </summary>
2929
public bool IsChanged = false;
30+
public bool IsForAnimation = false;
3031

3132
private const string TAG = "ConstraintSet";
3233
private const string ERROR_MESSAGE = "XML parser error must be within a Constraint ";

SharpConstraintLayout.Maui/SharpConstraintLayout.Maui.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'windows'">10.0.17763.0</SupportedOSPlatformVersion>
1616
<TargetPlatformMinVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'windows'">10.0.17763.0</TargetPlatformMinVersion>
1717
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'tizen'">6.5</SupportedOSPlatformVersion>
18-
<Version>2.1.1.5</Version>
18+
<Version>2.1.1.6</Version>
1919
<Description>constraintlayout for maui</Description>
2020
<Copyright></Copyright>
2121
<PackageProjectUrl>https://github.com/xtuzy/SharpConstraintLayout</PackageProjectUrl>

SharpConstraintLayout.Maui/Widget/ConstraintLayout.Maui.cs

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#if __MAUI__
22
using Microsoft.Maui.Layouts;
3+
using SharpConstraintLayout.Maui.Widget;
34
using System;
45
using System.Collections.Generic;
56
using System.Linq;
@@ -77,13 +78,39 @@ protected override ILayoutManager CreateLayoutManager()
7778
return new ConstraintLayoutManager(this);
7879
}
7980

80-
void LayoutChild(UIElement element, int x, int y, int w, int h)
81+
public void LayoutChild(UIElement element, int x, int y, int w, int h)
8182
{
8283
(element as IView).Arrange(new Rect(x, y, w, h));
8384
}
8485
#endregion
8586

8687
public Size GetLastMeasureSize() { return new Size(mLastMeasureWidth, mLastMeasureHeight); }
88+
89+
/// <summary>
90+
/// 获取ConstraintLayout所有子View信息,这是为动画特别提供的
91+
/// </summary>
92+
/// <param name="isNeedMeasure">没有测量过的可能需要测量才能获得到正确的信息</param>
93+
/// <returns></returns>
94+
public Dictionary<int, ViewInfo> CaptureLayoutTreeInfo(bool isNeedMeasure = false)
95+
{
96+
//Try强制Measure
97+
if (isNeedMeasure)
98+
{
99+
var availableSize = GetLastMeasureSize();
100+
(int horizontalSpec, int verticalSpec) = this.MakeSpec(this, availableSize);
101+
this.MeasureLayout(availableSize, horizontalSpec, verticalSpec);
102+
}
103+
104+
var dic = new Dictionary<int, ViewInfo>();
105+
foreach (var item in idToViews)
106+
{
107+
var view = item.Value;
108+
var widget = GetOrAddWidgetById(item.Key);
109+
var info = new ViewInfo() { X = widget.X, Y = widget.Y, Size = new Size(widget.Width, widget.Height), TranlateX = view.TranslationX, TranlateY = view.TranslationY, ScaleX = view.ScaleX, ScaleY = view.ScaleY, Alpha = view.Opacity, RotationX = view.RotationX, RotationY = view.RotationY };
110+
dic.Add(item.Key, info);
111+
}
112+
return dic;
113+
}
87114
}
88115

89116
public class ConstraintLayoutManager : LayoutManager
@@ -107,6 +134,8 @@ public override Size ArrangeChildren(Rect bounds)
107134
{
108135
Size finalSize = bounds.Size;
109136
var layout = Layout as ConstraintLayout;
137+
if (layout.mConstraintSet.IsForAnimation)
138+
return finalSize;
110139
var lastMeasureSize = layout.GetLastMeasureSize();
111140
if (finalSize.Width != lastMeasureSize.Width || finalSize.Height != lastMeasureSize.Height)
112141
{

0 commit comments

Comments
 (0)