2012-09-30 123 views
1

我有一個使用操作欄的選項卡功能託管多個片段的Activity。其中一個片段包含一個ListView。在選中此選項卡後,我想選擇某個項目。Android:在onResume中選擇ListView項目

要做到這一點編程,我用下面的代碼(如調用是ListView控件)

private void selectItem(int position) 
{ 
    long itemId = calls.GetItemIdAtPosition(position); 
    calls.PerformItemClick(calls, position, itemId); 
} 

如果ListView控件已經呈現,和我打電話這一點,沒有任何問題。但是,如果我從onResume調用它,那麼代碼將執行,但最終不會選擇任何內容。我認爲這是因爲在我調用selectItem的時候,ListView的所有項目都沒有被渲染。然而,如果我從後臺線程開始,睡幾百毫秒,然後運行相同的代碼(當然在ui線程中),一切都很好,但這是一個醜陋的黑客攻擊。

現在你可能想知道「爲什麼他不使用calls.setSelection」?事情是,我正在使用執行擴展的自定義佈局 - 所以我需要實際點擊我要選擇的項目(這反過來會觸發所選項目的佈局擴展)。但是,我可以直接調用PerformItemClick上執行的代碼,結果將相同(不執行佈局擴展)。

是不是有什麼辦法讓我趕上「ListView完成渲染所有可見項目」的時間點,然後在那一刻執行我的selectItem調用?在ASP.NET中,我在每個UI項目上都有一個事件,告訴我它何時完成渲染,因此我在那個時候進行了項目選擇,但是我沒有找到任何東西。

問候 斯蒂芬

下面是我使用

public class ActiveCallsAdapter: ObservableAdapter<Call> 
{ 

    public ActiveCallsAdapter(Activity activity, ObservableCollection<Call> calls) 
     : base(activity, calls) 
    { 
    } 

    public override View GetView(int position, View convertView, ViewGroup parent) 
    { 
     var item = items[position]; 
     var view = (convertView ?? context.LayoutInflater.Inflate(Resource.Layout.Call, parent, false)) as LinearLayout; 
     //View view = convertView; 
     //if (view == null) // no view to re-use, create new 
     // view = context.LayoutInflater.Inflate(Resource.Layout.Call, null); 

     SetTextView(view, Resource.Id.CallerName, item.CallerName); 
     SetTextView(view, Resource.Id.CallerNumber, item.CallerNumber); 
     SetTextView(view, Resource.Id.CallStatus, item.State.ToString()); 
     SetTextView(view, Resource.Id.CallDuration, item.Duration); 

     return view; 
    } 

    public void Update(LinearLayout view, Call item) 
    { 
     SetTextView(view, Resource.Id.CallerName, item.CallerName); 
     SetTextView(view, Resource.Id.CallerNumber, item.CallerNumber); 

     string identifier = "callState_" + item.State.ToString(); 
     int resourceId = Application.Context.Resources.GetIdentifier(identifier, "string", Application.Context.PackageName); 
     string callStateString = item.State.ToString(); 
     if (resourceId != 0) 
     { 
      try 
      { 
       callStateString = Application.Context.Resources.GetString(resourceId); 
      } 
      catch (Exception e) 
      { 
       AndroidLogModel.Model.AddLogMessage("ActiveCallsAdapter", "Unable to find call state string with resource id " + resourceId + " state string: " + identifier, 3); 
      } 
     } 
     SetTextView(view, Resource.Id.CallStatus, callStateString); 
     //SetTextView(view, Resource.Id.CallDuration, item.Duration); 
    } 

    public void UpdateDuration(LinearLayout view, Call item) 
    { 
     SetTextView(view, Resource.Id.CallDuration, item.Duration); 
    } 

} 

適配器和基類適配器

public class ObservableAdapter<T>: BaseAdapter<T> 
{ 

    protected readonly Activity context; 
    protected readonly ObservableCollection<T> items; 

    public ObservableAdapter(Activity context, ObservableCollection<T> collection) 
    { 
     this.context = context; 
     this.items = collection; 
     //this.collection.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(collection_CollectionChanged); 
     this.items.CollectionChanged += (sender, e) => NotifyDataSetChanged(); 
    } 

    void collection_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) 
    { 
     NotifyDataSetChanged(); 
    } 

    public override T this[int position] 
    { 
     get { return items[position]; } 
    } 

    public override int Count 
    { 
     get { return items.Count; } 
    } 

    public override long GetItemId(int position) 
    { 
     return position; 
    } 

    public override View GetView(int position, View convertView, ViewGroup parent) 
    { 
     var item = items[position]; 
     var view = (convertView ?? context.LayoutInflater.Inflate(Resource.Layout.Call, parent, false)) as LinearLayout; 
     // configure view here 
     return view; 
    } 

    protected void SetTextView(LinearLayout view, int id, string text) 
    { 
     var textView = view.FindViewById<TextView>(id); 
     if (textView != null) 
      textView.SetText(text, TextView.BufferType.Normal); 
    } 
} 
+0

