Как внедрить пользовательский компонент многократного использования в Android MvvmCross 6.x - PullRequest
0 голосов
/ 08 ноября 2018

Проблема

Привет! Я хотел бы реализовать компонент пользовательского интерфейса с некоторой базовой логикой для Android в MvvmCross, чтобы его можно было многократно использовать в разных макетах. То, что мы делали до сих пор, - это использование BaseActivity с компонентами, которые будут повторно использоваться и наследоваться от него (например, для панели инструментов). Спустя некоторое время появилась необходимость применять разные панели инструментов в некоторых действиях, поэтому у нас было два варианта:

  • Поместите более одной панели инструментов внутри AppBarLayout в BaseActivity, и каждый контроллер активности, который наследуется от BaseActivity, установит видимость для панели инструментов, которую он захочет использовать (включая использование более одной панели инструментов, если необходимо).
  • Создайте другой родительский класс (что-то вроде BaseActivityWithOtherHeader) для каждой отдельной комбинации заголовков, и каждое действие будет наследоваться от соответствующей BaseActivity.

Мы выбрали первый подход, но ни один из них не мог бы обслуживаться в течение длительного времени по понятным причинам ...

Гипотеза

Итак, с моей точки зрения, лучший подход для этого - создать файл .axml, в котором я указываю компоновку компонента и (возможно) создать ViewController, в котором я применяю Fluent Binding к ViewModel, содержащему логику указанный компонент. Таким образом, логика является общей для разных платформ. Кроме того, если я не ошибаюсь, этот подход позволил бы моим коллегам включить его в нашу деятельность, просто используя тег <include> (и, возможно, просто объявление в ViewController действия вместо всей логики или что-то в этом роде). как этот), который соответствует парадигме «Не повторяй себя», обеспечивая более чистый код и ускоряя разработку.

Но я не смог найти ни примеров, ни рекомендаций по реализации повторно используемых компонентов с помощью MvvmCross, ни путем поиска в Google, ни в документации ...

Я нашел только этот вопрос , который относится к старой версии MvvmCross, и, по-видимому, больше нет класса IMvxLayoutInflater, а объект IMvxBindingContext не имеет BindingInflate() метод ...

Вопросы

Итак, у меня есть два вопроса по этому вопросу:

  • Есть ли лучший подход для этого, чем предложенный выше, учитывая, что я использую MvvmCross 6.2.1?
  • Не могли бы вы привести примеры создания любого настраиваемого компонента многократного использования с MvvmCross 6.x, будь то с помощью предложенного метода или другого, не упомянутого в первом разделе?

1 Ответ

0 голосов
/ 09 ноября 2018

Да, так вы говорите, что это сработает, и вы можете использовать их повторно, но примите во внимание, что из-за axml вы не сможете изменить привязки, которые у вас есть. Так что если у вас есть ViewModel, которую вы также используете повторно, у вас нет проблем.

В случае, если вы хотите изменить привязки элемента управления, вам нужно создать элемент управления (.cs), в котором вы программно добавляете внутренние представления или раздуваете свой аксел, выполняете логику и имеете свойства для привязки, чтобы Вы можете использовать этот элемент управления и связать его.

например:.

[Register("myNamespace.SwitchRightText")]
public class SwitchRightText : LinearLayout
{
    public SwitchRightText(Context context, IAttributeSet attrs, int defStyleAttr)
        : base(context, attrs, defStyleAttr)
    {
        this.Init(context, attrs, defStyleAttr);
    }

    public SwitchRightText(Context context, IAttributeSet attrs, int defStyleAttr, int defStyleRes)
        : base(context, attrs, defStyleAttr, defStyleRes)
    {
        this.Init(context, attrs, defStyleAttr, defStyleRes);
    }

    public SwitchRightText(Context context, IAttributeSet attrs)
        : base(context, attrs)
    {
        this.Init(context, attrs);
    }

    public SwitchRightText(Context context)
        : base(context)
    {
        this.Init(context);
    }

