ViewPager 中的片段在旋转时未在其 RecyclerView 中显示任何内容
Fragment in ViewPager is not displaying anything in its RecyclerView on rotation
我在与 TabLayout 集成的 ViewPager 中有 4 个片段。这些片段中的每一个都包含一个 RecyclerView,因为我正在显示未知数量的列表项。这些项目按日期加载,所以我有两个按钮可以让您更改日期,然后根据设置的日期加载列表项目中的适当数据。这些按钮是外部 activity.
的一部分
到目前为止一切正常,但是一旦我旋转设备,片段就不会加载任何东西。片段内不显示任何内容。不过,外部 activity 中的视图显示良好。奇怪的是,如果我触摸更改日期的按钮之一,就会加载片段并正确显示数据。但是我需要在旋转后立即显示所有内容,而不仅仅是当我单击其中一个按钮时。这是我的代码:
public class MainActivity extends AppCompatActivity implements DatePickerFragment.DateDialogInterface
{
private final static String COLOR_CONTROL_CHOSEN = "color_control_chosen";
private static final String DIALOG_DATE = "dialog_date";
private static final String TAG = "MainActivity";
private static final String SET_LOCATION = "set_location";
public static final String LEAGUE_POSITION = "league_position";
private Toolbar mToolbar;
private ViewPager mViewPager;
private TabLayout mTabLayout;
private ImageButton mPrevDateButton, mNextDateButton;
private TextView mDateTextView, mLeagueTextView;
private String mTodaysDate;
private Date mLastSelectedDate, mTodaysDateObj;
private ProgressBar mProgressBar;
private FragmentPagerAdapter mAdapterViewPager;
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mToolbar = (Toolbar) findViewById(R.id.outnix_toolbar);
mViewPager = (ViewPager) findViewById(R.id.main_activity_view_pager);
mTabLayout = (TabLayout) findViewById(R.id.main_activity_tab_layout);
mDateTextView = (TextView) findViewById(R.id.display_date_text_view);
mLeagueTextView = (TextView) findViewById(R.id.league_title_header);
mPrevDateButton = (ImageButton) findViewById(R.id.previous_date_button);
mNextDateButton = (ImageButton) findViewById(R.id.next_date_button);
mProgressBar = (ProgressBar) findViewById(R.id.loading_circle);
mProgressBar.setVisibility(View.GONE);
setTodaysDate();
mDateTextView.setText("Today");
setSupportActionBar(mToolbar);
assert getSupportActionBar() != null;
getSupportActionBar().setDisplayShowTitleEnabled(false);
NavDrawerFragment navDrawerFrag = (NavDrawerFragment) getSupportFragmentManager().findFragmentById(R.id.fragment_nav_drawer);
navDrawerFrag.setUp((DrawerLayout) findViewById(R.id.nav_drawer), mToolbar);
setUpViewPagerAdapterOnCreate();
mTabLayout.setupWithViewPager(mViewPager);
mDateTextView.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View v)
{
FragmentManager fm = getSupportFragmentManager();
DatePickerFragment dialog = new DatePickerFragment();
Bundle args = new Bundle();
args.putSerializable("ARG_DATE", mLastSelectedDate);
dialog.setArguments(args);
dialog.show(fm, DIALOG_DATE);
}
});
THESE TWO LISTENERS ARE WHAT DISPLAY THE DATA AFTER A ROTATION CHANGE
//////////////////////////////////////////////////////////////////
mPrevDateButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v)
{
mProgressBar.setVisibility(View.VISIBLE);
updateDate(-1);
setupViewPagerAdapter(getViewPagerPos());
}
});
mNextDateButton.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View v)
{
mProgressBar.setVisibility(View.VISIBLE);
updateDate(1);
setupViewPagerAdapter(getViewPagerPos());
}
});
}
////////////////////////////////////////////////////////////////////
/* @Override
protected void onResume()
{
super.onResume();
setupViewPagerAdapter(getViewPagerPos());
}*/
private void setUpViewPagerAdapterOnCreate() //only called when opened from LauncherActivity
{
setupViewPagerAdapter(0);
int touchColorPixel = getIntent().getIntExtra(COLOR_CONTROL_CHOSEN, ContextCompat.getColor(this, R.color.blue));
if(touchColorPixel == ContextCompat.getColor(this, R.color.orange))
mViewPager.setCurrentItem(0);
else if(touchColorPixel == ContextCompat.getColor(this, R.color.blue))
mViewPager.setCurrentItem(1);
else
mViewPager.setCurrentItem(3);
}
private void setupViewPagerAdapter(int currentPos)
{
mAdapterViewPager = new MainPagerAdapter(getSupportFragmentManager());
mViewPager.setAdapter(mAdapterViewPager);
mViewPager.setOffscreenPageLimit(4);
mViewPager.setCurrentItem(currentPos);
}
@Override
protected void onSaveInstanceState(Bundle outState)
{
super.onSaveInstanceState(outState);
outState.putSerializable(DIALOG_DATE, mLastSelectedDate); //save the last date selected (or last date displayed) on rotation change.
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState)
{
super.onRestoreInstanceState(savedInstanceState);
setDateFromDialog((Date) savedInstanceState.getSerializable(DIALOG_DATE)); //restore the last date selected to the new rotation change
}
public ProgressBar getProgressBar()
{
return mProgressBar;
}
@Override
public void setDateFromDialog(Date date)
{
String newDate = getFormattedDate(date);
updateTodaysDate();
mLastSelectedDate = date;
setupViewPagerAdapter(getViewPagerPos());
if(newDate.equals(mTodaysDate))
mDateTextView.setText("Today");
else
mDateTextView.setText(newDate);
}
private int getViewPagerPos()
{
return mViewPager.getCurrentItem();
}
}
2 个按钮 mPrevDateButton
和 mNextDateButton
是单击时 "fix" 问题的按钮。在他们的点击监听器中,updateDate()
方法只做与 TextView 对象相关的事情,这就是为什么我没有包含定义(我试图让 post 尽可能短)。所以我确定它与我的 setupViewPagerAdapter()
方法有关。我已尝试在 onCreate()
和 onResume()
中添加此方法,但片段仍为空白。
这是我的 ViewPager 适配器代码:
public class MainPagerAdapter extends FragmentPagerAdapter
{
private final String[] mFragmentTitleList = {"My Games", "All Games", "All Places", "My Places"}; //tab title names
private final static int NUM_FRAGMENTS = 4;
public MainPagerAdapter(FragmentManager fm)
{
super(fm);
}
@Override
public Fragment getItem(int position)
{
switch(position)
{
case 0:
return MyGamesFragment.newInstance();
case 1:
return AllGamesFragment.newInstance();
case 2:
return AllPlacesFragment.newInstance();
case 3:
return MyPlacesFragment.newInstance();
default:
return null;
}
}
@Override
public int getCount()
{
return NUM_FRAGMENTS;
}
@Override
public CharSequence getPageTitle(int position)
{
return mFragmentTitleList[position];
}
}
这是4个片段之一。他们几乎都有相似的设计。
public class AllGamesFragment extends Fragment
{
private SwipeRefreshLayout mSwipeRefreshLayout;
private RecyclerView mAllGamesRecyclerView;
private GameAdapter mGameAdapter;
private List<Game> mGameList = new ArrayList<>();
private MainActivity mMainActivity; //keeps a reference to MainActivity to access public methods
public static AllGamesFragment newInstance()
{
return new AllGamesFragment();
}
@Override
public void onAttach(Context context)
{
super.onAttach(context);
mMainActivity = (MainActivity) context;
}
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setRetainInstance(true);
mMainActivity.getProgressBar().setVisibility(View.VISIBLE);
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
View view = inflater.inflate(R.layout.recycler_view_all_games, container, false);
mAllGamesRecyclerView = (RecyclerView) view.findViewById(R.id.all_games_recycler_view);
mAllGamesRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
mSwipeRefreshLayout = (SwipeRefreshLayout) view.findViewById(R.id.swipe_refresh);
mSwipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener()
{
@Override
public void onRefresh()
{
getGamesData();
mSwipeRefreshLayout.setRefreshing(false);
}
});
return view;
}
@Override
public void onResume()
{
super.onResume();
getGamesData();
}
private void setupAdapter()
{
if(isAdded())
{
mGameAdapter = new GameAdapter(mGameList);
mAllGamesRecyclerView.setAdapter(mGameAdapter);
}
}
private void getGamesData()
{
new GetDataTask(mMainActivity.getLastSelectedDate()).execute();
}
private class GetDataTask extends Scraper.GameDataTask
{
GetDataTask(Date date)
{
super(date, getActivity());
}
@Override
protected void onPostExecute(List<Game> games)
{
mGameList = games;
setupAdapter();
mMainActivity.getProgressBar().setVisibility(View.GONE);
}
}
private class GameAdapter extends RecyclerView.Adapter<GameHolder>
{
private List<Game> mGames;
public GameAdapter(List<Game> games)
{
mGames = games;
}
@Override
public GameHolder onCreateViewHolder(ViewGroup parent, int viewType)
{
LayoutInflater inflater = LayoutInflater.from(getActivity());
View view = inflater.inflate(R.layout.list_game_item, parent, false);
return new GameHolder(view, getActivity());
}
@Override
public void onBindViewHolder(GameHolder holder, int position)
{
holder.bindGameData(mGames.get(position));
}
@Override
public int getItemCount()
{
return mGames.size();
}
}
我在这里看了很多 post,更改了我的代码很多次,但仍然无法正常工作。当我单击其中一个日期按钮时,我只是感到困惑 "fixes"。如果您希望我 post 更多代码,请告诉我。很抱歉 post 这么长,但我觉得需要上下文。非常感谢任何帮助!
我想通了!我不得不切换到 FragmentStatePagerAdapter
而不是使用 FragmentPagerAdapter
。我相信这与碎片被销毁并重新创建有关。
我还读到另一种方法是将 ViewPager 包装在一个片段中,当您创建它的适配器时,您使用 getChildFragmentManager()
而不是 getSupportFragmentManager()
像这样:
mAdapterViewPager = new MyPagerAdapter(getChildFragmentManager());
我不想为了解决这个问题而不得不在我的 ViewPager 周围添加整个片段,所以我改用 FragmentStatePagerAdapter
并且成功了!希望这可以帮助其他人。
使 viewPager 适配器扩展 FragmentStatePagerAdapter 而不是 FragmentPagerAdapter 应该可以解决问题。
我在与 TabLayout 集成的 ViewPager 中有 4 个片段。这些片段中的每一个都包含一个 RecyclerView,因为我正在显示未知数量的列表项。这些项目按日期加载,所以我有两个按钮可以让您更改日期,然后根据设置的日期加载列表项目中的适当数据。这些按钮是外部 activity.
的一部分到目前为止一切正常,但是一旦我旋转设备,片段就不会加载任何东西。片段内不显示任何内容。不过,外部 activity 中的视图显示良好。奇怪的是,如果我触摸更改日期的按钮之一,就会加载片段并正确显示数据。但是我需要在旋转后立即显示所有内容,而不仅仅是当我单击其中一个按钮时。这是我的代码:
public class MainActivity extends AppCompatActivity implements DatePickerFragment.DateDialogInterface
{
private final static String COLOR_CONTROL_CHOSEN = "color_control_chosen";
private static final String DIALOG_DATE = "dialog_date";
private static final String TAG = "MainActivity";
private static final String SET_LOCATION = "set_location";
public static final String LEAGUE_POSITION = "league_position";
private Toolbar mToolbar;
private ViewPager mViewPager;
private TabLayout mTabLayout;
private ImageButton mPrevDateButton, mNextDateButton;
private TextView mDateTextView, mLeagueTextView;
private String mTodaysDate;
private Date mLastSelectedDate, mTodaysDateObj;
private ProgressBar mProgressBar;
private FragmentPagerAdapter mAdapterViewPager;
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mToolbar = (Toolbar) findViewById(R.id.outnix_toolbar);
mViewPager = (ViewPager) findViewById(R.id.main_activity_view_pager);
mTabLayout = (TabLayout) findViewById(R.id.main_activity_tab_layout);
mDateTextView = (TextView) findViewById(R.id.display_date_text_view);
mLeagueTextView = (TextView) findViewById(R.id.league_title_header);
mPrevDateButton = (ImageButton) findViewById(R.id.previous_date_button);
mNextDateButton = (ImageButton) findViewById(R.id.next_date_button);
mProgressBar = (ProgressBar) findViewById(R.id.loading_circle);
mProgressBar.setVisibility(View.GONE);
setTodaysDate();
mDateTextView.setText("Today");
setSupportActionBar(mToolbar);
assert getSupportActionBar() != null;
getSupportActionBar().setDisplayShowTitleEnabled(false);
NavDrawerFragment navDrawerFrag = (NavDrawerFragment) getSupportFragmentManager().findFragmentById(R.id.fragment_nav_drawer);
navDrawerFrag.setUp((DrawerLayout) findViewById(R.id.nav_drawer), mToolbar);
setUpViewPagerAdapterOnCreate();
mTabLayout.setupWithViewPager(mViewPager);
mDateTextView.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View v)
{
FragmentManager fm = getSupportFragmentManager();
DatePickerFragment dialog = new DatePickerFragment();
Bundle args = new Bundle();
args.putSerializable("ARG_DATE", mLastSelectedDate);
dialog.setArguments(args);
dialog.show(fm, DIALOG_DATE);
}
});
THESE TWO LISTENERS ARE WHAT DISPLAY THE DATA AFTER A ROTATION CHANGE
//////////////////////////////////////////////////////////////////
mPrevDateButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v)
{
mProgressBar.setVisibility(View.VISIBLE);
updateDate(-1);
setupViewPagerAdapter(getViewPagerPos());
}
});
mNextDateButton.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View v)
{
mProgressBar.setVisibility(View.VISIBLE);
updateDate(1);
setupViewPagerAdapter(getViewPagerPos());
}
});
}
////////////////////////////////////////////////////////////////////
/* @Override
protected void onResume()
{
super.onResume();
setupViewPagerAdapter(getViewPagerPos());
}*/
private void setUpViewPagerAdapterOnCreate() //only called when opened from LauncherActivity
{
setupViewPagerAdapter(0);
int touchColorPixel = getIntent().getIntExtra(COLOR_CONTROL_CHOSEN, ContextCompat.getColor(this, R.color.blue));
if(touchColorPixel == ContextCompat.getColor(this, R.color.orange))
mViewPager.setCurrentItem(0);
else if(touchColorPixel == ContextCompat.getColor(this, R.color.blue))
mViewPager.setCurrentItem(1);
else
mViewPager.setCurrentItem(3);
}
private void setupViewPagerAdapter(int currentPos)
{
mAdapterViewPager = new MainPagerAdapter(getSupportFragmentManager());
mViewPager.setAdapter(mAdapterViewPager);
mViewPager.setOffscreenPageLimit(4);
mViewPager.setCurrentItem(currentPos);
}
@Override
protected void onSaveInstanceState(Bundle outState)
{
super.onSaveInstanceState(outState);
outState.putSerializable(DIALOG_DATE, mLastSelectedDate); //save the last date selected (or last date displayed) on rotation change.
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState)
{
super.onRestoreInstanceState(savedInstanceState);
setDateFromDialog((Date) savedInstanceState.getSerializable(DIALOG_DATE)); //restore the last date selected to the new rotation change
}
public ProgressBar getProgressBar()
{
return mProgressBar;
}
@Override
public void setDateFromDialog(Date date)
{
String newDate = getFormattedDate(date);
updateTodaysDate();
mLastSelectedDate = date;
setupViewPagerAdapter(getViewPagerPos());
if(newDate.equals(mTodaysDate))
mDateTextView.setText("Today");
else
mDateTextView.setText(newDate);
}
private int getViewPagerPos()
{
return mViewPager.getCurrentItem();
}
}
2 个按钮 mPrevDateButton
和 mNextDateButton
是单击时 "fix" 问题的按钮。在他们的点击监听器中,updateDate()
方法只做与 TextView 对象相关的事情,这就是为什么我没有包含定义(我试图让 post 尽可能短)。所以我确定它与我的 setupViewPagerAdapter()
方法有关。我已尝试在 onCreate()
和 onResume()
中添加此方法,但片段仍为空白。
这是我的 ViewPager 适配器代码:
public class MainPagerAdapter extends FragmentPagerAdapter
{
private final String[] mFragmentTitleList = {"My Games", "All Games", "All Places", "My Places"}; //tab title names
private final static int NUM_FRAGMENTS = 4;
public MainPagerAdapter(FragmentManager fm)
{
super(fm);
}
@Override
public Fragment getItem(int position)
{
switch(position)
{
case 0:
return MyGamesFragment.newInstance();
case 1:
return AllGamesFragment.newInstance();
case 2:
return AllPlacesFragment.newInstance();
case 3:
return MyPlacesFragment.newInstance();
default:
return null;
}
}
@Override
public int getCount()
{
return NUM_FRAGMENTS;
}
@Override
public CharSequence getPageTitle(int position)
{
return mFragmentTitleList[position];
}
}
这是4个片段之一。他们几乎都有相似的设计。
public class AllGamesFragment extends Fragment
{
private SwipeRefreshLayout mSwipeRefreshLayout;
private RecyclerView mAllGamesRecyclerView;
private GameAdapter mGameAdapter;
private List<Game> mGameList = new ArrayList<>();
private MainActivity mMainActivity; //keeps a reference to MainActivity to access public methods
public static AllGamesFragment newInstance()
{
return new AllGamesFragment();
}
@Override
public void onAttach(Context context)
{
super.onAttach(context);
mMainActivity = (MainActivity) context;
}
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setRetainInstance(true);
mMainActivity.getProgressBar().setVisibility(View.VISIBLE);
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
View view = inflater.inflate(R.layout.recycler_view_all_games, container, false);
mAllGamesRecyclerView = (RecyclerView) view.findViewById(R.id.all_games_recycler_view);
mAllGamesRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
mSwipeRefreshLayout = (SwipeRefreshLayout) view.findViewById(R.id.swipe_refresh);
mSwipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener()
{
@Override
public void onRefresh()
{
getGamesData();
mSwipeRefreshLayout.setRefreshing(false);
}
});
return view;
}
@Override
public void onResume()
{
super.onResume();
getGamesData();
}
private void setupAdapter()
{
if(isAdded())
{
mGameAdapter = new GameAdapter(mGameList);
mAllGamesRecyclerView.setAdapter(mGameAdapter);
}
}
private void getGamesData()
{
new GetDataTask(mMainActivity.getLastSelectedDate()).execute();
}
private class GetDataTask extends Scraper.GameDataTask
{
GetDataTask(Date date)
{
super(date, getActivity());
}
@Override
protected void onPostExecute(List<Game> games)
{
mGameList = games;
setupAdapter();
mMainActivity.getProgressBar().setVisibility(View.GONE);
}
}
private class GameAdapter extends RecyclerView.Adapter<GameHolder>
{
private List<Game> mGames;
public GameAdapter(List<Game> games)
{
mGames = games;
}
@Override
public GameHolder onCreateViewHolder(ViewGroup parent, int viewType)
{
LayoutInflater inflater = LayoutInflater.from(getActivity());
View view = inflater.inflate(R.layout.list_game_item, parent, false);
return new GameHolder(view, getActivity());
}
@Override
public void onBindViewHolder(GameHolder holder, int position)
{
holder.bindGameData(mGames.get(position));
}
@Override
public int getItemCount()
{
return mGames.size();
}
}
我在这里看了很多 post,更改了我的代码很多次,但仍然无法正常工作。当我单击其中一个日期按钮时,我只是感到困惑 "fixes"。如果您希望我 post 更多代码,请告诉我。很抱歉 post 这么长,但我觉得需要上下文。非常感谢任何帮助!
我想通了!我不得不切换到 FragmentStatePagerAdapter
而不是使用 FragmentPagerAdapter
。我相信这与碎片被销毁并重新创建有关。
我还读到另一种方法是将 ViewPager 包装在一个片段中,当您创建它的适配器时,您使用 getChildFragmentManager()
而不是 getSupportFragmentManager()
像这样:
mAdapterViewPager = new MyPagerAdapter(getChildFragmentManager());
我不想为了解决这个问题而不得不在我的 ViewPager 周围添加整个片段,所以我改用 FragmentStatePagerAdapter
并且成功了!希望这可以帮助其他人。
使 viewPager 适配器扩展 FragmentStatePagerAdapter 而不是 FragmentPagerAdapter 应该可以解决问题。