2016-05-18 9 views
0

Ich versuche, diese einfache App zu bekommen, um vergeblich zu arbeiten.Android-Fragment tablayout Aktualisierung von ListView-Problemen

Diese Aktivität soll eine Reihe von Tabs anzeigen, die Staffeln einer Serie enthalten. Die erste Registerkarte enthält ihre Beschreibung und die nächsten enthalten ihre Episoden in ListRows.

Jede Episodenzeile kann angeklickt werden, um einen booleschen Wert in der Datenbank umzuschalten. Wenn true bedeutet, hat der Benutzer diese Episode beobachtet, die ein Symbol rechts in der Zeile in der ListView ändert. Wird beobachtet und ein Symbol ändert sich in der Reihe. Ich habe Probleme beim Aktualisieren der ListViews. Für die erste Saison-Registerkarte funktioniert es perfekt, Sie klicken auf eine Zeile und das Symbol ändert sich. Aber wenn ich auf einen Tab rechts von dem aktuellen gehe, werden die Dinge ausgeflippt, das Icon wird nicht aktualisiert (obwohl der Wert in der Datenbank aktualisiert wird) und wenn ich jetzt zu einem anderen Tab wische, wird dieser den Wert halten Der letzte und das Symbol haben den richtigen Wert.

Ich bin verloren so

Die Aktivität:

class ListEpisodes extends ListAbstract { 

private long seriesId;    // Identifier of current Series 
private String seriesTitle;   // Title of current Series 

/** 
* The {@link android.support.v4.view.PagerAdapter} that will provide 
* fragments for each of the sections. We use a {@link FragmentPagerAdapter} 
* derivative, which will keep every loaded fragment in memory. 
* If this becomes too memory intensive, it may be best to switch to a 
* {@link android.support.v4.app.FragmentStatePagerAdapter}. 
*/ 
private SeasonPagerAdapter mSectionsPagerAdapter; 

/** 
* The {@link ViewPager} that will host the section contents. 
*/ 
private ViewPager mViewPager; 

@Override 
protected void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    setContentView(R.layout.activity_list_episodes); 

    Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); 
    setSupportActionBar(toolbar); 
    getSupportActionBar().setDisplayHomeAsUpEnabled(true); 
    try { 
     seriesId = (Long) this.getIntent().getExtras().get("sid"); 
    } catch (NullPointerException e) { 
     seriesId = 0; 
    } 
    mDbAdapter = new DbAdapter(this); 
    mDbAdapter.open(); 
    list(); 
} 

@Override 
public boolean onCreateOptionsMenu(Menu menu) { 
    // Inflate the menu; this adds items to the action bar if it is present. 
    getMenuInflater().inflate(R.menu.menu_list_episodes, menu); 
    return true; 
} 

@Override 
public boolean onOptionsItemSelected(MenuItem item) { 
    // Handle action bar item clicks here. The action bar will 
    // automatically handle clicks on the Home/Up button, so long 
    // as you specify a parent activity in AndroidManifest.xml. 
    switch (item.getItemId()) { 
     case R.id.create_new_episode: 
      create(); 
      return true; 
     case R.id.edit_series: 
      editSeries(); 
      return true; 
     default: 
      break; 
    } 
    return super.onOptionsItemSelected(item); 
} 

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

/////////////////////////////////////// ListAbstract /////////////////////////////////////// 

/** 
* Fetches and shows all episodes from the database. 
*/ 
protected void list() { 
    Cursor series = mDbAdapter.fetchSeries(seriesId); 
    seriesTitle = series.getString(series.getColumnIndexOrThrow(DbAdapter.SERIES_KEY_TITLE)); 
    getSupportActionBar().setTitle(seriesTitle); 

    Cursor eCursor = mDbAdapter.getSeasons(seriesId); 
    ArrayList<Integer> seasons = new ArrayList<>(); 
    eCursor.moveToFirst(); 
    for (int i = 0; i < eCursor.getCount(); i++) { 
     seasons.add(eCursor.getInt(eCursor.getColumnIndex(DbAdapter.EPISODE_KEY_SEASON_NUM))); 
     eCursor.moveToNext(); 
    } 
    // Create the adapter that will return a fragment for each of the three 
    // primary sections of the activity. 
    mSectionsPagerAdapter = new SeasonPagerAdapter(getSupportFragmentManager(), 
      seasons, seriesId); 
    mSectionsPagerAdapter.notifyDataSetChanged(); 



    // Set up the ViewPager with the sections adapter. 
    mViewPager = (ViewPager) findViewById(R.id.container); 
    mViewPager.setAdapter(mSectionsPagerAdapter); 

    TabLayout tabLayout = (TabLayout) findViewById(R.id.tabs); 
    tabLayout.setupWithViewPager(mViewPager); 
} 

