3

У меня есть ListView в Fragment и я хочу обновить данные в ListView, когда я вернусь из другого Activity. Я перезаписал метод onResume() в Fragment, изменил данные в Adapter и позвонил notifyDataSetChanged() на Adpater, но почему-то ListView не обновляется. Я подозреваю, что с моим Adapter что-то не так, но я просто не могу найти ошибку.Как правильно реализовать адаптер для ListView

Вот код моего Adpater:

class ManualExceptionsListAdapter extends BaseAdapter { 

    private LayoutInflater mInflater; 
    private TextView mManualExceptions; 
    SwitchCompat mSwitch; 
    TextView name; 
    final Context context = getActivity(); 
    final SharedPreferences mSharedPreferences = PreferenceManager.getDefaultSharedPreferences(context); 
    int a; 
    int ifUse = 0; 

    ManualExceptionsListAdapter(LayoutInflater inflater) { 
     mInflater = inflater; 
    } 

    @Override 
    public int getCount() { 
     return (mPermanentManualException.size()+mContactsExceptionNumber.size()); 
    } 

    @Override 
    public Object getItem(int i) { 
     return null; 
    } 

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

    @Override 
    public int getItemViewType(int position) { 
     if (position < (mContactsExceptionNumber.size())) { 
      a = 0; 
      if(position == (mContactsExceptionNumber.size()-1)){ 
       ifUse = 1; 
      } 
      return a; 
     } else { 
      a = 1; 
      return a; 
     } 
    } 

    @Override 
    public int getViewTypeCount() { 
     return 2; 
    } 

    @Override 
    public void notifyDataSetChanged() { 
     super.notifyDataSetChanged(); 
    } 

    @Override 
    public View getView(int i, View view, ViewGroup viewGroup) { 
     final int pos; 
     if(mContactsExceptionNumber.size()>0) { 
      pos = i - (mContactsExceptionNumber.size()); 
     }else{ 
      pos = 0; 
     } 
     int pos2 = 0; 
     int type = getItemViewType(i); 
     if(ifUse == 1){ 
      if(mContactsExceptionNumber.size()>0) { 
       pos2 = i - (mContactsExceptionNumber.size()); 
       Exceptions.index = pos2; 
      } 
     } 
     View v = view; 
     if (view == null) { 
      switch (type) { 
       case 0: 
        v = mInflater.inflate(R.layout.contacts_exception_row, null); 
        name = (TextView) v.findViewById(R.id.contact_name); 
        name.setText(mContactsExceptionNames.get(i)); 
        break; 
       case 1: 
        v = mInflater.inflate(R.layout.manual_exception_row, null); 
        mManualExceptions = (TextView) v.findViewById(R.id.manual_exception_number); 
        mSwitch = (SwitchCompat) v.findViewById(R.id.manual_exception_switch); 
        mManualExceptions.setText(mPermanentManualException.get(pos2)); 
        mSwitch.setTag(i); 
        try { 
         if (mManualExceptionList.contains(mPermanentManualException.get(pos2))) { 
          mSwitch.setChecked(true); 
         } 
        } catch (Exception e) { 
         e.printStackTrace(); 
        } 
        break; 
      } 
     }else{ 
      switch (type) { 
       case 0: 
        v = mInflater.inflate(R.layout.contacts_exception_row, null); 
        name = (TextView) v.findViewById(R.id.contact_name); 
        name.setText(mContactsExceptionNames.get(i)); 
        break; 
       case 1: 
        v = mInflater.inflate(R.layout.manual_exception_row, null); 
        mManualExceptions = (TextView) v.findViewById(R.id.manual_exception_number); 
        mSwitch = (SwitchCompat) v.findViewById(R.id.manual_exception_switch); 
        mManualExceptions.setText(mPermanentManualException.get(pos2)); 
        mSwitch.setTag(i); 
        try { 
         if (mManualExceptionList.contains(mPermanentManualException.get(pos2))) { 
          mSwitch.setChecked(true); 
         } 
        } catch (Exception e) { 
         e.printStackTrace(); 
        } 
        break; 
      } 
     } 

     try { 
      mSwitch.setOnTouchListener(new View.OnTouchListener() { 
       @Override 
       public boolean onTouch(View view, MotionEvent motionEvent) { 
        isTouched = true; 
        return false; 
       } 
      }); 
     } catch (NullPointerException e) { 
      e.printStackTrace(); 
     } 
     try { 
      mSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { 
       @Override 
       public void onCheckedChanged(CompoundButton compoundButton, boolean b) { 
        if (isTouched) { 
         if (b) { 
          if (!mManualExceptionList.contains((mPermanentManualException.get(pos)))) { 
           mManualExceptionList.add((mPermanentManualException.get(pos))); 
          } 
          mSharedPreferences.edit().putString("ManualExceptions", TextUtils. 
            join(",", mManualExceptionList)).apply(); 

         } else { 
          try { 
           mManualExceptionList.remove((mPermanentManualException.get(pos))); 
           mSharedPreferences.edit().putString("ManualExceptions", TextUtils. 
             join(",", mManualExceptionList)).apply(); 
          } catch (Exception e) { 
           e.printStackTrace(); 
          } 
         } 
         Log.d("RejectCall", "Permanent " + TextUtils.join(",", mPermanentManualException)); 
         Log.d("RejectCall", TextUtils.join(",", mManualExceptionList)); 
        } 
       } 
      }); 
     } catch (NullPointerException e) { 
      e.printStackTrace(); 
     } 

