6

Ho un frammento che contiene un RecyclerView per visualizzare gli eventi per un determinato giorno. Sto usando un ViewPager per separare i Frammenti in più giorni; Un frammento per gli eventi di sabato e un frammento per gli eventi della domenica.I frammenti ViewPager fanno tutti riferimento allo stesso RecyclerView e/o Adattatore

Tuttavia, sembra che entrambi i frammenti facciano riferimento allo stesso RecyclerView e/o adattatore, poiché è solo l'ultima scheda (in questo caso, domenica) i cui eventi vengono visualizzati.

Nel mio caso specifico, il sabato ha due eventi e la domenica non ha eventi. Entrambi i frammenti hanno vuote RecyclerViews. Per confermare la mia teoria che è stata causata dall'ultima scheda, ho cambiato la data. Ciò ha causato a RecyclerViews due eventi (quelli di sabato).

Ecco il codice rilevante per i singoli frammenti:

public class EventListFragment extends Fragment{ 
    private EventAdapter mEventAdapter; 

    private static final String DATE_ARG = "eventDate"; 

    public static EventListFragment newInstance(LocalDate date){ 
     EventListFragment eventListFragment = new EventListFragment(); 
     Bundle args = new Bundle(); 
     args.putSerializable(DATE_ARG, date); 
     eventListFragment.setArguments(args); 
     return eventListFragment; 
    } 

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

     // Setup recyclerview 
     RecyclerView eventRecyclerView = (RecyclerView) view.findViewById(R.id.event_recycler_view); 
     LinearLayoutManager layoutManager = new LinearLayoutManager(getActivity()); 
     layoutManager.setOrientation(LinearLayoutManager.VERTICAL); 
     eventRecyclerView.setLayoutManager(layoutManager); 

     // Get date 
     LocalDate eventDate = (LocalDate) getArguments().getSerializable(DATE_ARG); 

     // Set adapter 
     mEventAdapter = new EventAdapter(getActivity(), getEvents(eventDate)); 
     eventRecyclerView.setAdapter(mEventAdapter); 

     return view; 
    } 
} 

getEvents() è solo una funzione privata per restituire gli eventi per una determinata data. Ho usato il debugger così come i test unitari per verificare che funzioni correttamente. Il debugger mostra che tira l'elenco corretto per ciascun frammento, ma come ho spiegato non vengono visualizzati correttamente.

ecco il codice rilevante per il frammento genitore:

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

     // Get and set up viewpager 
     final ViewPager viewPager = (ViewPager) view.findViewById(R.id.event_view_pager); 
     EventFragmentAdapter eventFragmentAdapter = new EventFragmentAdapter(getFragmentManager(), getEventDates()); 
     viewPager.setAdapter(eventFragmentAdapter); 

     // Get and set up tablayout 
     final TabLayout tabLayout = (TabLayout) view.findViewById(R.id.event_tabs); 
     tabLayout.setTabGravity(TabLayout.GRAVITY_FILL); 

     tabLayout.post(new Runnable() { 
      @Override 
      public void run() { 
       tabLayout.setupWithViewPager(viewPager); 
      } 
     }); 

     return view; 
    } 
} 

Simile a quello ultimo, getEventDates() tira solo le date che gli eventi sono in corso. A scopo di test al momento, sto codificando con difficoltà un elenco di date restituito poiché non abbiamo ancora impostato il nostro database. Ho tirato questo metodo fuori perché voglio l'applicazione per essere in grado di funzionare di nuovo nel 2016, che può avere diverse date:

private List<LocalDate> getEventDates(){ 
    List<LocalDate> eventDates = new ArrayList<>(); 

    eventDates.add(new LocalDate(2015, 10, 17)); 
    eventDates.add(new LocalDate(2015, 10, 18)); 

    return eventDates; 
} 

L'ultimo pezzo di codice di riferimento è per la FragmentStatePagerAdapter sto usando per la mia ViewPager:

public class EventFragmentAdapter extends FragmentStatePagerAdapter { 
    private List<LocalDate> mEventDates; 

