2016-11-02 5 views
0

시각적으로 올바르게 작동하는 것으로 보이는 ListView에 문제가 있습니다. 그러나 항목을 클릭하면 항목이 잘못 되었음이 드러납니다. 저는 Xamarin과 네이티브 앱 개발에 익숙합니다. 그래서 이것은 단순한 신참 실수 일뿐입니다.Xamarin Android ListView ViewHolder 이상한 동작

ListViews + ViewHolders를 사용하는 방법이나 내가 뭔가를 잊어 버린 경우와 관련하여 일반적으로 잘못된 것을하고 있기 때문에 확신 할 수 없습니다.

내 실수는 Xamarin noobs가 빠지는 일반적인 함정입니다. 어쩌면 여러분 중 일부는 더 많은 경험이있는 사람들이 내 문제의 원인을 바로 알 수 있습니다. 내가 ProjectTask라는 객체의 목록을

:

여기 내 시나리오입니다. 이러한 객체는 비슷한 수준의 목록을 보유 할 수 있지만 깊이는 1 단계입니다. 따라서 TaskObj.Tasks는 가능하지만 TaskObj.Tasks [0] .Tasks는 불가능합니다.

그래서 첫 번째 목록보기에 모든 상위 작업을 표시하고 항목을 클릭하면 해당 작업의 하위 작업을 표시하는 두 번째 목록보기로 전환합니다.

이것은 첫 번째 목록보기를 스크롤하기 전까지 작동하는 것으로 보입니다. 완료되면 목록보기는 여전히 "보이게"되지만 항목을 클릭하면 올바른 항목이 선택되지 않습니다.

작업이 모두 목록보기 항목에 표시 이름설명 속성을 가지고 있다면, 나는 그것을이되고 실제로 다른 이름와 아이템이라고 볼 수있는 항목을 클릭 할 때처럼 두 번째 listview를 처리하는 활동으로 전송됩니다.

누가이 일이 벌어지고 있는지에 대한 설명이 있습니까?

사실 나는 많은 코드를 즉시 게시하고 싶지는 않지만 어쨌든 나는 나중에 더 빨리 요청할 것입니다. 사용중인 클래스의 단순화 된 버전입니다. 사소한 것들이 제거되었습니다.

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; 
    } 
} 

편집

좋아, 나는 당신의 예를 InitLipton과 그 뒤에 이어지는 지금 내 ListAdapter을 변경 시도 언제 일하는 것 같아. 이런 식으로. 태그에 실제 항목을 전달하면 항목에 색인을 전달하고 해당 항목을 색인으로 검색하는 것과 반대되는 이유가 없습니다. 목록보기가 스크롤되었을 때 잘못 표시되는 방법은 무엇입니까? ?

업데이트 된 ListViewAdapter 클래스 (다시 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]; 
    }