     return v; 
    } 

} 
+0

'onResume()' вызывается при возврате к предыдущему 'Activity' /' Fragment'. –

+0

@XaverKapeller Это здорово. Благодарю. В любом случае, я вызвал notifydatasetchanged withing onResume, но listview не будет обновляться. – Slay

+0

Вы переопределяете 'onResume()' одного из 'Фрагментов' внутри' ViewPager'? Потому что это не сработает. Переопределите 'onResume()' в 'Fragment' или' Activity', который содержит 'ViewPager'. –

ответ

8

Есть несколько вопросов с вами Adapter реализации. Слишком много для меня, чтобы дать вам советы о том, как исправить это. Я просто объясню, как вы можете эффективно реализовать Adapter, и затем вы можете применить его к своему Adapter.

Достаточно сказать, что вы должны оптимально переключиться на использование нового RecyclerView, который имеет значительные улучшения по сравнению с старыми ListView. Вы можете найти документацию RecyclerViewhere и руководство Googles о том, как его использовать here.


Если вы хотите, чтобы отобразить данные в ListView вы должны сначала создать макет для каждого из элементов в ListView. Для этого примера я буду использовать этот макет:

<?xml version="1.0" encoding="utf-8"?> 
<RelativeLayout 
    xmlns:android="http://schemas.android.com/apk/res/android" 
    android:layout_width="match_parent" 
    android:layout_height="wrap_content" 
    android:padding="12dp"> 

    <CheckBox 
     android:id="@+id/checkbox" 
     android:layout_width="match_parent" 
     android:layout_height="wrap_content"/> 

</RelativeLayout> 

Чтобы связать данные, которые мы хотим отобразить в каждом элементе ListView мы пишем новый класс, который содержит только данные в частных полях и добытчиками и сеттеров, чтобы получить и установите эти данные. Такие классы обычно называют моделями взглядов. Модель вид на макете, как выше может выглядеть примерно так:

public class ExampleViewModel { 

    // This is the text which will be set to the CheckBox 
    private String text; 

    // This boolean will be used to save the checked state of the CheckBox 
    private boolean checked; 

    public String getText() { 
     return text; 
    } 

    public void setText(String text) { 
     this.text = text; 
    } 

