2016-05-18 117 views
0

我試圖讓這個簡單的應用程序工作無濟於事。Android片段tablayout更新ListView問題

此活動應該顯示一系列包含一系列季節的選項卡。第一個標籤包含它的描述,下一個標籤包含它在ListRows中的插曲。

可以點擊每一集的行來切換數據庫中的布爾值,當true表示用戶已經觀看了該集改變了ListView中的行右側的圖標,並且圖標將會改變在行上。 我無法更新ListViews。對於第一季的標籤,它完美的工作,你點擊一行,圖標變化。但是,當我去到當前一個的右邊的標籤變得怪異的時候,圖標不會更新(儘管數據庫中的值被更新),如果現在我滑動到另一個標籤,這個值將從最後一個和圖標將具有正確的值。

我迷茫

活動:

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

活動的佈局:

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

(內容佈局)

<?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> 

單行的佈局:

<?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> 

我試圖改變在SeasonFragment toggleWatched的方法,最終使機器人呼籲EpisodeListViewAdapter方法bindView(其實我很多東西檢查該方法在給定情節中被多次調用),但視圖不會更新。也許在適配器選擇哪個選項卡保存哪些數據的方式上有什麼問題?但是,爲什麼它會在某些情況下起作用。

回答

0

解決了這個問題,這個問題在這裏:

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

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

是錯誤的,那種unneccesary。這行會從(可能)最後一個視圖中得到一些其他ListView。所以,我只是將從意圖獲得的ListView作爲一個私有變量存儲在這裏,並在此處使用它。