2016-01-09 38 views
4

在MvvmCross应用程序中,我有一个包含经典聊天行为(WhatsApp like)的页面:此页面显示两个用户之间交换消息的历史记录,最后一条消息位于底部的名单。 我已经在Windows Phone 8.1中成功实现了这个视图,但我正在努力解决Android中的一个问题。MvvmCross,Mvx.MvxListView和MvxItemTemplate中的自定义绑定

我会给你一个简短的介绍和我的问题的描述,接下来我会通过技术细节。

介绍

其实,我需要的是不同的风格适用于不同的用户发送的消息:tipically对准其他用户发送左的消息和调整由我直接发送邮件(我这样做是通过weight属性);我需要应用不同的drawable背景并设置不同的gravity属性。 我使用自定义绑定,因为,AFAIK,这些属性不能绑定经典绑定:local:MvxBind="Gravity MyPropery"不起作用,因为没有Gravity属性。

所以,我有两个过程axml文件:

  • 第一个包含Mvx.MvxListView
  • 第二个包含的项目模板MvxListView

而且我已经创建了三种不同的自定义绑定(用于背景,重力和重量):

的问题

我想的是,当用户打开聊天视图,列表控件自动显示的最后一条消息。为了实现这一点,我以编程方式将列表滚动到最后一条消息,并且这个似乎是问题所在。

如果我不以编程方式滚动,当我打开页面并手动滚动到页面末尾时,所有自定义绑定都会成功应用:我可以看到右对齐和左对齐的消息,并且背景和权重均适用。

如果我以编程方式强制滚动,当我打开页面时,我看到一个奇怪的行为:所有消息都存在(经典绑定,如Text属性,已成功应用),但自定义绑定丢失。所有消息具有相同的背景,并全部左对齐。 但是,如果我手动上下滚动,自定义绑定将被处理并且消息以正确的样式显示。

调试分析

要调试,我已经把一个简单的静态计数器在自定义绑定程序来跟踪每一个功能处理时间的行为。

public class LinearLayoutWeightTargetBinding : MvxAndroidTargetBinding 
{ 
    public static int debugCounter = 0; 
    public LinearLayoutWeightTargetBinding(object target) : base(target) 
    { 
    } 

    protected LinearLayout MyTarget 
    { 
     get { return (LinearLayout)Target; } 
    } 

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

    protected override void SetValueImpl(object target, object value) 
    { 
     var ll = (LinearLayout)target; 
     var itsMe = (bool)value; 
     var weight = itsMe ? (float)20.0 : (float)5.0; 

     var layoutParams = new LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.WrapContent, weight); 
     ll.LayoutParameters = layoutParams; 
     Log.Debug("MeeCHAT", string.Format("LinearLayoutWeightTargetBinding::SetValueImpl::ItsMe:{0} - counter:{1}", itsMe, ++debugCounter)); 
    } 

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

通过这种方式,我看到其实由上下滚动的自定义绑定应用(debugCounter增加正确)。 但是当我以编程方式应用滚动时,只有前10个项目由自定义绑定处理,这似乎是我看到没有正确样式的消息的原因。因为我有一个很长的列表,所以只处理前10个项目,但它们不可见(它们不在可见区域内),并且可见项目未被处理。

技术细节

这里是一些与我的应用程序的技术方面有关的细节。我试图给你所有重要的方面。

组织的观点 通过遵循由Greg卸扣本文http://gregshackles.com/presenters-in-mvvmcross-navigating-android-with-fragments/中所描述的方法的我只有一个为应用程序一般Activity和一个Fragment每个View;然后通过Presenter可以激活右侧的ViewModel并管理导航的堆栈的

的查看,我有Mvx.MvxListView插件的片段

public class MyMatchersChatView : MvxFragment 
{ 
    public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) 
    { 
     var ignore = base.OnCreateView(inflater, container, savedInstanceState); 
     var result = this.BindingInflate(Resource.Layout.MyMatchersChatView, null); 

     var headerFrame = result.FindViewById<FrameLayout>(Resource.Id.headerFrameMyMatchersChatView); 
     var headerWidget = new HeaderWidget() { ViewModel = this.ViewModel }; 
     var tran = ChildFragmentManager.BeginTransaction(); 
     tran.Add(headerFrame.Id, headerWidget, "headerMyMatchersChat"); 
     tran.Commit(); 

     var listView = result.FindViewById<MvxListView>(Resource.Id.messagesList); 
     listView.SetSelection(listView.Adapter.Count - 1); // Scroll to the end of the list 
     return result; 
    }   
} 

声明listView.SetSelection(listView.Adapter.Count - 1);力在列表中滚动到最后。

最后两件事:自定义绑定如何注册以及如何应用于axml文件。

注册自定义绑定

在Setup.cs我有:

protected override void FillTargetFactories(IMvxTargetBindingFactoryRegistry registry) 
{ 
    base.FillTargetFactories(registry); 
    registry.RegisterFactory(new MvxCustomBindingFactory<LinearLayout>("CustomWeight", 
      (b) => new LinearLayoutWeightTargetBinding(b))); 
} 

应用自定义约束力

在我axml我有:

<LinearLayout 
    android:orientation="horizontal" 
    android:layout_width="0dp" 
    android:layout_height="wrap_content" 
    local:MvxBind="CustomWeight IsCurrentUser"> 

ListView和视图模型

这里的ListView

<Mvx.MvxListView 
    android:id="@+id/messagesList" 
    android:layout_width="fill_parent" 
    android:layout_height="fill_parent" 
    local:MvxBind="ItemsSource MyMessages" 
    local:MvxItemTemplate="@layout/mymatcherschatview_itemtemplate" /> 

的代码,并在视图模型

财产
private IEnumerable<MyMatchMessageModel> _myMessages; 
    public IEnumerable<MyMatchMessageModel> MyMessages 
    { 
     get { return _myMessages; } 
     set 
     { 
      _myMessages = value; 
      RaisePropertyChanged(() => MyMessages); 
     } 
    } 

环境 最后,这里是我的环境:

  • 的Visual Studio 2015年
  • MvvmCross 3.5.1
  • 核心目标:.NET框架4.5,Windows 8中,ASP.NET 5.0核心的Windows Phone 8.1,Xamarin.Android,Xamarin.iOS,Xamarin.iOS(经典)
  • Android应用程序目标是API级别19(Xamarin.Android v4.4支持)
  • Xamarin 3.11.1450。0
  • Xamarin.Android 5.1.6.7

有人能帮助我理解,如果我做错了什么? 感谢您的阅读和任何帮助!

>>编辑1 < <

我已通过添加stackFromBottomtranscriptMode属性,并通过在Fragment消除滚动以下编程获得自动滚动行为改变了我的布局,但问题依然存在:以看到消息以正确的风格我必须手动上下滚动(激活自定义绑定)

这是新axml ...

<Mvx.MvxListView 
    android:id="@+id/messagesList" 
    android:layout_width="fill_parent" 
    android:layout_height="fill_parent" 
    android:stackFromBottom="true" 
    android:transcriptMode="alwaysScroll" 
    local:MvxBind="ItemsSource MyMessages" 
    local:MvxItemTemplate="@layout/mymatcherschatview_itemtemplate" /> 

...在片段中的新代码

public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) 
{ 
    var ignore = base.OnCreateView(inflater, container, savedInstanceState); 
    var result = this.BindingInflate(Resource.Layout.MyMatchersChatView, null); 

    var headerFrame = result.FindViewById<FrameLayout>(Resource.Id.headerFrameMyMatchersChatView); 
    var headerWidget = new HeaderWidget() { ViewModel = this.ViewModel }; 
    var tran = ChildFragmentManager.BeginTransaction(); 
    tran.Add(headerFrame.Id, headerWidget, "headerMyMatchersChat"); 
    tran.Commit(); 

    return result; 
} 
+0

使用观察的集合,而不是IEnumerable的在您的视图模型 – xleon

+0

喜@ xleon,我已经试过你的建议,但没有任何变化。 请注意,一切工作正常与我目前的代码,如果我有一个列表少于10个项目 –

+0

它不是项目的数量,但有一个问题,您的自定义绑定不工作,直到项目被刷新我认为。我使用自定义mvxadapter而不是自定义绑定。没有问题 – xleon

回答

0

我会做的第一件事是确保您的自定义绑定总是获取调用。 在SetValueImpl()方法上设置一个断点,并检查它是否在有问题的项目上被调用。如果发生这种情况,那么问题依赖于没有得到任何理由更新的观点,你应该着手这方面的工作。如果它没有中断,你肯定会知道它是一个自定义绑定问题(可能是一个bug),在MvxAdapter

如果你发现它是第二个。我建议摆脱您的自定义绑定和创建自己的ChatListAdapter : MvxAdapter如下:

public class CoolChatListAdapter : MvxAdapter 
{ 
    public CoolChatListAdapter(Context context, IMvxAndroidBindingContext bindingContext) : base(context, bindingContext) 
    { 
    } 

    protected override View GetBindableView(View convertView, object source, int templateId) 
    { 
     var item = source as MyMatchMessageModel; 
     var weight = item.IsCurrentUser ? (float) 20.0 : (float) 5.0; 

     var ll = (LinearLayout) convertView; 
     var layoutParams = new LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.WrapContent, weight); 
     ll.LayoutParameters = layoutParams; 

     return base.GetBindableView(convertView, source, templateId); 
    } 
} 

然后,在你的Android设备上查看:

var adapter = new ChatListAdapter(this, (IMvxAndroidBindingContext)BindingContext); 
_chatList = FindViewById<MvxListView>(Resource.Id.chat_list_view); 
_chatList.Adapter = adapter;