/** 
* Starts an activity to create a new episode. 
*/ 
protected void create() { 
    Intent i = new Intent(this, EditEpisodes.class); 
    i.putExtra(DbAdapter.EPISODE_KEY_ID, Long.valueOf(0)); 
    i.putExtra(DbAdapter.EPISODE_KEY_SERIES, seriesId); 
    startActivityForResult(i, ACTIVITY_CREATE); 
} 

/** 
* Starts an activity to edit an episode. 
* 
* @param elementId id of the episode that will be edited. 
*/ 
protected void edit(long elementId) { 
    Intent i = new Intent(this, EditEpisodes.class); 
    i.putExtra(DbAdapter.EPISODE_KEY_SERIES, seriesId); 
    i.putExtra(DbAdapter.EPISODE_KEY_ID, elementId); 
    startActivityForResult(i, ACTIVITY_EDIT); 
} 

/** 
* Deletes the episode elementId. 
* 
* @param elementId id of the episode that will be deleted. 
*/ 
protected void delete(long elementId) { 
    // Episodes are refreshed if the current episode has been correctly deleted. 
    if (mDbAdapter.deleteEpisode(elementId)) { 
     list(); 
    } 
} 

/** 
* Starts an activity to edit the current series (with identifier [seriesId]). 
*/ 
protected void editSeries() { 
    Intent i = new Intent(this, EditSeries.class); 
    i.putExtra(DbAdapter.SERIES_KEY_ID, seriesId); 
    startActivityForResult(i, ACTIVITY_EDIT); 
} 

//////////////////////////////////////////////////////////////////////////////////////////// 

/** 
* Fragment containing all episodes that correspond to the same season of a given 
* series. 
*/ 
public static class SeasonFragment extends Fragment { 

    /** 
    * These arguments can only be passed via bundle. They match to season number and series Id. 
    */ 
    private static final String ARG_TAB_NUMBER = "section_number"; 
    private static final String ARG_SERIES_ID = "series_id"; 
    private static final String ARG_SEASONS_ARRAY = "seasons_array"; 

    private ArrayList<Integer> seasons; 
    private int season = -1; 
    private int tab = -1; 

    public SeasonFragment() { 
    } 

    /** 
    * Returns a new instance of this fragment for the given season. 
    */ 
    protected static SeasonFragment newInstance(ArrayList<Integer> seasons, int tabNumber, 
               long seriesId) { 
     SeasonFragment fragment = new SeasonFragment(); 
     Bundle args = new Bundle(); 
     args.putIntegerArrayList(ARG_SEASONS_ARRAY, seasons); 
     args.putInt(ARG_TAB_NUMBER, tabNumber); 
     args.putLong(ARG_SERIES_ID, seriesId); 
     fragment.setArguments(args); 
     return fragment; 
    } 

