2016-06-30 17 views
1

Ich habe eine VerticalGridView, die einen RecyclerView.Adapter verwendet, um die Elemente aufzufüllen. Ich habe festgestellt, dass die onBindViewHolder() -Methode nicht aufgerufen wird, wenn das potenzielle Element außerhalb des Darstellungsbereichs ist. Unglücklicherweise verursacht dies eine NullPointerException von einer anderen Methode, weil ich eine TextView-Referenz in der onBindViewHolder() -Methode abfange und sie zur späteren Manipulation an eine externe Variable übergebe.Erzwingen RecyclerView.Adapter, um onBindViewHolder() für alle Elemente aufzurufen

Von dem, was ich verstehe, wird der Verweis auf die TextView erstellt, wenn das Viewholder-Objekt instanziiert wird.

public class ViewHolder extends RecyclerView.ViewHolder { 

    public TextView txtCategoryName; 
    public TextView txtCategoryDefectTotal; 
    public View categoryBoxRoot; 

    public ViewHolder(View itemView) { 
     super(itemView); 
     txtCategoryName = (TextView) itemView.findViewById(R.id.textViewCategoryName); 
     txtCategoryDefectTotal = (TextView) itemView.findViewById(R.id.textViewCategoryTotalDefects); 
     categoryBoxRoot = itemView.findViewById(R.id.root_category_box); 
    } 
} 

Gibt es eine Möglichkeit onBindViewHolder zu zwingen, auf alle Elemente mindestens einmal aufgerufen werden, wenn der Adapter instanziiert wird?

Ich habe die Vorschläge here ohne Erfolg versucht.

Ich verstehe, dass OnBindViewHolder für alle Elemente erzwingen gegen die whole purpose of the RecycleView.Adapter arbeiten würde. Daher bin ich offen für weitere Vorschläge, wie ich diese TextView-Referenz erfassen kann.

Als vorübergehende Lösung für dieses Problem kann ich einen try catch-Block um die Methode verwenden, die die NullPointerException generiert. Ich bin jedoch besorgt, dass das Fehlen der Referenz bedeutet, dass ich in Zukunft Fehler einführen könnte.

+0

Verwenden Sie Ihre OnClickListener auf der ViewHolder Klasse selbst, nicht 'onBindViewHolder' - Sie sollten nicht zu schaffen ein neuer onclicklistener für jede Zeile. http://StackOverflow.com/a/30285361/4252352 –

+0

Das ist eine nette Optimierung aber das hilft mir nicht mit dem Problem zur Hand. Ich brauche die TextView-Referenz, damit ich sie von einer externen Methode aktualisieren kann. – bergler77

+1

Warum nicht die Datenquelle aktualisieren und 'notifyDataSetChanged();' oder 'notifyItemChanged (position);' aufrufen (was eine weniger kostspielige Operation ist) auf Ihrem Adapter. –

Antwort

2

Die einfachste Lösung für dieses Problem an der Unterseite des Gitters nach unten scrollen und dann wieder nach oben an die Spitze. Dies ist jedoch in der onCreate-Methode nicht möglich, da das Raster zu diesem Zeitpunkt technisch nicht sichtbar ist. Stattdessen sollte es in der OnResume-Methode der Aktivität aufgerufen werden.

@Override 
public void onResume(){ 
    super.onResume(); 
    VerticalGridView defectGrid = grids.get(0); 
    RecyclerView.Adapter adapter = defectGrid.getAdapter(); 
    defectGrid.smoothScrollToPosition(adapter.getItemCount()-1); 
    defectGrid.smoothScrollToPosition(0); 
} 

Dies war ein guter Versuch für eine Lösung, aber leider funktioniert es immer noch nicht. Während es die Referenz erhält, um die Methode funktionieren zu lassen, hat es NICHT unbedingt die richtige Referenz. Ich habe festgestellt, dass die Referenz auf einen anderen TextView zeigen kann, da der RecycleView.Adapter Ansichten wieder verwendet, um sie in verschiedenen Bereichen anzuzeigen.

LÖSUNG:

Mark Keen hatte Recht, als er sagte notifyDataSetChanged() zu verwenden. Ich habe es zum Funktionieren gebracht, indem ich meine onBindViewHolder Methode reparierte, um richtig zu arbeiten.

@Override 
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { 
    final ViewHolder viewHolder = (ViewHolder) holder; 
    viewHolder.txtCategoryName.setText(categories.get(position).getStrCategory()); 
    viewHolder.txtCategoryDefectTotal.setText(String.valueOf(categories.get(position).getTotalDefectsInCategory())); 
} 

