2

To help with this discussion, I have published all relevant source-code on GitHub... https://github.com/WindSpirit/DroidCustomView

Basically, I wrote an Android Custom-View (Asgl.Android.Views.RatingView), that inherits from TableLayout and contains several ImageView's that get changed by public get/set properties (via MvvmCross bindings and/or AXML property attributes).

This Custom-View is used within an MvxList, MvxItemTemplate.

Here is what the MvxItemTemplate AXML code looks like...

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:local="http://schemas.android.com/apk/res-auto"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical">
    <TextView
        android:id="@+id/task_description"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        local:MvxBind="Text RatingValue" />
    <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        local:MvxBind="Text ScaleValue" />
    <Asgl.Android.Views.RatingView
        android:id="@+id/rating"
        android:layout_margin="20dp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        local:MvxBind="Rating RatingValue; Scale ScaleValue" />
</LinearLayout>

The TextView controls display data via MvvmCross bindings as expected, but the Custom-View control does not display correctly.

Based on "N-28-CustomBinding", I customize the look-and-feel of the Custom-View when Public Property setter methods are called on the View object. See also http://slodge.blogspot.ca/2013/06/n28-custom-bindings-n1-days-of-mvvmcross.html

Everything works as it should, except all of the initial MvxItemTemplate items are displayed exactly the same, even when they should appear differently. It is as though a cached image of the Custom-View is used for every list item, even though the list item properties differ in value.

Within the Custom-View, this.Parent() is never available and calling Custom-View methods like this.Invalidate() or this.InvalidateDrawable( this.myDrawable.Drawable ), don't appear to have any effect.

In other words, simply executing the following code will not necessarily result in a visual list item change during the construction of the MvxList...

public int? Rating
{
  get { return _rating; }

  set {

    if ((value != null) && (value < -2 || value > 5))
      throw new ArgumentOutOfRangeException();

    if (_rating != value)
    {
      _rating = value;
      _isUpdating = true;
      try
      {
        var testValue = Scale ?? 0;

        Rating0.Visibility = (testValue > 0) ?
          ViewStates.Visible : ViewStates.Gone;

        Rating1.Visibility = (testValue > 0) ?
          ViewStates.Visible : ViewStates.Gone;
        Rating2.Visibility = (testValue > 2) ?
          ViewStates.Visible : ViewStates.Gone;
        Rating3.Visibility = (testValue > 3) ?
          ViewStates.Visible : ViewStates.Gone;

        Rating4.Visibility = (testValue > 4) ?
          ViewStates.Visible : ViewStates.Gone;

        ActnGood.SetColorFilter((Rating > 0) ?
          ColourGood : ColourDisabled, PorterDuff.Mode.SrcIn);

        Rating0.SetImageDrawable(ActnGood);

        ActnBad.SetColorFilter((Rating > 0) ?
          ColourDisabled : ColourBad, PorterDuff.Mode.SrcIn);

        Rating1.SetImageDrawable(ActnBad);

        this.Invalidate();
        // etc...

        if (RatingChanged != null)
          RatingChanged(this, EventArgs.Empty);
      } finally {
        _isUpdating = false;
      }
    }
  }
}
public event EventHandler RatingChanged;

QUESTION: What do I need to do to have MvxList redraw one of its list items when the list items property value changes due to an MvvmCross binding?

WindSpirit
  • 181
  • 1
  • 7

3 Answers3

1

Whew! I finally have a solution and will publish it as a revision to my GitHub project noted in the vary first line of this thread.

I arrived at this solution by examining the GroupView handle to the DrawChild View handle, and then considered... "what-if I do the same things to the DrawChild View parameter as I had done to the GroupView"? Would it draw the correct information on the screen?

The draft solution looks more or less like this...

    protected override bool DrawChild(Canvas canvas, View child, long drawingTime)
    {
        System.Diagnostics.Debug.Print(string.Format("this.Handle = {0} Scale = {1} Rating = {2}", Handle, Scale, Rating));

        InitControls(child);
        Refresh();

        return base.DrawChild(canvas, child, drawingTime);
    }

Yes! Updating the GroupView (child) that is passed into DrawChild does the trick!

