2016-11-02 15 views
0

視覚的に正しく動作しているように見えるListViewにはいくつか問題がありますが、項目をクリックすると間違っていることが明らかになります。 私は一般にXamarinとネイティブアプリの開発にかなり新しかったので、これは仕事で単純なルーキーミスである可能性があります。Xamarin Android ListView ViewHolderが奇妙な動作

ListViews + ViewHoldersの使用方法が間違っているか、何かを忘れてしまっているため、私は何か間違っていると確信しています。

おそらく、私の間違いは私たちのXamarin noobsが陥落する一般的な落とし穴なので、多分あなたの経験豊富な人が私の問題を引き起こしていることをすぐに教えてくれるかもしれません。私はProjectTaskと呼ばれるオブジェクトのリストを持っている

は、ここに私のシナリオです。このようなオブジェクトは、1レベルの深さではあるが、同様のリスト自体を保持することができます。したがって、TaskObj.Tasksは可能ですが、TaskObj.Tasks [0] .Tasksはそうではありません。

私は最初のリストビューにすべての親タスクを表示し、アイテムがクリックされると、そのタスクのサブタスクを表示する2番目のリストビューに切り替えます。

これは、最初のリストビューをスクロールするまで機能しているようです。それが完了すると、リストビューは正しい "見た目"ですが、アイテムをクリックすると、選択されている正しいアイテムではありません。

タスクが名前との両方がリストビュー項目に表示説明属性を持っている場合、私はアイテムをクリックしたときに、私はそれが実際にされている別の名前とアイテムであることがわかります同様2番目のリストビューを処理するアクティビティに送信されます。

何が起こっているかについて、この説明から考えている人はいますか?

実際には、すぐにたくさんのコードを投稿したくはありませんでしたが、私はすぐ後でお尋ねすることになるので、とにかくそれをやるつもりです。ここでは使用するクラスの簡略化されたバージョンです重要でないものは削除されました。

ViewHolderクラス

public class ViewHolderProjectTaskExtended : Java.Lang.Object 
{ 
    public Button btnStop { get; set; } 
    public Button btnStart { get; set; } 
    public TextView tvName { get; set; } 
    public CheckBox is_started { get; set; } 
    public TextView task_id { get; set; } 

    public ViewHolderProjectTaskExtended() 
    { 
    } 
} 

ListAdapterクラス

public class ProjectTaskExtendedListAdapter : BaseAdapter<ProjectTask> 
{ 
    List<ProjectTask> _items; 
    Activity _context; 

    public ProjectTaskExtendedListAdapter(Activity context, List<ProjectTask> tasks) 
    { 
     _items = tasks; 
     _context = context; 
    } 