    public boolean isChecked() { 
     return checked; 
    } 

    public void setChecked(boolean checked) { 
     this.checked = checked; 
    } 
} 

Каждый экземпляр такой модели представления будет представлять собой один элемент в ListView. Когда один из предметов входит в видимую область ListView, он должен быть привязан к View в ListView (Это то, что мы должны реализовать в getView() of Adapter). Пока элемент виден, модель останется связанной с этим View, но как только View выйдет из видимой области ListView, она будет переработана и привязана к другой модели вида, которая просто входит в видимую область. Это называется рециркуляцией вида, и это делается для сведения к минимуму объема памяти в ListView и для увеличения общей производительности прокрутки и текучести. Views - очень дорогие предметы, особенно надувающие Views и findViewById(), стоят очень высокой производительности, и основная точка зрения на переработку заключается в том, что вам нужно раздувать только небольшое количество Views, которое затем можно использовать повторно, и поэтому вы избегаете дорогого надувания и findViewById() позже.

Большинство из того, что я объяснял выше, происходит автоматически. То, что вы, как разработчик, должно сделать, это надуть правильные Views в getView() или повторно использовать их, если их уже есть, а затем привязать правильную модель просмотра к View. Я знаю, что большая часть этого кажется довольно сложной и запутанной, если вы впервые узнаете об этом, но она становится намного проще и понятнее, когда мы начнем смотреть на код.

Итак, теперь у нас есть макет элементов вида в ListView и модель представления, чтобы согласиться с этим. Теперь нам нужно написать другой класс, который обычно называют держателями. Эти держатели обзора по существу представляют собой классы контейнеров вокруг представлений в ListView. Каждый держатель вида содержит один View, связанный с товаром в ListView, и они также заботятся о привязке данных модели вида к View. Без дальнейших церемоний здесь есть держатель для просмотра, который следует вместе с моделью обзора сверху:

public class ExampleViewHolder { 

    // The reference to the CheckBox is saved so we only have to perform the findViewById() once. 
    private final CheckBox checkBox; 

    // A reference to the view model which is currently bound to this view holder 
    private ExampleViewModel currentModel; 

    // The View associated with this view holder is passed into the constructor from the Adapter. 
    public ExampleViewHolder(View view) { 

     // And here we look for all relevant views 
     // In our case we just need the CheckBox 
     this.checkBox = (CheckBox) view.findViewById(R.id.checkbox); 
    } 

    public void bind(ExampleViewModel viewModel) { 

     // Unset the listener in case there was one from a previous view model 
     this.checkBox.setOnCheckedChangeListener(null); 

     // Save a reference to the view model which is currently bound to this view holder 
     this.currentModel = viewModel; 

     // Bind the data to the CheckBox 
     this.checkBox.setText(viewModel.getText()); 
     this.checkBox.setChecked(viewModel.isChecked()); 

     // Reset the listener 
     this.checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { 
      @Override 
      public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { 
       currentModel.setChecked(isChecked); 
      } 
     }); 
    } 
} 

Теперь мы почти закончили. Единственное, что в настоящее время отсутствует, чтобы подключить все это вместе в Adapter:

public class ExampleAdapter extends BaseAdapter { 

    // Each type of view in the `ListView` gets its own id 
    // In this example we only have one type of View so we only need one id 
    private static final int EXAMPLE_VIEW_ID = 0; 

    // The default view id is just a fallback 
    private static final int DEFAULT_VIEW_ID = EXAMPLE_VIEW_ID; 

    private final LayoutInflater inflater; 
    private List<ExampleViewModel> viewModels; 

    public ExampleAdapter(Context context, List<ExampleViewModel> viewModels) { 

     // The view models are initially passed in through the constructor. 
     // You can pass an empty list into the Adapter if there is not data initially. 
     this.viewModels = viewModels; 
     this.inflater = LayoutInflater.from(context); 
    } 