如果你需要使用'PerformItemClick'(單聲道),我認爲你不能沒有延遲的行動。在hack類別中,你還可以在'ListView'上發佈一個'postn'發佈的'Runnable'(並在那裏調用'PerformItemClick')。如果我在您的位置,我只需更改適配器以使其具有擴展項目並直接調用的方法。 – Luksprog

+0

正如你從我不完整的代碼清理中猜到的,我正在使用Mono4Android。 – user1537915

+0

我不熟悉Mono for Android(所以可能有一些限制,我不知道),但我會改變適配器,就像我上面說的,而不是使用'performClick'方法。 – Luksprog

回答

2

我的單聲道的技能是有限的,所以我不知道如果我完全理解你的適配器,反正我已經適應了一些舊代碼,並提出,擴大單個項目時的點擊,也將在onResume移動ListView適配器到期望的位置:

private static class CustomAdapter extends BaseAdapter { 

     // the data 
     private ArrayList<String> mData; 

     // an int pointing to a position that has an expanded layout, 
     // for simplicity I assume that you expand only one item(otherwise use 
     // an array or list) 
     private int mExpandedPosition = -1; // -1 meaning no expanded item 
     private LayoutInflater mInflater; 

     public CustomAdapter(Context context, ArrayList<String> items) { 
      mInflater = LayoutInflater.from(context); 
      mData = items; 
     } 

     public void setExpandedPosition(int position) { 
      // if the position equals mExpandedPosition then we have a click on 
      // the same row so simply toggle the row to be gone again 
      if (position == mExpandedPosition) { 
       mExpandedPosition = -1; 
      } else { 
       // else change position of the row that was expanded 
       mExpandedPosition = position; 
      } 
      // notify the adapter 
      notifyDataSetChanged(); 
     } 

     @Override 
     public int getCount() { 
      return mData.size(); 
     } 

     @Override 
     public String getItem(int position) { 
      return mData.get(position); 
     } 

     @Override 
     public long getItemId(int position) { 
      return position; 
     } 

     @Override 
     public View getView(int position, View convertView, ViewGroup parent) { 
      if (convertView == null) { 
       convertView = mInflater.inflate(R.layout.ad_expandedelement, 
         parent, false); 
      } 
      ((TextView) convertView.findViewById(R.id.textView1)) 
        .setText(getItem(position)); 
      // see if there is an expanded position and if we are at that 
      // position 
      if (mExpandedPosition != -1 && mExpandedPosition == position) { 
       // if yes simply expand the layout 
       convertView.findViewById(R.id.button1).setVisibility(
         View.VISIBLE); 
      } else { 
       // this is required, we must revert any possible changes 
       // otherwise the recycling mechanism will hurt us 
       convertView.findViewById(R.id.button1).setVisibility(View.GONE); 
      } 
      return convertView; 
     } 

    } 

onListItemClick將簡單地:

@Override 
protected void onListItemClick(ListView l, View v, int position, long id) { 
    // set the expanded(or collapsed if it's a click on the same row that 
    // was previously expanded) row in the adapter 
    ((CustomAdapter) getListView().getAdapter()) 
      .setExpandedPosition(position); 
} 

onResume將有:

@Override 
protected void onResume() { 
    super.onResume(); 
    // set the position to the desired element 
    ((CustomAdapter) getListView().getAdapter()).setExpandedPosition(15); 
    // set the selection to that element so we can actually see it 
    // this isn't required but has the advantage that it will move the 
    // ListView to the desired 
    // position if not visible 
    getListView().setSelection(15); 
} 

R.layout.ad_expandedelement是一個簡單的垂直LinearLayoutTextView和初始隱藏(可見性設置爲離去)Button。對於此Button我將可見性更改爲模擬在ListView中展開/摺疊一行。你應該能夠理解我的代碼,如果你想我可以在github上發佈完整的示例。

+0

謝謝 - 我將不得不重寫我的片段代碼的大部分內容,但我已經成功實現了這種方法的基礎知識,並且它的工作就像一個魅力。 – user1537915

1

。雖然我不知道確切的當量爲C#/ Mono,Android框架在Activity上提供了一個名爲onWindowFocusChanged()的回調函數,表示期間與給定的Activity關聯的對用戶可見。在此之前,您可能有更好的運氣等待您的選擇方法,因爲ListView應該在那個時間點進行測量和佈局。在Java中,這將是這樣的:

@Override 
public void onWindowFocusChanged (boolean hasFocus) { 
    if (hasFocus) { 
     selectItem(position); 
    } 
} 

您可能需要有更多的邏輯在裏面,這個回調直接與窗口焦點關聯,而不是一個真正的生命週期方法。如果您正在顯示對話框或執行其他類似操作,我可以多次調用。

+0

哦,這就是所謂的相同,只是使用C#符號。但(我懷疑我應該把它放在原始問題中),活動託管片段標籤(使用操作欄)..所以可見性不直接耦合到已經呈現的列表視圖。實際上,我在onWindowFocusChanged時觸發了一次標籤活動加載(它從一個不同的片段開始,而不是具有listview的那個片段),然後如果我用ListView交換到標籤,我不會一起開啓onWindowFocusChanged 。 – user1537915