    public EventFragmentAdapter(FragmentManager fragmentManager, List<LocalDate> eventDates){ 
     super(fragmentManager); 
     this.mEventDates = eventDates; 
    } 
    @Override 
    public Fragment getItem(int i) { 
     return EventListFragment.newInstance(mEventDates.get(i)); 
    } 

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

    @Override 
    public CharSequence getPageTitle(int position) { 
     return mEventDates.get(position).dayOfWeek().getAsText(); 
    } 
} 

Qualche idea sul perché entrambi gli elenchi siano sempre gli stessi e si basino sull'ultima scheda del ViewPager? Suppongo che in qualche modo stiano facendo riferimento allo stesso RecyclerView o allo stesso RecyclerViewAdapter, ma non ho campi statici per quelli quindi non sono sicuro di come stia accadendo.

+0

tutto sembra giusto. Potresti mostrarci il tuo codice getEventDates? –

+0

Sei sicuro che 'getEvents()' non sta restituendo lo stesso set di dati di ogni 'LocalDate'? Come piccolo test, cosa succede se si sposta il 'ViewPager' su' Activity'? – Blackbelt

+0

@RodrigoHenriques a scopo di test in questo momento, ho appena hardcoded le date che ho restituito. Ritorna un oggetto 'List ' che ha date 2015-10-17 e 2015-10-18. Sto ancora lavorando su un modo per estrarre questi eventi da un database in seguito, quindi l'app si applicherà ancora al nostro evento nel 2016. Ma è tutto ciò che fa è restituire queste due date. – AdamMc331

risposta

16

Una lunga caccia e una soluzione anti-climatica (come con bug più difficili). Anche un po 'ingiusto dal momento che il bug non è nel codice pubblicato sopra, ho dovuto dare la caccia al tuo progetto git per capirlo. Il bug è in EventAdapter:

public class EventAdapter extends RecyclerView.Adapter<EventAdapter.ViewHolder> { 
    private static final List<Event> mEvents; 
    private final Context mContext; 

    public EventAdapter(Context context, List<Event> events){ 
     this.mContext = context; 
     mEvents = events; 
    } 
    ... 
} 

mEvents è statico ... quindi è condiviso tra tutte le istanze di mEvents!. Questo spiega perfettamente il bug dato che gli ultimi aggiornamenti imposteranno i valori per tutti EventAdapters.

Sembra che tu abbia effettuato mEvents statico in modo da poterlo accedere all'interno del tuo ViewHolders. Invece è possibile memorizzare il singolo evento all'interno dello ViewHolder e rilasciare il modificatore statico pericoloso. Sul rovescio della medaglia, evviva i progetti Open Source!

+0

WOW. Buona pesca. Non penso di averlo fatto intenzionalmente statico, come deve averlo fatto, quindi è stato usato altrove. Ci proverò quando tornerò a casa stasera e te lo farò sapere. – AdamMc331

+0

Hai ragione che era statico in modo che potesse essere visualizzato all'interno di ViewHolder. Qualche idea su come posso archiviare l'evento individuale all'interno dell'osservatore? – AdamMc331

+1

L'evento non è disponibile inizialmente quando si crea ViewHolder in onCreateViewHolder(). Aggiungi l'evento come nuovo campo privato a ViewHolder e quindi impostalo all'interno di onBindViewHolder(). –

1

Puoi provare questo?

public class EventFragmentAdapter extends FragmentStatePagerAdapter { 
    private List<LocalDate> mEventDates; 
    private List<EventListFragment> mFragments; 


    public EventFragmentAdapter(FragmentManager fragmentManager, List<LocalDate> eventDates){ 
     super(fragmentManager); 
     this.mEventDates = eventDates; 
     this.mFragments = new ArrayList<>; 
     for (LocalDate date : this.mEventDates) { 
      this.mFragments.add(EventListFragment.newInstance(date)); 
     } 
    } 

    @Override 
    public Fragment getItem(int i) { 
     return mFragments.get(i); 
    } 

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

    @Override 
    public CharSequence getPageTitle(int position) { 
     return mEventDates.get(position).dayOfWeek().getAsText(); 
    } 
} 