    @Override 
    public int getCount() { 
     if(viewModels == null) { 
      return 0; 
     } 

     return viewModels.size(); 
    } 

    @Override 
    public Object getItem(int position) { 
     return viewModels.get(position); 
    } 

    @Override 
    public long getItemId(int position) { 

     final Object model = getItem(position); 

     // Here we check if the model is an instance of ExampleViewModel and if yes we return its id 
     if(model instanceof ExampleViewModel) { 
      return EXAMPLE_VIEW_ID; 
     } 

     return DEFAULT_VIEW_ID; 
    } 

    @Override 
    public View getView(int position, View convertView, ViewGroup parent) { 

     if(getItemId(position) == EXAMPLE_VIEW_ID) { 
      final ExampleViewModel model = (ExampleViewModel) getItem(position); 

      final ExampleViewHolder viewHolder; 

      // If the convertView is null we need to inflate a new view 
      if(convertView == null) { 
       final View view = this.inflater.inflate(ExampleViewHolder.LAYOUT, parent, false); 
       viewHolder = new ExampleViewHolder(view); 

       // Here we set the viewHolder as tag to the View 
       // This is done so we can reuse the same view holder later on 
       // Essentially this is the integral part of the whole view recycling process 
       view.setTag(viewHolder); 
      } else { 
       // If the convertView is not null we can just get the view holder with getTag() from the View 
       viewHolder = (ExampleViewHolder) convertView.getTag(); 
      } 

      // And we just need to bind the model to the view holder 
      viewHolder.bind(model); 
     } 

     return convertView; 
    } 
} 

И это все, что вам нужно. Это довольно эффективная реализация Adapter. Если вы имеете дело с двумя или более разными типами представлений, вам нужно написать модель представления и просмотреть класс держателя для каждого типа. Вы можете написать интерфейс, который называется ViewModel, который будет выглядеть примерно так:

public interface ViewModel { 

} 

Каждый вид модели ваш должен реализовывать этот интерфейс. Затем вы можете использовать List<ViewModel> в Adapter, который может содержать все различные виды моделей.

public class TypeOneViewModel implements ViewModel { 

} 

public class TypeTwoViewModel implements ViewModel { 

} 

Как только все ваши просматривать модели реализуют этот интерфейс, вы можете сделать это:

final List<ViewModel> models = new ArrayList<ViewModel>(); 
models.add(new TypeOneViewModel()); 
models.add(new TypeTwoViewModel()); 
... 

И этот List который теперь содержит несколько различных типов зрения моделей затем могут быть переданы в Adapter. Adapter будет выглядеть примерно так:

public class ExampleAdapter extends BaseAdapter { 

    private static final int TYPE_ONE_VIEW_ID = 0; 
    private static final int TYPE_TWO_VIEW_ID = 1; 
    private static final int DEFAULT_VIEW_ID = TYPE_ONE_VIEW_ID; 

    private final LayoutInflater inflater; 
    private List<ViewModel> viewModels; 

    public ExampleAdapter(Context context, List<ViewModel> viewModels) { 
     this.viewModels = viewModels; 
     this.inflater = LayoutInflater.from(context); 
    } 

    @Override 
    public int getCount() { 
     if(viewModels == null) { 
      return 0; 
     } 

     return viewModels.size(); 
    } 

    @Override 
    public ViewModel getItem(int position) { 
     return viewModels.get(position); 
    } 

    @Override 
    public long getItemId(int position) { 

     final ViewModel model = getItem(position); 

     if(model instanceof TypeOneViewModel) { 
      return TYPE_ONE_VIEW_ID; 
     } 

     if(model instanceof TypeTwoViewModel) { 
      return TYPE_TWO_VIEW_ID; 
     } 

     return DEFAULT_VIEW_ID; 
    } 