Ich habe auch mein Datenobjekt, so dass es einen int-Wert anstelle einem Verweis auf die Textview hält, da die oben bewiesen, dass Referenz ungültig war. Schließlich fügte ich meinem Adapter einen Anruf hinzu, als ich meine benutzerdefinierte Zurück-Taste drückte.

grids.get(0).getAdapter().notifyDataSetChanged(); 

Vielen Dank an alle, die dazu beigetragen haben!

0

verstehen Fluss

public class ThemeGridAdapter extends RecyclerView.Adapter<ThemeGridAdapter.ViewHolder> { 
    private OnItemClickedListener listener; 
    private List<Theme> mDataset; 

    public static class ViewHolder extends RecyclerView.ViewHolder { 
     public TextView title; 
     public TextView subtitle; 
     //  public SimpleDraweeView image; 
     public ImageView img; 
     public ViewGroup container; 

     public ViewHolder(RelativeLayout v) { 
      super(v); 
      container = v; 
      title = (TextView) v.findViewById(R.id.theme_name_text); 
      subtitle = (TextView) v.findViewById(R.id.theme_name_type); 
//   image = (SimpleDraweeView) v.findViewById(R.id.cell_img); 
      img = (ImageView) v.findViewById(R.id.cell_img); 
     } 
    } 

    public ThemeGridAdapter(List<Theme> dataset) { 
     mDataset = dataset; 
    } 

    @Override 
    public ThemeGridAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, 
                  int viewType) { 
     RelativeLayout v = (RelativeLayout) LayoutInflater.from(parent.getContext()) 
       .inflate(R.layout.cell, parent, false); 
     return new ViewHolder(v); 
    } 

    @Override 
    public void onBindViewHolder(final ViewHolder holder, final int position) { 
     holder.title.setText(mDataset.get(position).getTitle()); 
     holder.subtitle.setText(mDataset.get(position).getAuthor()); 
//  Uri uri = Uri.parse(mDataset.get(position).getUrl()); 
     holder.img.setImageResource(mDataset.get(position).getImage()); 
//  holder.image.setImageURI(uri); 
     holder.container.setOnClickListener(new View.OnClickListener() { 
      @Override 
      public void onClick(View v) { 
       if (listener != null) 
        listener.onItemClicked(holder.getAdapterPosition()); 
      } 
     }); 

    } 

    @Override 
    public int getItemCount() { 
     return mDataset.size(); 
    } 

    public void setListener(OnItemClickedListener listener) { 
     this.listener = listener; 
    } 
} 
+0

Das hilft mir nicht. Ich erzeuge einen Verweis auf eine TextView (wahrscheinlich keine sichere Operation) in der onBindViewHolder-Methode. Diese Methode wird für nichts außerhalb der Grenzen meines Ansichtsfensters aufgerufen. Wenn ich eine Operation für die Textansicht außerhalb des Bildschirms ausführe, erhalte ich eine NullPointerException. – bergler77

0

den Stil prüfen Sicht Bindung

public class CrimeListFragment extends Fragment { 


    private static final String SAVED_SUBTITLE_VISIBLE = "subtitle"; 

    private static final int REQUEST_CRIME = 1; 

    private RecyclerView mRecyclerView; 
    private CrimeAdapter mCrimeAdapter; 

    private boolean mSubtitleVisible; 

    @Override 
    public void onCreate(@Nullable Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     setHasOptionsMenu(true); 
    } 

