All I needed to do to make it work in MvvmCross was to add the binding code from the LinearLayout :)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections; | |
using System.Collections.Specialized; | |
using Android.Content; | |
using Android.Util; | |
using Android.Widget; | |
using Cirrious.MvvmCross.Binding; | |
using Cirrious.MvvmCross.Binding.Attributes; | |
using Cirrious.MvvmCross.Binding.Droid; | |
using Cirrious.MvvmCross.Binding.Droid.Views; | |
using Cirrious.MvvmCross.Interfaces.Platform.Diagnostics; | |
namespace MyApp.UI.Droid.Controls | |
{ | |
public class BindingFlowLayout : FlowLayout | |
{ | |
public BindingFlowLayout(Context context, IAttributeSet attrs) | |
: base(context, attrs) | |
{ | |
var itemTemplateId = MvxBindableListViewHelpers.ReadAttributeValue(context, attrs, | |
MvxAndroidBindingResource.Instance | |
.BindableListViewStylableGroupId, | |
MvxAndroidBindingResource.Instance | |
.BindableListItemTemplateId); | |
Adapter = new MvxBindableListAdapterWithChangedEvent(context); | |
Adapter.ItemTemplateId = itemTemplateId; | |
Adapter.DataSetChanged += AdapterOnDataSetChanged; | |
this.ChildViewRemoved += OnChildViewRemoved; | |
} | |
private void OnChildViewRemoved(object sender, ChildViewRemovedEventArgs childViewRemovedEventArgs) | |
{ | |
var boundChild = childViewRemovedEventArgs.Child as MvxBindableListItemView; | |
if (boundChild != null) | |
{ | |
boundChild.ClearBindings(); | |
} | |
} | |
private MvxBindableListAdapterWithChangedEvent _adapter; | |
public MvxBindableListAdapterWithChangedEvent Adapter | |
{ | |
get { return _adapter; } | |
set | |
{ | |
var existing = _adapter; | |
if (existing == value) | |
return; | |
if (existing != null && value != null) | |
{ | |
existing.DataSetChanged -= AdapterOnDataSetChanged; | |
value.ItemsSource = existing.ItemsSource; | |
value.ItemTemplateId = existing.ItemTemplateId; | |
} | |
if (value != null) | |
{ | |
value.DataSetChanged += AdapterOnDataSetChanged; | |
} | |
if (value == null) | |
{ | |
MvxBindingTrace.Trace(MvxTraceLevel.Warning, | |
"Setting Adapter to null is not recommended - you amy lose ItemsSource binding when doing this"); | |
} | |
_adapter = value; | |
} | |
} | |
[MvxSetToNullAfterBinding] | |
public IEnumerable ItemsSource | |
{ | |
get { return Adapter.ItemsSource; } | |
set { Adapter.ItemsSource = value; } | |
} | |
public int ItemTemplateId | |
{ | |
get { return Adapter.ItemTemplateId; } | |
set { Adapter.ItemTemplateId = value; } | |
} | |
private void AdapterOnDataSetChanged(object sender, NotifyCollectionChangedEventArgs eventArgs) | |
{ | |
switch (eventArgs.Action) | |
{ | |
case NotifyCollectionChangedAction.Add: | |
this.Add(Adapter, eventArgs.NewStartingIndex, eventArgs.NewItems.Count); | |
break; | |
case NotifyCollectionChangedAction.Remove: | |
this.Remove(Adapter, eventArgs.OldStartingIndex, eventArgs.OldItems.Count); | |
break; | |
case NotifyCollectionChangedAction.Replace: | |
if (eventArgs.NewItems.Count != eventArgs.OldItems.Count) | |
{ | |
this.Refill(Adapter); | |
} | |
else | |
{ | |
this.Replace(Adapter, eventArgs.NewStartingIndex, eventArgs.NewItems.Count); | |
} | |
break; | |
case NotifyCollectionChangedAction.Move: | |
// move is not implemented - so we call Refill instead | |
this.Refill(Adapter); | |
break; | |
case NotifyCollectionChangedAction.Reset: | |
this.Refill(Adapter); | |
break; | |
default: | |
throw new ArgumentOutOfRangeException(); | |
} | |
} | |
public void Refill(IAdapter adapter) | |
{ | |
RemoveAllViews(); | |
var count = adapter.Count; | |
for (var i = 0; i < count; i++) | |
{ | |
AddView(adapter.GetView(i, null, this)); | |
} | |
} | |
public void Add(IAdapter adapter, int insertionIndex, int count) | |
{ | |
for (var i = 0; i < count; i++) | |
{ | |
AddView(adapter.GetView(insertionIndex + i, null, this), insertionIndex + i); | |
} | |
} | |
public void Remove(IAdapter adapter, int removalIndex, int count) | |
{ | |
for (var i = 0; i < count; i++) | |
{ | |
RemoveViewAt(removalIndex + i); | |
} | |
} | |
public void Replace(IAdapter adapter, int startIndex, int count) | |
{ | |
for (var i = 0; i < count; i++) | |
{ | |
RemoveViewAt(startIndex + i); | |
AddView(adapter.GetView(startIndex + i, null, this), startIndex + i); | |
} | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using Android.Content; | |
using Android.Graphics; | |
using Android.Util; | |
using Android.Views; | |
namespace MyApp.UI.Droid.Controls | |
{ | |
// This control based on work in http://forums.xamarin.com/discussion/comment/156#Comment_156: | |
/** | |
* User: Romain Guy | |
* <p/> | |
* Using example: | |
* <?xml version="4.0" encoding="utf-8"?> | |
* <com.example.android.layout.FlowLayout | |
* xmlns:f="http://schemas.android.com/apk/res/org.apmem.android" | |
* xmlns:android="http://schemas.android.com/apk/res/android" | |
* f:horizontalSpacing="6dip" | |
* f:verticalSpacing="12dip" | |
* android:layout_width="wrap_content" | |
* android:layout_height="wrap_content" | |
* android:paddingLeft="6dip" | |
* android:paddingTop="6dip" | |
* android:paddingRight="12dip"> | |
* <Button | |
* android:layout_width="wrap_content" | |
* android:layout_height="wrap_content" | |
* f:layout_horizontalSpacing="32dip" | |
* f:layout_breakLine="true" | |
* android:text="Cancel" /> | |
* <p/> | |
* </com.example.android.layout.FlowLayout> | |
*/ | |
public class FlowLayout : ViewGroup | |
{ | |
public static int Horizontal = 0; | |
public static int Vertical = 1; | |
public int HorizontalSpacing = 0; | |
public int VerticalSpacing = 0; | |
public int Orientation = 0; | |
public bool DebugDraw = false; | |
public FlowLayout(Context context) | |
: base(context) | |
{ | |
ReadStyleParameters(context, null); | |
} | |
public FlowLayout(Context context, IAttributeSet attributeSet) | |
: base(context) | |
{ | |
ReadStyleParameters(context, attributeSet); | |
} | |
public FlowLayout(Context context, IAttributeSet attributeSet, int defStyle) | |
: base(context, attributeSet, defStyle) | |
{ | |
ReadStyleParameters(context, attributeSet); | |
} | |
protected override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec) | |
{ | |
var sizeWidth = MeasureSpec.GetSize(widthMeasureSpec) - PaddingRight - PaddingLeft; | |
var sizeHeight = MeasureSpec.GetSize(heightMeasureSpec) - PaddingRight - PaddingLeft; | |
var modeWidth = (int)MeasureSpec.GetMode(widthMeasureSpec); | |
var modeHeight = (int)MeasureSpec.GetMode(heightMeasureSpec); | |
int size; | |
int mode; | |
if (Orientation == Horizontal) | |
{ | |
size = sizeWidth; | |
mode = modeWidth; | |
} | |
else | |
{ | |
size = sizeHeight; | |
mode = modeHeight; | |
} | |
var lineThicknessWithSpacing = 0; | |
var lineThickness = 0; | |
var lineLengthWithSpacing = 0; | |
var prevLinePosition = 0; | |
var controlMaxLength = 0; | |
var controlMaxThickness = 0; | |
var count = ChildCount; | |
for (var i = 0; i < count; i++) | |
{ | |
var child = GetChildAt(i); | |
if (child.Visibility == ViewStates.Gone) | |
{ | |
continue; | |
} | |
child.Measure( | |
MeasureSpec.MakeMeasureSpec(sizeWidth, (MeasureSpecMode)modeWidth == MeasureSpecMode.Exactly ? MeasureSpecMode.AtMost : (MeasureSpecMode)modeWidth), | |
MeasureSpec.MakeMeasureSpec(sizeHeight, (MeasureSpecMode)modeHeight == MeasureSpecMode.Exactly ? MeasureSpecMode.AtMost : (MeasureSpecMode)modeHeight) | |
); | |
var lp = (LayoutParams)child.LayoutParameters; | |
var hSpacing = GetHorizontalSpacing(lp); | |
var vSpacing = GetVerticalSpacing(lp); | |
var childWidth = child.MeasuredWidth; | |
var childHeight = child.MeasuredHeight; | |
int childLength; | |
int childThickness; | |
int spacingLength; | |
int spacingThickness; | |
if (Orientation == Horizontal) | |
{ | |
childLength = childWidth; | |
childThickness = childHeight; | |
spacingLength = hSpacing; | |
spacingThickness = vSpacing; | |
} | |
else | |
{ | |
childLength = childHeight; | |
childThickness = childWidth; | |
spacingLength = vSpacing; | |
spacingThickness = hSpacing; | |
} | |
var lineLength = lineLengthWithSpacing + childLength; | |
lineLengthWithSpacing = lineLength + spacingLength; | |
var newLine = lp.NewLine || ((MeasureSpecMode)mode != MeasureSpecMode.Unspecified && lineLength > size); | |
if (newLine) | |
{ | |
prevLinePosition = prevLinePosition + lineThicknessWithSpacing; | |
lineThickness = childThickness; | |
lineLength = childLength; | |
lineThicknessWithSpacing = childThickness + spacingThickness; | |
lineLengthWithSpacing = lineLength + spacingLength; | |
} | |
lineThicknessWithSpacing = Math.Max(lineThicknessWithSpacing, childThickness + spacingThickness); | |
lineThickness = Math.Max(lineThickness, childThickness); | |
int posX; | |
int posY; | |
if (Orientation == Horizontal) | |
{ | |
posX = PaddingLeft + lineLength - childLength; | |
posY = PaddingTop + prevLinePosition; | |
} | |
else | |
{ | |
posX = PaddingLeft + prevLinePosition; | |
posY = PaddingTop + lineLength - childHeight; | |
} | |
lp.SetPosition(posX, posY); | |
controlMaxLength = Math.Max(controlMaxLength, lineLength); | |
controlMaxThickness = prevLinePosition + lineThickness; | |
} | |
if (Orientation == Horizontal) | |
{ | |
SetMeasuredDimension(ResolveSize(controlMaxLength, widthMeasureSpec), ResolveSize(controlMaxThickness, heightMeasureSpec)); | |
} | |
else | |
{ | |
SetMeasuredDimension(ResolveSize(controlMaxThickness, widthMeasureSpec), ResolveSize(controlMaxLength, heightMeasureSpec)); | |
} | |
} | |
private int GetVerticalSpacing(LayoutParams lp) | |
{ | |
int vSpacing; | |
if (lp.VerticalSpacingSpecified()) | |
{ | |
vSpacing = lp.VerticalSpacing; | |
} | |
else | |
{ | |
vSpacing = VerticalSpacing; | |
} | |
return vSpacing; | |
} | |
private int GetHorizontalSpacing(LayoutParams lp) | |
{ | |
int hSpacing; | |
if (lp.HorizontalSpacingSpecified()) | |
{ | |
hSpacing = lp.HorizontalSpacing; | |
} | |
else | |
{ | |
hSpacing = HorizontalSpacing; | |
} | |
return hSpacing; | |
} | |
protected override void OnLayout(bool changed, int l, int t, int r, int b) | |
{ | |
var count = ChildCount; | |
for (var i = 0; i < count; i++) | |
{ | |
var child = GetChildAt(i); | |
var lp = (LayoutParams)child.LayoutParameters; | |
child.Layout(lp.X, lp.Y, lp.X + child.MeasuredWidth, lp.Y + child.MeasuredHeight); | |
} | |
} | |
protected override bool DrawChild(Canvas canvas, View child, long drawingTime) | |
{ | |
var more = base.DrawChild(canvas, child, drawingTime); | |
DrawDebugInfo(canvas, child); | |
return more; | |
} | |
protected override bool CheckLayoutParams(ViewGroup.LayoutParams p) | |
{ | |
return p is LayoutParams; | |
} | |
protected override ViewGroup.LayoutParams GenerateDefaultLayoutParams() | |
{ | |
return new LayoutParams(ViewGroup.LayoutParams.WrapContent, ViewGroup.LayoutParams.WrapContent); | |
} | |
public override ViewGroup.LayoutParams GenerateLayoutParams(IAttributeSet attributeSet) | |
{ | |
return new LayoutParams(Context, attributeSet); | |
} | |
protected override ViewGroup.LayoutParams GenerateLayoutParams(ViewGroup.LayoutParams p) | |
{ | |
return new LayoutParams(p); | |
} | |
private void ReadStyleParameters(Context context, IAttributeSet attributeSet) | |
{ | |
var a = context.ObtainStyledAttributes(attributeSet, Resource.Styleable.FlowLayout); | |
try | |
{ | |
HorizontalSpacing = a.GetDimensionPixelSize(Resource.Styleable.FlowLayout_horizontalSpacing, 0); | |
VerticalSpacing = a.GetDimensionPixelSize(Resource.Styleable.FlowLayout_verticalSpacing, 0); | |
Orientation = a.GetInteger(Resource.Styleable.FlowLayout_orientation, Horizontal); | |
DebugDraw = a.GetBoolean(Resource.Styleable.FlowLayout_debugDraw, false); | |
} | |
finally | |
{ | |
a.Recycle(); | |
} | |
} | |
private void DrawDebugInfo(Canvas canvas, View child) | |
{ | |
if (!DebugDraw) | |
{ | |
return; | |
} | |
var childPaint = CreatePaint(new Color(255, 255, 0, 255)); | |
var layoutPaint = CreatePaint(new Color(0, 255, 0, 255)); | |
var newLinePaint = CreatePaint(new Color(255, 0, 0, 255)); | |
var lp = (LayoutParams)child.LayoutParameters; | |
if (lp.HorizontalSpacing > 0) | |
{ | |
float x = child.Right; | |
var y = child.Top + child.Height / 2.0f; | |
canvas.DrawLine(x, y, x + lp.HorizontalSpacing, y, childPaint); | |
canvas.DrawLine(x + lp.HorizontalSpacing - 4.0f, y - 4.0f, x + lp.HorizontalSpacing, y, childPaint); | |
canvas.DrawLine(x + lp.HorizontalSpacing - 4.0f, y + 4.0f, x + lp.HorizontalSpacing, y, childPaint); | |
} | |
else | |
if (HorizontalSpacing > 0) | |
{ | |
float x = child.Right; | |
var y = child.Top + child.Height / 2.0f; | |
canvas.DrawLine(x, y, x + HorizontalSpacing, y, layoutPaint); | |
canvas.DrawLine(x + HorizontalSpacing - 4.0f, y - 4.0f, x + HorizontalSpacing, y, layoutPaint); | |
canvas.DrawLine(x + HorizontalSpacing - 4.0f, y + 4.0f, x + HorizontalSpacing, y, layoutPaint); | |
} | |
if (lp.VerticalSpacing > 0) | |
{ | |
var x = child.Left + child.Width / 2.0f; | |
float y = child.Bottom; | |
canvas.DrawLine(x, y, x, y + lp.VerticalSpacing, childPaint); | |
canvas.DrawLine(x - 4.0f, y + lp.VerticalSpacing - 4.0f, x, y + lp.VerticalSpacing, childPaint); | |
canvas.DrawLine(x + 4.0f, y + lp.VerticalSpacing - 4.0f, x, y + lp.VerticalSpacing, childPaint); | |
} | |
else if (VerticalSpacing > 0) | |
{ | |
var x = child.Left + child.Width / 2.0f; | |
float y = child.Bottom; | |
canvas.DrawLine(x, y, x, y + VerticalSpacing, layoutPaint); | |
canvas.DrawLine(x - 4.0f, y + VerticalSpacing - 4.0f, x, y + VerticalSpacing, layoutPaint); | |
canvas.DrawLine(x + 4.0f, y + VerticalSpacing - 4.0f, x, y + VerticalSpacing, layoutPaint); | |
} | |
if (lp.NewLine) | |
{ | |
if (Orientation == Horizontal) | |
{ | |
float x = child.Left; | |
var y = child.Top + child.Height / 2.0f; | |
canvas.DrawLine(x, y - 6.0f, x, y + 6.0f, newLinePaint); | |
} | |
else | |
{ | |
var x = child.Left + child.Width / 2.0f; | |
float y = child.Top; | |
canvas.DrawLine(x - 6.0f, y, x + 6.0f, y, newLinePaint); | |
} | |
} | |
} | |
private static Paint CreatePaint(Color color) | |
{ | |
var paint = new Paint | |
{ | |
AntiAlias = true, | |
Color = color, | |
StrokeWidth = 2.0f | |
}; | |
return paint; | |
} | |
public new class LayoutParams : ViewGroup.LayoutParams //Java class WAS static | |
{ | |
private const int NoSpacing = -1; | |
public int X; | |
public int Y; | |
public int HorizontalSpacing = NoSpacing; | |
public int VerticalSpacing = NoSpacing; | |
public bool NewLine = false; | |
public LayoutParams(Context context, IAttributeSet attributeSet) : | |
base(context, attributeSet) | |
{ | |
ReadStyleParameters(context, attributeSet); | |
} | |
public LayoutParams(int width, int height) : base(width, height) { } | |
public LayoutParams(ViewGroup.LayoutParams layoutParams) : base(layoutParams) { } | |
public bool HorizontalSpacingSpecified() | |
{ | |
return HorizontalSpacing != NoSpacing; | |
} | |
public bool VerticalSpacingSpecified() | |
{ | |
return VerticalSpacing != NoSpacing; | |
} | |
public void SetPosition(int x, int y) | |
{ | |
X = x; | |
Y = y; | |
} | |
private void ReadStyleParameters(Context context, IAttributeSet attributeSet) | |
{ | |
var a = context.ObtainStyledAttributes(attributeSet, Resource.Styleable.FlowLayout_LayoutParams); | |
try | |
{ | |
HorizontalSpacing = a.GetDimensionPixelSize(Resource.Styleable.FlowLayout_LayoutParams_layout_horizontalSpacing, NoSpacing); | |
VerticalSpacing = a.GetDimensionPixelSize(Resource.Styleable.FlowLayout_LayoutParams_layout_verticalSpacing, NoSpacing); | |
NewLine = a.GetBoolean(Resource.Styleable.FlowLayout_LayoutParams_layout_newLine, false); | |
} | |
finally | |
{ | |
a.Recycle(); | |
} | |
} | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="utf-8" ?> | |
<resources> | |
<declare-styleable name="FlowLayout"> | |
<attr name="horizontalSpacing" format="dimension"/> | |
<attr name="verticalSpacing" format="dimension"/> | |
<attr name="orientation" format="enum"> | |
<enum name="horizontal" value="0"/> | |
<enum name="vertical" value="1"/> | |
</attr> | |
<attr name="debugDraw" format="boolean" /> | |
</declare-styleable> | |
<declare-styleable name="FlowLayout_LayoutParams"> | |
<attr name="layout_newLine" format="boolean"/> | |
<attr name="layout_horizontalSpacing" format="dimension"/> | |
<attr name="layout_verticalSpacing" format="dimension"/> | |
</declare-styleable> | |
</resources> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="utf-8"?> | |
<RelativeLayout | |
xmlns:android="http://schemas.android.com/apk/res/android" | |
xmlns:local="http://schemas.android.com/apk/res/MyApp.UI.Droid" | |
android:layout_width="fill_parent" | |
android:layout_height="fill_parent" | |
> | |
<ScrollView | |
android:layout_width="fill_parent" | |
android:layout_height="fill_parent"> | |
<MyApp.UI.Droid.Controls.BindingFlowLayout | |
android:id="@+id/ButtonPanel" | |
android:gravity="center" | |
local:horizontalSpacing="10dip" | |
local:verticalSpacing="10dip" | |
local:debugDraw="true" | |
android:layout_width="fill_parent" | |
android:layout_height="fill_parent" | |
local:MvxBind="{'ItemsSource':{'Path':'MenuItems'}}" | |
local:MvxItemTemplate="@layout/menuitem_button" | |
> | |
</MyApp.UI.Droid.Controls.BindingFlowLayout> | |
</ScrollView> | |
</RelativeLayout> |
Hi
ReplyDeleteI tried using this but it seems I am missing alot of types such as MvxBindableListViewHelpers and MvxBindableListAdapterWithChangedEvent and more...
Where can I find them?
I couldn't make this work. There's a lot of missing refs!
ReplyDelete