    /** 
    * Fetches and shows all episodes on this fragment 
    * 
    * @param inflater   to instantiate the season view 
    * @param container   to match the tabs (internal to android) 
    * @param savedInstanceState argument container, since this class' constructor can't have 
    *       parameters 
    */ 
    @Override 
    public View onCreateView(LayoutInflater inflater, ViewGroup container, 
          Bundle savedInstanceState) { 
     DbAdapter mDbAdapter = new DbAdapter(this.getActivity()); 
     mDbAdapter.open(); 
     tab = getArguments().getInt(ARG_TAB_NUMBER); 
     long series = getArguments().getLong(ARG_SERIES_ID); 
     if (tab == 0) { 
      View rootView = inflater.inflate(R.layout.fragment_description, container, false); 
      Cursor descriptionCursor = mDbAdapter.fetchSeries(series); 
      //getActivity().startManagingCursor(descriptionCursor); 
      String description = descriptionCursor.getString(
        descriptionCursor.getColumnIndexOrThrow(DbAdapter.SERIES_KEY_DESCRIPTION)); 
      ((TextView) rootView.findViewById(R.id.description)).setText(description); 
      return rootView; 
     } else { 
      seasons = getArguments().getIntegerArrayList(ARG_SEASONS_ARRAY); 
      assert seasons != null; 
      season = seasons.get(tab - 1); 
      View rootView = inflater.inflate(R.layout.fragment_list_episodes, container, false); 
      // Get seriesId and fetch episodes for the season. 
      Cursor episodes = mDbAdapter.fetchEpisodesFromSeason(getArguments().getLong(ARG_SERIES_ID), 
        season); 
      //getActivity().startManagingCursor(episodes); 
      EpisodeListViewAdapter adapter = new EpisodeListViewAdapter(this.getContext(), R.layout.episode_row, episodes, 0); 
      ListView episodeList = (ListView) rootView.findViewById(R.id.episode_list); 
      episodeList.setAdapter(adapter); 
      registerForContextMenu(episodeList); 
      episodeList.setOnItemClickListener(
        new AdapterView.OnItemClickListener() { 
         @Override 
         public void onItemClick(AdapterView<?> parent, View view, int position, long id) { 
          toggleWatched(id); 
         } 
        }); 
      return rootView; 
     } 

    } 

    private void toggleWatched(long episodeId) { 
     DbAdapter mDbAdapter = new DbAdapter(this.getActivity()); 
     mDbAdapter.open(); 
     mDbAdapter.toggleWatched(episodeId); 
     // Get seriesId and fetch episodes for the season. 
     Cursor episodes = mDbAdapter.fetchEpisodesFromSeason(getArguments().getLong(ARG_SERIES_ID), 
       season); 
     ListView episodeList = (ListView) this.getActivity().findViewById(R.id.episode_list); 
     EpisodeListViewAdapter lva = ((EpisodeListViewAdapter) episodeList.getAdapter()); 
     // Sometimes this works sometimes it doesn't 
     lva.swapCursor(episodes); 
     lva.notifyDataSetChanged(); 
     episodeList.setAdapter(lva); 
    } 

    /** 
    * Method that creates an options menu when a user clicks and holds on a series. 
    */ 
    @Override 
    public void onCreateContextMenu(ContextMenu menu, View v, 
            ContextMenu.ContextMenuInfo menuInfo) { 
     super.onCreateContextMenu(menu, v, menuInfo); 
     menu.clear(); 
     menu.add(Menu.NONE, EDIT_ID, Menu.NONE, R.string.edit_episode); 
     menu.add(Menu.NONE, DELETE_ID, Menu.NONE, R.string.delete_episode); 
    } 

    /** 
    * Method called when a ContextMenu option is selected. 
    */ 
    @Override 
    public boolean onContextItemSelected(MenuItem item) { 
     int i = item.getItemId(); 
     if (i == DELETE_ID) { 
      AdapterView.AdapterContextMenuInfo info = 
        (AdapterView.AdapterContextMenuInfo) item.getMenuInfo(); 
      ((ListAbstract) getActivity()).delete(info.id); 
      return true; 
     } else if (i == EDIT_ID) { 
      AdapterView.AdapterContextMenuInfo info = 
        (AdapterView.AdapterContextMenuInfo) item.getMenuInfo(); 
      ((ListAbstract) getActivity()).edit(info.id); 
      return true; 
     } 

     return super.onContextItemSelected(item); 
    } 

    /** 
    * Adapter class specific to populate the listView in ListSeries from a cursor. 
    */ 
    static class EpisodeListViewAdapter extends ResourceCursorAdapter { 
     public EpisodeListViewAdapter(Context context, int layout, Cursor c, int flags) { 
      super(context, layout, c, flags); 
     } 

     @Override 
     public void changeCursor(Cursor cursor){ 
      super.changeCursor(cursor); 
     } 