    public SwitchRightText(IntPtr javaReference, JniHandleOwnership transfer)
        : base(javaReference, transfer)
    {
    }

    // Controls
    public SwitchCompat Switch { get; set; }

    public TextView TextView { get; set; }

    private bool _toggleBackgrond;
    public bool ToggleBackground
    {
        get => this._toggleBackgrond;
        set
        {
            this._toggleBackgrond = value;
            this.UpdateBackground();
        }
    }

    // Methods
    private void Init(Context context = null, IAttributeSet attrs = null, int defStyleAttr = 0, int defStyleRes = 0)
    {
        if(IsInEditMode)
            return;

        this.Orientation = Orientation.Horizontal;

        this.InitializeSwitch(context, attrs);
        this.InitializeTextView(context, attrs);

        this.AddView(this.Switch);
        this.AddView(this.TextView);
    }

    private void InitializeSwitch(Context context, IAttributeSet attrs = null)
    {
        this.Switch = new SwitchCompat(context, attrs);
        this.Switch.ShowText = false;
    }

    private void InitializeTextView(Context context, IAttributeSet attrs = null)
    {
        this.TextView = new TextView(context, attrs);
    }

    private void UpdateBackground()
    {
        this.SetBackgroundColor(this.ToggleBackground ? Android.Graphics.Color.Black : Android.Graphics.Color.Yellow);
        this.Invalidate();
    }
}

Вам может понадобиться пользовательская привязка, например,

public class TextSwitchRightTextTargetBinding : MvxAndroidTargetBinding
{
    public TextSwitchRightTextTargetBinding(SwitchRightText switchRightText)
        : base(switchRightText)
    {
    }

    private SwitchRightText SwitchRightText => (SwitchRightText)this.Target;

    protected override void SetValueImpl(object target, object value)
    {
        this.SwitchRightText.TextView.Text = (string)value;
    }

    public override Type TargetType => typeof(string);

    public override MvxBindingMode DefaultMode => MvxBindingMode.TwoWay;

    public override void SubscribeToEvents()
    {
        base.SubscribeToEvents();

        this.SwitchRightText.TextView.TextChanged += TextView_TextChanged;
    }

    private void TextView_TextChanged(object sender, Android.Text.TextChangedEventArgs e)
    {
        this.FireValueChanged(this.SwitchRightText.TextView.Text);
    }

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

        if(isDisposing)
            this.SwitchRightText.TextView.TextChanged -= TextView_TextChanged;
    }
}

И зарегистрируйте его в настройках:

protected override void FillTargetFactories(IMvxTargetBindingFactoryRegistry registry)
{
    base.FillTargetFactories(registry);

    registry.RegisterFactory(new MvxCustomBindingFactory<SwitchRightText>("Text", srt => new TextSwitchRightTextTargetBinding(srt)));
}

Тогда в вашем .axml вы можете использовать его как:

<myNamespace.SwitchRightText
    ...
    mvxBind="Text MyTextInMyViewModel; ToggleBackground ToggleBackgroundInMyVM" />

В iOS вы можете сделать пользовательский UIViewController или пользовательский UIView, добавить его туда, где вы хотите его использовать, и выполнить привязки.


Другой способ - создать общий проект и выполнить всю логику компонента там

FFImageLoading делает это в элементе управления MvxCachedImageView

Выдержка из кода

#if __IOS__
    [Preserve(AllMembers = true)]
    [Register("MvxCachedImageView")]
#elif __ANDROID__
    [Preserve(AllMembers = true)]
    [Register("ffimageloading.cross.MvxCachedImageView")]
#endif
    /// <summary>
    /// MvxCachedImageView by Daniel Luberda
    /// </summary>
    public class MvxCachedImageView
#if __IOS__
        : UIImageView, ICachedImageView, INotifyPropertyChanged
#elif __ANDROID__
        : ImageViewAsync, ICachedImageView, INotifyPropertyChanged
#endif
...