    @Override 
    public View getView(int position, View convertView, ViewGroup parent) { 

     if(getItemId(position) == TYPE_ONE_VIEW_ID) { 
      final TypeOneViewModel model = (TypeOneViewModel) getItem(position); 

      final TypeOneViewHolder viewHolder; 
      if(convertView == null) { 
       final View view = this.inflater.inflate(TypeOneViewHolder.LAYOUT, parent, false); 
       viewHolder = new TypeOneViewHolder(view); 
       view.setTag(viewHolder); 
      } else { 
       viewHolder = (TypeOneViewHolder) convertView.getTag(); 
      } 

      viewHolder.bind(model); 
     } 

     if(getItemId(position) == TYPE_TWO_VIEW_ID) { 
      final TypeTwoViewModel model = (TypeTwoViewModel) getItem(position); 

      final TypeTwoViewHolder viewHolder; 
      if(convertView == null) { 
       final View view = this.inflater.inflate(TypeTwoViewHolder.LAYOUT, parent, false); 
       viewHolder = new TypeTwoViewHolder(view); 
       view.setTag(viewHolder); 
      } else { 
       viewHolder = (TypeTwoViewHolder) convertView.getTag(); 
      } 

      viewHolder.bind(model); 
     } 

     return convertView; 
    } 
} 

Вы также можете объединить ваши обладателям просматривать, написав абстрактный класс. Такой абстрактный класс будет выглядеть примерно так:

public abstract class ViewHolder<T extends ViewModel> { 
    protected final View itemView; 

    public ViewHolder(View view) { 
     this.itemView = view; 
    } 

    public abstract void bind(T model); 
} 

Если вы используете этот абстрактный класс в качестве базового класса для держателей зрения можно было бы написать их так:

public class TypeOneViewHolder extends ViewHolder<TypeOneViewModel> { 
    public TypeOneViewHolder(View view) { 
     super(view); 

     ... 
    } 

    public void bind(TypeOneViewModel model) { 
     ... 
    } 
} 

Хотя эта часть не действительно требуется. Самая важная часть при работе с несколькими различными типами элементов в ListView состоит в том, что все модели реализуют общий интерфейс, поэтому вы можете смело помещать их в один и тот же List.

В любом случае, все это выглядит намного проще и чище, чем ваш Adapter, не так ли? Таким образом, у вас есть идеальное разделение между данными в ListView и Views, которые отображают данные, и это намного удобнее обслуживать. Вы можете легко реализовать анимацию в держателе представления, не заботясь о том, чтобы переработать представление, и многие требования стали намного проще реализовать. Конечно, RecyclerView берет все это на следующий уровень. Он работает почти так же, но имеет несколько серьезных улучшений по сравнению с ListView, я действительно предлагаю вам взглянуть на него.


Одна вещь, я совсем забыл: Вы можете выставить внутреннюю List зрения моделей с добытчика, так что вы можете изменить модели вид с внешней стороны. Добавить методы, как это в Adapter:

public List<ExampleViewModel> viewmodels() { 
    return viewModels; 
} 

public void setViewModels(List<ExampleViewModel> models) { 
    viewModels = models; 
} 

Затем вы можете изменить модели просмотра в Adapter, как это:

adapter.setViewModels(newData); 
... 
adapter.viewmodels().add(viewModel); 

И когда вы закончите изменение данных вы можете обновить ListView по телефону notifyDataSetChanged() на Adapter.

+0

Это так упрощено. Вы пошли к гайкам и болтам и дали понять. Спасибо! Кроме того, если я хочу, чтобы один список показывал два разных типа строк, должен ли я создавать два разных адаптера? Потому что я сделал это, используя только один адаптер. – Slay

+0

В нижней части моего ответа я покажу, как реализовать несколько типов элементов в одном и том же «адаптере», если это то, что вы имеете в виду. –

+0

Не совсем понял эту часть. Что такое модель в этом контексте? – Slay

 Смежные вопросы

  • Нет связанных вопросов^_^