     /** 
     * Will be automatically called by android to populate the list view. 
     * To do that it extracts the information from the cursor and transforms it to 
     * something usable in the case of the rating images. 
     */ 
     @Override 
     public void bindView(View view, Context context, Cursor cursor) { 
      TextView numberView = (TextView) view.findViewById(R.id.episode_number); 
      String episode_number = cursor.getString(cursor.getColumnIndex(DbAdapter.EPISODE_KEY_EPISODE_NUM)); 
      numberView.setText(episode_number); 

      TextView nameView = (TextView) view.findViewById(R.id.episode_name); 
      String episode_name = cursor.getString(cursor.getColumnIndex(DbAdapter.EPISODE_KEY_NAME)); 
      if (episode_name.length() > 17) { 
       try{ 
        int cut = episode_name.indexOf(" ", 5); 
        episode_name = episode_name.substring(0, cut) + "\n" + episode_name.substring(cut + 1); 
       }catch(Exception e){ 
        episode_name = episode_name.substring(0,10)+"..."; 
       } 
      } 
      nameView.setText(episode_name); 

      ImageView image = (ImageView) view.findViewById(R.id.episode_watched); 
      String wasWatched = cursor.getString(cursor.getColumnIndexOrThrow(DbAdapter.EPISODE_KEY_WATCHED)); 
      wasWatched = wasWatched == null ? "0" : wasWatched; 
      int watched_img = 0; 
      switch (wasWatched) { 
       case ("0"): 
        watched_img = R.drawable.unwatched; 
        break; 
       case ("1"): 
        watched_img = R.drawable.watched; 
        break; 
      } 
      image.setImageResource(watched_img); 
     } 
    } 
} 

/** 
* A {@link FragmentPagerAdapter} that returns a fragment corresponding to 
* one of the seasons. 
*/ 
protected class SeasonPagerAdapter extends FragmentStatePagerAdapter { 

    private ArrayList<Integer> seasons; // Holds all the seasons for the series 
    private long seriesId;    // Id of the series 

    public SeasonPagerAdapter(FragmentManager fm, ArrayList<Integer> seasons, long seriesId) { 
     super(fm); 
     this.seasons = seasons; 
     this.seriesId = seriesId; 
    } 

    @Override 
    public Fragment getItem(int seasonNum) { 
     // getItem is called to instantiate the fragment for the given page. 
     // Return a PlaceholderFragment (defined as a static inner class below). 
     return SeasonFragment.newInstance(seasons, seasonNum, seriesId); 
    } 

    /** 
    * Forces all fragments to reload on update 
    */ 
    @Override 
    public int getItemPosition(Object object) { 
     // There are more efficient implementations. 
     return POSITION_NONE; 
    } 

    /** 
    * @return amount of tabs in the view. 
    */ 
    public int getCount() { 
     // Since the description is in the first tab the count is one more 
     // than the number of seasons 
     return seasons.size() + 1; 
    } 

    @Override 
    public CharSequence getPageTitle(int position) { 
     // The first tab holds the description 
     if (position == 0) return "Description"; 
     else { 
      // The title contains the number of the season for the tab 
      int season = seasons.get(position - 1); 
      if (season >= 10) return "S" + season; 
      else return "S0" + season; 
     } 
    } 
} 
} 

Die Tätigkeit des Layouts:

<LinearLayout 
    android:id="@+id/test" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent" 
    android:orientation="vertical"> 


<android.support.design.widget.AppBarLayout 
    android:id="@+id/appbar" 
    android:layout_width="match_parent" 
    android:layout_height="wrap_content" 
    android:paddingTop="@dimen/appbar_padding_top" 
    android:theme="@style/AppTheme.AppBarOverlay"> 

    <android.support.v7.widget.Toolbar 
     android:id="@+id/toolbar" 
     android:layout_width="match_parent" 
     android:layout_height="?attr/actionBarSize" 
     android:background="?attr/colorPrimary" 
     app:layout_scrollFlags="scroll|enterAlways" 
     app:popupTheme="@style/AppTheme.PopupOverlay"> 

    </android.support.v7.widget.Toolbar> 

    <android.support.design.widget.TabLayout 
     android:id="@+id/tabs" 
     android:layout_width="match_parent" 
     android:layout_height="wrap_content" 
     android:layout_gravity="center_horizontal" 
     app:tabMode="scrollable"/> 

</android.support.design.widget.AppBarLayout> 

<android.support.v4.view.ViewPager 
    android:id="@+id/container" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent" 
    app:layout_behavior="@string/appbar_scrolling_view_behavior" /> 

</LinearLayout> 

(Inhalt Layout)