    public override ProjectTask 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]; 
     ViewHolderProjectTaskExtended viewHolder = null; 
     View view = convertView; 

     if (view != null) 
     { 
      viewHolder = view.Tag as ViewHolderProjectTaskExtended; 
     } 

     #region viewHolder doesn't exist 
     if (viewHolder == null) 
     { 
      view = this._context.LayoutInflater.Inflate(Resource.Layout.ListItem_SalesOrderExtended, null); 

      viewHolder = new ViewHolderSalesOrderExtended(); 

      viewHolder.tvName = view.FindViewById<TextView>(Resource.Id.tv_salesorder_text); 
      viewHolder.btnStop = view.FindViewById<Button>(Resource.Id.btn_stop_session); 
      viewHolder.btnStart = view.FindViewById<Button>(Resource.Id.btn_start_session); 

      viewHolder.is_started = view.FindViewById<CheckBox>(Resource.Id.chb_is_started); 
      viewHolder.task_id = view.FindViewById<TextView>(Resource.Id.tv_task_id); 

      view.Tag = viewHolder; 

      viewHolder.tvName.Text = "(" + item.name + ")" + Environment.NewLine + item.description; 

      viewHolder.task_id.Text = item.id; 

      viewHolder.btnStart.Tag = item.id; 
      viewHolder.btnStop.Tag = item.id; 

      if (item.tasks.Count > 0) // has sub tasks 
      { 
       viewHolder.has_children.Checked = true; 
       viewHolder.btnStop.Visibility = ViewStates.Gone; 
       viewHolder.btnStart.Visibility = ViewStates.Invisible; 

       viewHolder.tvName.Click += (sender, e) => 
       { 
        Toast.MakeText(_context, "Select sub task for " + item.name + "", ToastLength.Short).Show(); 

        var ident_select_sub_task = new Intent(_context, typeof(SelectSubTaskActivity)); 

        ident_select_sub_task.PutExtra("pt_parent", JsonConvert.SerializeObject(item)); 

        _context.StartActivity(ident_select_sub_task); 
       }; 
      } 
      else // has no sub tasks 
      { 
       if (viewHolder.is_started.Checked == false) 
       { 
        viewHolder.btnStart.Visibility = ViewStates.Visible; 
        viewHolder.btnStop.Visibility = ViewStates.Gone; 
       } 
       else 
       { 
        viewHolder.btnStart.Visibility = ViewStates.Gone; 
        viewHolder.btnStop.Visibility = ViewStates.Visible; 
       } 

       viewHolder.btnStart.Click += (sender, e) => 
       { 
        Toast.MakeText(_context, "Task " + item.name + " is starting", ToastLength.Short).Show(); 

        // code dealing with starting a task 
       }; 

       viewHolder.btnStop.Click += (sender, e) => 
       { 
        Toast.MakeText(_context, "Task " + item.name + " is stopping", ToastLength.Short).Show(); 

        // code dealing with stopping a task 
       }; 
      } 
     } 
     #endregion 

     #region viewHolder exists (reuse) 
     else 
     { 
      viewHolder.tvName.Text = "(" + item.name + ")" + Environment.NewLine + item.description; 

      viewHolder.task_id.Text = item.id; 

      viewHolder.btnStart.Tag = item.id; 
      viewHolder.btnStop.Tag = item.id; 

      if (item.tasks.Count > 0) // has sub tasks 
      { 
       viewHolder.btnStart.Visibility = ViewStates.Invisible; 
       viewHolder.btnStop.Visibility = ViewStates.Gone; 
      } 
      else // has no sub tasks 
      { 
       if (viewHolder.is_started.Checked == false) 
       { 
        viewHolder.btnStart.Visibility = ViewStates.Visible; 
        viewHolder.btnStop.Visibility = ViewStates.Gone; 
       } 
       else 
       { 
        viewHolder.btnStart.Visibility = ViewStates.Gone; 
        viewHolder.btnStop.Visibility = ViewStates.Visible; 
       } 
      } 
     } 
     #endregion 

     return view; 
    } 
} 

EDIT

[OK]を、私はあなたの例InitLiptonし、それに従うことを今、私のListAdapterを変更しようとしましたときに動作するようですこのようにしてください。 インデックスにアイテムを渡すことに反対するタグの実際のアイテムを渡し、そのアイテムをインデックスで取得することに失敗した場合、なぜ失敗するのか - リストビューがスクロールされたときに間違ってしまうメカニズム?

ListViewAdapterクラス(again 2)をもう一度更新しました。不要なものは削除されています。

public class ProjectTaskExtendedListAdapter : BaseAdapter<ProjectTask> 
{ 
    List<ProjectTask> _items; 
    Activity _context; 

    public ProjectTaskExtendedListAdapter(Activity context, List<ProjectTask> tasks) 
    { 
     _items = tasks; 
     _context = context; 
    } 