Quindi si controlla in ciascun elemento di mFragents se hanno il contenuto previsto.

+0

Grazie per il suggerimento! Tuttavia, questo ha lo stesso comportamento del mio codice originale. – AdamMc331

+1

Per favore, non mantenere riferimenti a Frammenti. È semplicemente sbagliato, sbagliato come mantenere i riferimenti a Attività, BrodcastReceivers, ContentProviders e ogni tipo di contesto o viste! –

2

Ho visto il tuo codice e sono totalmente d'accordo con Travor: stai usando un membro statico, che viene sostituito ogni volta che crei un nuovo Adattatore (e quindi ottiene solo i dati dell'ultima pagina). Ho modificato un po 'il tuo progetto per farlo funzionare correttamente. Dai un'occhiata, spero che possa essere utile.

EventFragment: sostituire getFragmentManager con getChildFragmentManager in quanto è necessario per essere EventListFragment maniglia EventFragment direttore frammento e non dal gestore frammento attività

public class EventFragment extends Fragment { 

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

     // Get and set up viewpager 
     final ViewPager viewPager = (ViewPager) view.findViewById(R.id.event_view_pager); 
     EventFragmentAdapter eventFragmentAdapter = new EventFragmentAdapter(getChildFragmentManager(), getEventDates()); 
     viewPager.setAdapter(eventFragmentAdapter); 

     // Get and set up tablayout 
     final TabLayout tabLayout = (TabLayout) view.findViewById(R.id.event_tabs); 
     tabLayout.setTabGravity(TabLayout.GRAVITY_FILL); 

     tabLayout.post(new Runnable() { 
      @Override 
      public void run() { 
       tabLayout.setupWithViewPager(viewPager); 
      } 
     }); 

     return view; 
    } 

    /** 
    * Retrieves the event dates for the hackathon so that the proper events can be displayed. 
    * @return 
    */ 
    private List<LocalDate> getEventDates(){ 
     List<LocalDate> eventDates = new ArrayList<>(); 

     eventDates.add(new LocalDate(2015, 10, 17)); 
     eventDates.add(new LocalDate(2015, 10, 18)); 

     return eventDates; 
    } 
} 

EventListFragment - Ho modificato la query, dal momento che la query SQLite doesn' t lavoro con il mio locale (italiano)

public class EventListFragment extends Fragment{ 

    private EventAdapter mEventAdapter; 

    private static final String TAG = EventListFragment.class.getSimpleName(); 

    private static final String DATE_ARG = "eventDate"; 

    public static EventListFragment newInstance(LocalDate date){ 
     EventListFragment eventListFragment = new EventListFragment(); 
     Bundle args = new Bundle(); 
     args.putSerializable(DATE_ARG, date); 
     eventListFragment.setArguments(args); 
     return eventListFragment; 
    } 

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

     // Setup recyclerview 
     RecyclerView eventRecyclerView = (RecyclerView) view.findViewById(R.id.event_recycler_view); 
     LinearLayoutManager layoutManager = new LinearLayoutManager(getActivity()); 
     layoutManager.setOrientation(LinearLayoutManager.VERTICAL); 
     eventRecyclerView.setLayoutManager(layoutManager); 

     // Get date 
     LocalDate eventDate = (LocalDate) getArguments().getSerializable(DATE_ARG); 

     // Set adapter 
     mEventAdapter = new EventAdapter(getEvents(eventDate)); 
     eventRecyclerView.setAdapter(mEventAdapter); 

     Log.v(TAG, eventRecyclerView.toString()); 

     return view; 
    } 

    /** 
    * Retrieves the events for the given date for the fragment. 
    */ 
    private List<Event> getEvents(LocalDate date){ 
     List<Event> returnList = new ArrayList<>(); 

     String dateString = Utility.getDBDateString(date); 
     List<String> dateList = new ArrayList<>(); 

     Cursor cursor = getActivity().getContentResolver().query(
       GHContract.EventEntry.CONTENT_URI, 
       new String[]{ "*", "substr(" + GHContract.EventEntry.COLUMN_TIME + ",0,11)" }, 
       "substr(" + GHContract.EventEntry.COLUMN_TIME + ",0,11) = ? ", 
       new String[]{dateString}, 
       GHContract.EventEntry.COLUMN_TIME 
     ); 

     while(cursor.moveToNext()){ 
      returnList.add(new Event(cursor)); 
      dateList.add(cursor.getString(cursor.getColumnIndex(GHContract.EventEntry.COLUMN_TIME))); 
     } 

     cursor.close(); 

     return returnList; 
    } 
} 