    @Nullable 
    @Override 
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { 
     View view = inflater.inflate(R.layout.fragment_crime_list, container, false); 

     mRecyclerView = (RecyclerView) view.findViewById(R.id.crime_recycler_view); 
     mRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity())); 

     if (savedInstanceState != null){ 
      mSubtitleVisible = savedInstanceState.getBoolean(SAVED_SUBTITLE_VISIBLE); 
     } 
     updateUI(); 


     return view; 
    } 

    private void updateUI() { 

     CrimeLab crimeLab = CrimeLab.get(getActivity()); 
     List<Crime> crimes = crimeLab.getCrimes(); 

     if (mCrimeAdapter == null) { 
      mCrimeAdapter = new CrimeAdapter(crimes); 
      mRecyclerView.setAdapter(mCrimeAdapter); 
     } else { 
      mCrimeAdapter.setCrimes(crimes); 
      mCrimeAdapter.notifyDataSetChanged(); 
     } 
     updateSubtitle(); 
    } 

    private class CrimeHolder extends RecyclerView.ViewHolder implements View.OnClickListener { 

     private TextView mTitleTextView; 
     private TextView mDateTextView; 
     private CheckBox mSolvedCheckBox; 
     private Crime mCrime; 


     public CrimeHolder(View itemView) { 
      super(itemView); 

      itemView.setOnClickListener(this); 

      mTitleTextView = (TextView) itemView.findViewById(R.id.list_item_crime_title_text_view); 
      mDateTextView = (TextView) itemView.findViewById(R.id.list_item_crime_date_text_view); 
      mSolvedCheckBox = (CheckBox) itemView.findViewById(R.id.list_item_crime_solved_checkbox); 

     } 

     public void bindCrime(Crime crime) { 

      mCrime = crime; 

      mTitleTextView.setText(mCrime.getTitile()); 
      mDateTextView.setText(mCrime.getDate().toString()); 
      mSolvedCheckBox.setChecked(mCrime.isSolved()); 
     } 

     @Override 
     public void onClick(View v) { 

      /*Toast.makeText(getActivity(),mCrime.getTitile() + "clicked!", Toast.LENGTH_SHORT).show();*/ 

      /*Intent for calling activity from fragment*/ 
      /* Intent intent = new Intent(getActivity(), CrimeActivity.class);*/ 
      /*Intent intent = CrimeActivity.newIntent(getActivity(),mCrime.getId());*/ 
      Intent intent = CrimePagerActivity.newIntent(getActivity(), mCrime.getId()); 
      /*startActivityForResult(intent,REQUEST_CRIME);*/ 
      startActivity(intent); 
     } 
    } 

    private class CrimeAdapter extends RecyclerView.Adapter<CrimeHolder> { 

     private List<Crime> mCrimes; 

     public CrimeAdapter(List<Crime> crimes) { 

      mCrimes = crimes; 
     } 

     @Override 
     public CrimeHolder onCreateViewHolder(ViewGroup parent, int viewType) { 
      LayoutInflater layoutInflater = LayoutInflater.from(getActivity()); 

      View view = layoutInflater.inflate(R.layout.list_item_crime, parent, false); 
      return new CrimeHolder(view); 

     } 

     @Override 
     public void onBindViewHolder(CrimeHolder holder, int position) { 

      Crime crime = mCrimes.get(position); 
      /*holder.mTextView.setText(crime.getTitile());*/ 
      holder.bindCrime(crime); 

     } 

     @Override 
     public int getItemCount() { 
      return mCrimes.size(); 
     } 

     public void setCrimes(List<Crime> crimes) { 

      mCrimes = crimes; 
     } 
    } 


    @Override 
    public void onResume() { 
     super.onResume(); 
     updateUI(); 
    } 


    @Override 
    public void onActivityResult(int requestCode, int resultCode, Intent data) { 
     if (requestCode == REQUEST_CRIME) { 

     } 
    } 

    @Override 
    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { 
     super.onCreateOptionsMenu(menu, inflater); 
     inflater.inflate(R.menu.fragment_crime_list, menu); 

     MenuItem subtitleItem = menu.findItem(R.id.menu_item_show_subtitle); 
     if (mSubtitleVisible){ 
      subtitleItem.setTitle(R.string.hide_subtitle); 
     }else { 
      subtitleItem.setTitle(R.string.show_subtitle); 
     } 
    } 

    @Override 
    public boolean onOptionsItemSelected(MenuItem item) { 

     switch (item.getItemId()) { 

      case R.id.menu_item_new_crime: 

       Crime crime = new Crime(); 
       CrimeLab.get(getActivity()).addCrime(crime); 
       Intent intent = CrimePagerActivity.newIntent(getActivity(), crime.getId()); 
       startActivity(intent); 
       return true; 

      case R.id.menu_item_show_subtitle: 

       mSubtitleVisible = !mSubtitleVisible; 
       getActivity().invalidateOptionsMenu(); 
       updateSubtitle(); 
       return true; 

      default: 
       return super.onOptionsItemSelected(item); 
     } 
    } 

    private void updateSubtitle(){ 

     CrimeLab crimeLab = CrimeLab.get(getActivity()); 
     int crimeCount = crimeLab.getCrimes().size(); 

     String subtitle = getString(R.string.subtitle_format,crimeCount); 
     if (!mSubtitleVisible){ 

      subtitle = null; 
     } 
     AppCompatActivity appCompatActivity = (AppCompatActivity)getActivity(); 
     appCompatActivity.getSupportActionBar().setSubtitle(subtitle); 

    } 

    @Override 
    public void onSaveInstanceState(Bundle outState) { 
     super.onSaveInstanceState(outState); 
     outState.putBoolean(SAVED_SUBTITLE_VISIBLE,mSubtitleVisible); 
    } 
} 
+0

Ich bearbeite meinen Beitrag, damit Sie die vollständige Methode sehen können – bergler77

0

zurückgesetzt einfach den Adapter wie diese

RecyclerView.Adapter adapter = recList.getAdapter(); 
recList.setAdapter(null); 
recList.setAdapter(adapter); 

recList sollten Sie Ansicht Recycler sein, einfach :)

Verwandte Themen