<?xml version="1.0" encoding="utf-8"?> 
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    xmlns:app="http://schemas.android.com/apk/res-auto" 
    xmlns:tools="http://schemas.android.com/tools" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent" 
    android:paddingBottom="@dimen/activity_vertical_margin" 
    android:paddingLeft="@dimen/activity_horizontal_margin" 
    android:paddingRight="@dimen/activity_horizontal_margin" 
    android:paddingTop="@dimen/activity_vertical_margin" 
    app:layout_behavior="@string/appbar_scrolling_view_behavior" 
    tools:context=".activities.ListEpisodes" 
    tools:showIn="@layout/activity_list_episodes"> 

    <LinearLayout 
     android:orientation="vertical" 
     android:id="@+id/linear_layout" 
     android:layout_width="match_parent" 
     android:layout_height="match_parent" 
     android:layout_alignParentTop="true" 
     android:layout_alignParentLeft="true" 
     android:layout_alignParentStart="true"> 

     <TextView 
      android:layout_width="wrap_content" 
      android:layout_height="wrap_content" 
      android:textAppearance="?android:attr/textAppearanceLarge" 
      android:text="" 
      android:id="@+id/series_title" /> 

     <TextView 
      android:layout_width="wrap_content" 
      android:layout_height="wrap_content" 
      android:textAppearance="?android:attr/textAppearanceMedium" 
      android:text="" 
      android:id="@+id/series_description" /> 
    </LinearLayout> 
</RelativeLayout> 

eine einzelne Zeile Layout:

<?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="match_parent" 
    android:layout_alignParentLeft="true" 
    android:layout_alignParentStart="true" 
    android:layout_alignParentTop="true" 
    android:orientation="horizontal"> 

    <LinearLayout 
     android:layout_width="match_parent" 
     android:layout_height="match_parent"> 

     <TextView 
      android:layout_width="wrap_content" 
      android:layout_height="wrap_content" 
      android:text="@string/EDot" 
      android:textSize="25sp" /> 

     <TextView 
      android:id="@+id/episode_number" 
      android:layout_width="wrap_content" 
      android:layout_height="wrap_content" 
      android:textSize="25sp" /> 

     <TextView 
      android:layout_width="wrap_content" 
      android:layout_height="wrap_content" 
      android:text="@string/space" 
      android:textSize="25sp" /> 

     <TextView 
      android:id="@+id/episode_name" 
      android:layout_width="wrap_content" 
      android:layout_height="wrap_content" 
      android:textSize="25sp" /> 
    </LinearLayout> 

    <ImageView 
     android:id="@+id/episode_watched" 
     android:layout_width="50dp" 
     android:layout_height="50dp" 
     android:layout_alignParentEnd="true" 
     android:layout_alignParentRight="true" /> 

</RelativeLayout> 

Ich habe versucht, in SeasonFragment toggleWatched viele Dinge, die in dem Verfahren zu ändern, die am Ende auf dem EpisodeListViewAdapter die Methode Bindview android nennen machen (tatsächlich, ich habe überprüft, dass die Methode für eine bestimmte Episode mehrmals aufgerufen wird), aber die Ansicht wird nicht aktualisiert. Vielleicht stimmt etwas nicht mit der Art und Weise, wie der Adapter wählt, welche Registerkarte welche Daten enthält? Aber warum sollte es in einigen Fällen funktionieren?

Antwort

0

es Gelöst wurde das Problem hier:

private void toggleWatched(long episodeId) { 
    DbAdapter mDbAdapter = new DbAdapter(this.getActivity()); 
    mDbAdapter.open(); 
    mDbAdapter.toggleWatched(episodeId); 
    // Get seriesId and fetch episodes for the season. 
    Cursor episodes = mDbAdapter.fetchEpisodesFromSeason(getArguments().getLong(ARG_SERIES_ID), 
      season); 
    ListView episodeList = (ListView) this.getActivity().findViewById(R.id.episode_list); 
    EpisodeListViewAdapter lva = ((EpisodeListViewAdapter) episodeList.getAdapter()); 
    // Sometimes this works sometimes it doesn't 
    lva.swapCursor(episodes); 
    lva.notifyDataSetChanged(); 
    episodeList.setAdapter(lva); 
} 

Die Linie

ListView episodeList = (ListView)this.getActivity().findViewById(R.id.episode_list); 

falsch und Art unneccesary war. Diese Zeile würde ein anderes ListView von (wahrscheinlich) dem letzten fraglichen Fragment erhalten. Also habe ich die ListView, die ich von der Absicht des Fragments erhalten habe, einfach als private Variable gespeichert und hier benutzt.