EventAdapter - rimossa la parola chiave static e il riferimento al contesto di attività (è necessario per ottenere il contesto da qualche altra parte)

public class EventAdapter extends RecyclerView.Adapter<EventAdapter.ViewHolder> { 
    private List<Event> mEvents; 

    public EventAdapter(List<Event> events){ 
     mEvents = events; 
    } 

    /** 
    * Inflates the view for Event items. 
    */ 
    @Override 
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 
     View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_event, parent, false); 
     return new ViewHolder(view); 
    } 

    /** 
    * Binds the data for an event to its view. 
    */ 
    @Override 
    public void onBindViewHolder(ViewHolder holder, int position) { 
     Event event = mEvents.get(position); 
     holder.timeView.setText(Utility.getTimeString(event.getTime())); 
     holder.titleView.setText(event.getTitle()); 
     holder.locationView.setText(event.getLocation()); 

     // If reminder time is not null, show check mark. If it is, show plus. 
     if(event.getReminderTime() != null){ 
      holder.alarmView.setImageDrawable(holder.itemView.getResources().getDrawable(R.drawable.ic_alarm_on)); 
     } else{ 
      holder.alarmView.setImageDrawable(holder.itemView.getResources().getDrawable(R.drawable.ic_add_alarm)); 
     } 
    } 

    /** 
    * Returns the size of the adapter. 
    */ 
    @Override 
    public int getItemCount() { 
     return mEvents.size(); 
    } 

    /** 
    * Retains a reference to the view so `findViewById` calls are only made once for the adapter. 
    */ 
    public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{ 
     public final TextView timeView; 
     public final TextView titleView; 
     public final TextView locationView; 
     public final ImageView alarmView; 

     public ViewHolder(View view){ 
      super(view); 
      timeView = (TextView) view.findViewById(R.id.event_time); 
      titleView = (TextView) view.findViewById(R.id.event_title); 
      locationView = (TextView) view.findViewById(R.id.event_location); 
      alarmView = (ImageView) view.findViewById(R.id.event_add_reminder); 
      alarmView.setOnClickListener(this); 
     } 

     /** 
     * Handles the click a user makes on the alarm image view. 
     */ 
     @Override 
     public void onClick(View v) { 
      OnEventReminderClickListener activity = (OnEventReminderClickListener) v.getContext(); 
      activity.onEventReminderClicked(mEvents.get(getPosition())); 
     } 
    } 

    /** 
    * Interface to call back to the activity when an alarm is clicked for an event item. 
    */ 
    public interface OnEventReminderClickListener{ 
     void onEventReminderClicked(Event event); 
    } 
} 

E infine l'applicazione/build.gradle, in quanto è necessario per ottenere la stessa versione per tutte le librerie di supporto (riciclaggio, carta e così via)

apply plugin: 'com.android.application' 

android { 
    compileSdkVersion 22 
    buildToolsVersion "22.0.1" 

    defaultConfig { 
     applicationId "com.adammcneilly.grizzhacks" 
     minSdkVersion 15 
     targetSdkVersion 22 
     versionCode 1 
     versionName "1.0" 
    } 
    buildTypes { 
     release { 
      minifyEnabled false 
      proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 
     } 
    } 
} 

dependencies { 
    compile fileTree(dir: 'libs', include: ['*.jar']) 
    compile 'com.android.support:appcompat-v7:22.2.1' 
    compile 'joda-time:joda-time:2.7' 
    compile 'com.android.support:recyclerview-v7:22.2.1' 
    compile 'com.android.support:cardview-v7:22.2.1' 
    compile 'com.android.support:design:22.2.1' 
} 
Problemi correlati