2016-01-09 37 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;