    public override ProjectTask 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) 
    { 
     ViewHolderProjectTaskExtended viewHolder = null; 
     View view = convertView; 

     if (viewHolder == null) 
     { 
      view = this._context.LayoutInflater.Inflate(Resource.Layout.ListItem_SalesOrderExtended, null); 

      viewHolder = new ViewHolderSalesOrderExtended(); 

      viewHolder.tvName = view.FindViewById<TextView>(Resource.Id.tv_salesorder_text); 
      viewHolder.btnStop = view.FindViewById<Button>(Resource.Id.btn_stop_session); 
      viewHolder.btnStart = view.FindViewById<Button>(Resource.Id.btn_start_session); 

      viewHolder.is_started = view.FindViewById<CheckBox>(Resource.Id.chb_is_started); 
      viewHolder.has_children = view.FindViewById<CheckBox>(Resource.Id.chb_has_children); 
      viewHolder.task_id = view.FindViewById<TextView>(Resource.Id.tv_task_id); 

      viewHolder.tvName.Click += (sender, e) => itemClicked(viewHolder.tvName);    
     } 
     else 
     { 
      viewHolder = (ViewHolderProjectTaskExtended)view.Tag; 
     } 

     var item = _items[position]; 

     viewHolder.tvName.Text = "(" + item.name + ")" + Environment.NewLine + item.description; 
     viewHolder.tvName.Tag = position; 

     if (item.tasks.Count > 0) 
     { 
      viewHolder.btnStart.Visibility = ViewStates.Invisible; 
      viewHolder.btnStop.Visibility = ViewStates.Gone; 
     } 
     else 
     { 
      if (viewHolder.is_started.Checked == false) 
      { 
       viewHolder.btnStart.Visibility = ViewStates.Visible; 
       viewHolder.btnStop.Visibility = ViewStates.Gone; 
      } 
      else 
      { 
       viewHolder.btnStart.Visibility = ViewStates.Gone; 
       viewHolder.btnStop.Visibility = ViewStates.Visible; 
      } 
     } 

     return view; 
    } 

    private void itemClicked(object sender) 
    { 
     var tv = sender as TextView; 

     var position = (int)tv.Tag; 
     var _item = _items[position]; 

     Toast.MakeText(_context, "Select sub task for " + _item.name + "", ToastLength.Short).Show(); 

     var ident_select_sub_task = new Intent(_context, typeof(SelectSubTaskActivity)); 

     ident_select_sub_task.PutExtra("pt_parent", JsonConvert.SerializeObject(_item)); 

     _context.StartActivity(ident_select_sub_task); 
    } 
} 

答えて

1

作成するTextviewのタグには、アイテムの位置を使用します。次に、それをアイテムのリストにインデックスとして戻すことができます

これは以前に行ったアダプタですが、CheckBoxを見てください。それがobjとしてSetCheckedに入ると、それをチェックボックスに戻して解析することができます。次に、リスト内の項目の位置であるタグinvを取得します。

public override View GetView(int position, View convertView, ViewGroup parent) 
    { 
     ViewHolder holder; 

     if (convertView == null) 
     { 
      convertView = _activity.LayoutInflater.Inflate(Resource.Layout.CarItem, parent, false); 

      holder = new ViewHolder 
      { 
       CheckBox = convertView.FindViewById<CheckBox>(Resource.Id.CheckBoxActiveItem), 
       Title = convertView.FindViewById<TextView>(Resource.Id.Title), 
      }; 

      convertView.Tag = holder; 
      convertView.SetTag(Resource.Id.CheckBoxActiveItem, holder.CheckBox); 
      convertView.SetTag(Resource.Id.Title, holder.Title); 
     } 
     else 
     { 
      holder = (ViewHolder)convertView.Tag; 
     } 


     var item = _items[position]; 
     holder.Title.Text = item .DisplayName; 
     holder.CheckBox.Checked = item .IsDefault; 
     holder.CheckBox.Click += (sender, args) => SetChecked(holder.CheckBox.Checked, sender); 
     holder.CheckBox.Tag = position; 


     return convertView; 
    } 


    private void SetChecked(bool isChecked, object sender) 
    { 

     var box = sender as CheckBox; 

     //Now you have the Item that has been selected, regardless of the scroll 
     var position = (int)box.Tag; 
     var ccItem = _items[position]; 
    } 
関連する問題