WindSpirit
  • 181
  • 1
  • 7
0

I opened your sample project

Try to update first to the latest MvvmCross.

It doesn't make sense to work with a lower version

WriteEatSleepRepeat
  • 3,083
  • 3
  • 33
  • 56
  • Thank you for bring the need to update MvvmCross to my attention. I have done this. However, I should mention that the MvvmCross bindings appear to be working. The issue appears to be the updating of the Canvas. That is to say (ViewModel appears to be okay) => (View appears to be okay) => (Android Canvas appears to be using a cached image) – WindSpirit Nov 07 '14 at 15:53
  • that's not releated to MvvMcross bindings. the issues you are having are related to how you update those ImageView's – WriteEatSleepRepeat Nov 07 '14 at 16:22
  • the binding seems to work fine, as you said, the values are different – WriteEatSleepRepeat Nov 07 '14 at 16:24
  • 1
    I recommend you change the way you do the display of that control. instead of using codebehind to change ImageViews, you could use MVVM techniques like converters. You are mixing UI code with logic in my opinion – WriteEatSleepRepeat Nov 07 '14 at 16:25
  • You should have only one ImageView for good\bad which has drawable value bound to a good\bad enum (use a converter). Also, you should also make a separate control just for rating which has a Rating property value and bind to that. – WriteEatSleepRepeat Nov 07 '14 at 16:29
  • why not use Android's RatingBar control instead of creating your own? you can change the default look, see here: http://stackoverflow.com/questions/607610/how-to-change-the-star-images-of-the-ratingbar – WriteEatSleepRepeat Nov 07 '14 at 16:38
  • Android's RatingBar is inherited from the Progress Bar. Even if the multi-purpose behaviour were removed, the thumbs-up and thumbs-down component would still have this issue. Lots of arguments could be made for various approaches, and it looks like a change in the approach used may bring about a quicker resolution. That said, it is also like saying "I don't understand why the current approach fails, so let's try something different (that we know will work)." – WindSpirit Nov 07 '14 at 19:37
  • Oh really? You expected me to find the errors in your spaghetti code? It's not even related to MvvmCross. Carry on the good work, sorry to disappoint you, won't happen again. I'm glad you found the exact reason and fix for the issue. – WriteEatSleepRepeat Nov 07 '14 at 23:14
  • Are snide remarks welcome here? Regardless, and more on point. Perhaps it is related to MvvmCross, in as much as the issue may be related to MvxList. I have not looked and the MvxList implementation as of yet. The problem is that there are extra creation of MvxList items and their synchronization with MvvmCross bindings does not occur automatically between their first creation and first rendering on canvas. Everything works fine after the MvxList items are rendered in the list for the first time. Perhaps the issue is outside MvvmCross, but I thought this is a place that one seeks help. – WindSpirit Nov 09 '14 at 05:22
  • Andrei, why bother commenting if you are going to be so hostile? Your comments were certainly of no value (in my opinion) and I did find a solution without them. The world would be such a nicer place if people would not be so rude, unkind, and referring to other peoples work in a negative ways such as "spaghetti code". I don't think it is and well others can form their own opinion about it or do you think they are not bright enough to recognize poor coding on their own? – WindSpirit Nov 09 '14 at 05:36
  • BTW: The version of MvvmCross that I was using was only one release behind. Sorry for not being able to update production code on a daily basis. This type of banter is of no use to anyone and I hope that you can see this. Why did I have to be the one to point out this :( – WindSpirit Nov 09 '14 at 05:45
0

Overriding the DrawChild method and synchronizing its visual settings with the event handler's instance settings was the only solution to reveal itself thus far.

This Custom View (Android View Group) would appear to be the correct approach, although the Listeners were registered in the wrong place (they should be registered in the OnAttachedToWindows() and removed in the OnDetachedFromWindow()).

This YouTube.com video reaffirms my belief in the approach taken

Google I/O 2013 - Writing Custom Views for Android https://www.youtube.com/watch?v=NYtB6mlu7vA#t=1286

However, I sincerely believe that I am missing something that would make the DrawChild logic far more efficient.

WindSpirit
  • 181
  • 1
  • 7