Issue with binding to GridLayout to Android

2019-01-28 19:29发布

问题:

I create game using technology Mono. While writing, I have a problem. I need create a grid (4 to 5) with pictures. I thought to apply here GridLayout or GridView. But I do not know how to dynamically create images in GridLayout, which are updated with ViewModel (properties List ). During the game will change object properties SquareModel in List, namely the path to the image. If you click on the image, it will change.

A similar game I did ​​in Windows Phone. There did not have problems.

回答1:

There is a GridView available in Android - but I've personally never used the GridView with databinding in mvvmcross - but I've heard it's quite simple to add - see https://github.com/slodge/MvvmCross/issues/37

As an alternative to the grid view, you could build your own Grid using some combination of vertical and horizontal LinearLayouts. You might do this by having an outer linearlayout like:

<Mvx.MvxBindableLinearLayout
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical"
    local:MvxItemTemplate="@layout/listitem_row"
    local:MvxBind="{'ItemsSource':{'Path':'Rows'}}"
  />

with the row template containing

<Mvx.MvxBindableLinearLayout
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal"
    local:MvxItemTemplate="@layout/listitem_cell"
    local:MvxBind="{'ItemsSource':{'Path':'Cells'}}"
  />

and the cell containing just the ImageView


Regardless of whether you use GridView or LinearLayouts, then you should be able to build a container ready for your individual ImageView squares.

Once this is ready then changing the image shown in each square should be very straight-forward - it should just be a case of binding an ImageView's AssetImagePath to a property on the ViewModel object representing the square.

ie. if your ViewModel is:

public class MyViewModel : MvxViewModel
{
    public List<GameRow> Rows {get;private set;}
}

where GameRow is:

public class GameRow : MvxNotifyProperty
{
    public List<GameSquare> Cells {get;private set;}
}

where GameSquare is:

public class GameSquare : MvxNotifyProperty
{
    private string _icon;
    public string Icon 
    {
       get { return _icon; }
       set { _icon = value; RaisePropertyChanged(() => Icon);
    }
}

then the binding on each ImageView display should just be something like:

 {'AssetImagePath':{'Path':'Icon'}}

Obviously you might not want to use icon paths directly in your ViewModel - you might prefer to use an enumeration instead. If you do this, then you might want to use a ValueConverter or a custom binding to display the correct image based on the current enumeration value.

See the question and answer in how to bind an image src to resource drawable image with Mvvmcross? for some more information.



回答2:

Now the example of BindableGridView is on https://github.com/slodge/MvvmCross/issues/37

Here is the solution we used to bind images on Buttons when the name of the file comes from a viewmodel:

1) Create a binding

public class MvxButtonIconBinding: MvxBaseAndroidTargetBinding
{
    private readonly View _view;

    public MvxButtonIconBinding(View view)
    {
        _view = view;
    }

    public override void SetValue(object value)
    {
        string path = System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal);
        path = Path.Combine(path, "pictures");
        path = Path.Combine(path, (string)value);
        if (File.Exists(path))
        {
            var dr = Drawable.CreateFromPath(path);
            Button b = _view as Button;
            var drawables = b.GetCompoundDrawables();
            foreach (var d in drawables)
                if (d!=null)
                    d.Dispose(); // To avoid "out of memory" messages
            b.SetCompoundDrawablesWithIntrinsicBounds(null, dr, null, null);
        }
        else
        {
            Console.WriteLine("File {0} does not exists", path);
        } 
    }

    public override MvxBindingMode DefaultMode
    {
        get { return MvxBindingMode.OneWay; }
    }

    public override Type TargetType
    {
        get { return typeof(string); }
    }

    protected override void Dispose(bool isDisposing)
    {
        if (isDisposing)
        {
        }
        base.Dispose(isDisposing);
    }
}

2) Setup the binding :

 registry.RegisterFactory(new MvxCustomBindingFactory<View>("ButtonIcon", view => new MvxButtonIconBinding(view)));

3) Use it in your gridview/listview:

    <ShopBazarAndroid.MvvmCross.MvxBindableGridView
        android:layout_width="0dp"
        android:layout_weight="8"
        android:layout_height="fill_parent"
        android:id="@+id/ArticleLayout"
        android:columnWidth="170dp"
        android:numColumns="auto_fit"
        android:verticalSpacing="5dp"
        android:horizontalSpacing="5dp"
        android:stretchMode="columnWidth"
        android:gravity="center"
        local:MvxItemTemplate="@layout/article_buttonlayout"
        local:MvxBind="{&apos;ItemsSource&apos;:{&apos;Path&apos;:&apos;VisualArticles&apos;}}" />

"article_buttonlayout" :

 <Button
    android:id="@+id/ButtonArticle"
    android:layout_width="fill_parent"
    android:layout_height="160dp"
    android:gravity="bottom|center"
    android:paddingBottom="5dp"
    android:paddingTop="5dp"
    local:MvxBind="{'Click':{'Path':'Command1'},'ButtonIcon':{'Path':'Item.PictureFileName'}}"
    android:textSize="14dip"
    android:textColor="@drawable/ToggleButtonSelector" />

Where "Item" is my object, and "PictureFileName", the filename on the local filesystem.
I'm new on C#, be indulgent if you find errors :)