Этот проект является расширением шаблона потока Master/Detail, который можно найти в Android Studio. Разница в том, что в этом приложении используется одно действие и ViewPager, который управляет тремя фрагментами. Третий фрагмент - это фрагмент мастера (списка), который содержит интерактивный RecycleView. Когда элемент списка щелкнут, он переключает фрагмент с фрагментом Child (detail).
Пока проект работает, я бы хотел избежать использования android:configChanges= "orientation|keyboardHidden|screenSize"
в манифесте. Как мне это сделать?
Если атрибут удален, mListener в ItemListFragment уничтожается вместе с ItemFragmentList при вращении, но никогда не воссоздается при воссоздании ItemListFragment. Это приводит к тому, что ничего не происходит при нажатии на элемент "Список" в режиме "Портрет".
Моим первоначальным решением было вручную переопределить обработку изменений конфигурации, что означало, что ItemListFragment не был разрушен при вращении экрана. onConfigurationChanged()
и populateViewForOrientation()
были добавлены для повторного раздувания макета. Разумеется, есть лучшее решение, чем ручное переопределение обработки конфигурации.
Проект доступен по адресу: https://github.com/lukeallison/ViewPagerMasterDetail, Видео: http://tinypic.com/r/1zltyeq/9
BaseFragment.java
public class BaseFragment extends Fragment {
public Bridge mBridget;
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
mBridget = (MainActivity) getActivity();
}
}
Интерфейс: Bridge.java
public interface Bridge {
abstract void onBack();
}
ItemListFragment.java
public class ItemListFragment extends BaseFragment{
private boolean mTwoPane = false;
private PageFragmentListener mListener;
public static ItemListFragment newInstance(PageFragmentListener listener) {
ItemListFragment fragment = new ItemListFragment();
fragment.mListener = listener;
return fragment;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.fragment_item_list, container, false);
initLayout(root);
return root;
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
LayoutInflater inflater = LayoutInflater.from(getActivity());
populateViewForOrientation(inflater, (ViewGroup) getView());
}
private void populateViewForOrientation(LayoutInflater inflater, ViewGroup viewGroup) {
viewGroup.removeAllViewsInLayout();
View subview = inflater.inflate(R.layout.fragment_item_list, viewGroup);
initLayout(subview);
}
public void initLayout(View root) {
View recyclerView = root.findViewById(R.id.item_list);
mTwoPane = false;
if (root.findViewById(R.id.item_detail_container) != null) { // R.layout.list_item is located "layout", "layout-land"..
mTwoPane = true; // currently loaded "layout-land/list_item". landscape mode
}
Toolbar toolbar = (Toolbar) root.findViewById(R.id.toolbar);
((AppCompatActivity)getActivity()).setSupportActionBar(toolbar);
toolbar.setTitle("List");
assert recyclerView != null;
setupRecyclerView((RecyclerView) recyclerView);
}
private void setupRecyclerView(RecyclerView recyclerView) {
recyclerView.setAdapter(new SimpleItemRecyclerViewAdapter(DummyContent.ITEMS));
}
public class SimpleItemRecyclerViewAdapter
extends RecyclerView.Adapter<SimpleItemRecyclerViewAdapter.ViewHolder> {
private final List<DummyContent.DummyItem> mValues;
public SimpleItemRecyclerViewAdapter(List<DummyContent.DummyItem> items) {
mValues = items;
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.item_list_content, parent, false);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(final ViewHolder holder, int position) {
holder.mItem = mValues.get(position);
holder.mIdView.setText(mValues.get(position).id);
holder.mContentView.setText(mValues.get(position).content);
// One row of List. define click event
holder.mView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mTwoPane) { // landscape mode
Bundle arguments = new Bundle();
arguments.putString(Constants.ARG_ITEM_ID, holder.mItem.id);
ItemTwoDetailFragment fragment = ItemTwoDetailFragment.newInstance();
fragment.setArguments(arguments);
// show detail fragment to right side of screen
getActivity().getSupportFragmentManager().beginTransaction()
.replace(R.id.item_detail_container, fragment)
.commit();
} else { // portrait mode
if (mListener!=null)
mListener.onSwitchToNextFragment(holder.mItem.id); // switch detail fragment
}
}
});
}
@Override
public int getItemCount() {
return mValues.size();
}
public class ViewHolder extends RecyclerView.ViewHolder {
public final View mView;
public final TextView mIdView;
public final TextView mContentView;
public DummyContent.DummyItem mItem;
public ViewHolder(View view) {
super(view);
mView = view;
mIdView = (TextView) view.findViewById(R.id.id);
mContentView = (TextView) view.findViewById(R.id.content);
}
@Override
public String toString() {
return super.toString() + " '" + mContentView.getText() + "'";
}
}
}
}
ItemOneDetailFragment.java
/**
* A fragment representing a single Item detail screen.
* on handsets.
*/
public class ItemOneDetailFragment extends BaseFragment {
private DummyContent.DummyItem mItem;
/*
Listener for switch fragment
*/
private PageFragmentListener mListener;
public static ItemOneDetailFragment newInstance(PageFragmentListener listener) {
ItemOneDetailFragment fragment = new ItemOneDetailFragment();
fragment.mListener = listener;
return fragment;
}
public ItemOneDetailFragment() {
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments().containsKey(Constants.ARG_ITEM_ID)) {
mItem = DummyContent.ITEM_MAP.get(getArguments().getString(Constants.ARG_ITEM_ID)); // Get selected Item ID to show details
}
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_item_one_detail, container, false);
// Show the dummy content as text in a TextView.
if (mItem != null) {
Toolbar toolbar = (Toolbar) rootView.findViewById(R.id.detail_toolbar);
toolbar.setTitle(mItem.content);
toolbar.setNavigationIcon(R.drawable.ic_ab_back_material);
toolbar.setNavigationOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mBridget.onBack();
}
});
((TextView) rootView.findViewById(R.id.item_detail)).setText(mItem.details); // show details
}
return rootView;
}
}
ItemTwoDetailFragment.java
// for landscape orientation
public class ItemTwoDetailFragment extends BaseFragment {
private DummyContent.DummyItem mItem;
public static ItemTwoDetailFragment newInstance() {
return new ItemTwoDetailFragment();
}
public ItemTwoDetailFragment() {
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments().containsKey(Constants.ARG_ITEM_ID)) {
mItem = DummyContent.ITEM_MAP.get(getArguments().getString(Constants.ARG_ITEM_ID)); // Get selected item to show details
}
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_item_two_detail, container, false);
// Show the dummy content as text in a TextView.
if (mItem != null) {
((TextView) rootView.findViewById(R.id.item_detail)).setText(mItem.details); // show details
}
return rootView;
}
}
MainActivity.java
public class MainActivity extends AppCompatActivity implements Bridge{
private ViewPager viewPager = null;
private MyAdapter mAdapter;
PageChangeListener mListener = new PageChangeListener();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
viewPager = (ViewPager) findViewById(R.id.pager);
viewPager.setOnPageChangeListener(mListener); // Page Change Listener
mAdapter = new MyAdapter(getSupportFragmentManager());
viewPager.setAdapter(mAdapter);
// Show the Up button in the action bar.
ActionBar actionBar = getSupportActionBar();
if (actionBar != null) {
actionBar.setDisplayHomeAsUpEnabled(true);
}
}
@Override
public void onBackPressed() {
if (mListener.getCurrentPage()==2 && mAdapter.mFragment instanceof ItemOneDetailFragment) { // current page is Tab-3, current fragment is detail fragment
mAdapter.mListener.onSwitchToNextFragment("0"); // show List fragment
return; // prevent to finish app.
}
super.onBackPressed();
}
// Do the same thing as the back button - go back to ItemListFragment
// Only when in ItemOneDetailFragment
@Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == android.R.id.home) {
if (mListener.getCurrentPage() == 2 && mAdapter.mFragment instanceof ItemOneDetailFragment) { // current page is Tab-3, current fragment is detail fragment
mAdapter.mListener.onSwitchToNextFragment("0"); // show List fragment
// return; // prevent to finish app.
}
}
return super.onOptionsItemSelected(item);
}
@Override
public void onBack() {
onBackPressed();
}
}
class PageChangeListener extends ViewPager.SimpleOnPageChangeListener {
private int currentPage;
@Override
public void onPageSelected(int position) {
currentPage = position; // current selected page
}
public final int getCurrentPage() {
return currentPage;
}
}
/**
* Fragment Page Adapter
*/
class MyAdapter extends FragmentPagerAdapter{
private final FragmentManager mFragmentManager;
public BaseFragment mFragment;
/**
* PageFragmentListener for switching fragment.
*/
public PageFragmentListener mListener = new PageFragmentListener() {
@Override
public void onSwitchToNextFragment(final String id) {
mFragmentManager.beginTransaction().remove(mFragment).commit();
if (mFragment instanceof ItemListFragment){ // current fragment is List Fragment
Bundle arguments = new Bundle();
arguments.putString(Constants.ARG_ITEM_ID, id); // selected item id
mFragment = ItemOneDetailFragment.newInstance(mListener); // switch detail fragment
mFragment.setArguments(arguments);
}else{ // DetailFragment
mFragment = ItemListFragment.newInstance(mListener); // => switch list fragment
}
notifyDataSetChanged(); // notify changes
}
};
public MyAdapter(FragmentManager fm) {
super(fm);
mFragmentManager = fm;
}
@Override
public Fragment getItem(int position) {
if (position == 0) // Tab-1
return FragmentA.newInstance();
if (position == 1) // Tab-2
return FragmentB.newInstance();
if (position == 2) { // Tab-3
if (mFragment==null) // first time => create list fragment
mFragment = ItemListFragment.newInstance(mListener);
return mFragment;
}
return null;
}
@Override
public CharSequence getPageTitle(int position) {
if (position == 0) { // Tab-1
return "Tab 1";
}
if (position == 1) { // Tab-2
return "Tab 2";
}
if (position == 2) { //Tab-3
return "Tab 3";
}
return null;
}
@Override
public int getCount() { // Count of Tabs
return 3;
}
@Override
public int getItemPosition(Object object) {
Log.i("Adapter", "ItemPosition>>>" + object.toString());
if (object instanceof ItemListFragment && mFragment instanceof ItemOneDetailFragment) { // fragment changed
return POSITION_NONE;
}
if (object instanceof ItemOneDetailFragment && mFragment instanceof ItemListFragment) { // fragment changed
return POSITION_NONE;
}
return POSITION_UNCHANGED;
}
}
Интерфейс: PageFragmentListener.java
public interface PageFragmentListener {
void onSwitchToNextFragment(String id);
}
Журнал: с атрибутом, добавленным в Manifest.xml
First instantiation
03-16 .../com.allison.viewpagermasterdetail D/ItemListFragment: newInstance
03-16 .../com.allison.viewpagermasterdetail D/ItemListFragment: onCreate()
03-16 .../com.allison.viewpagermasterdetail D/ItemListFragment: onCreateView()
03-16 .../com.allison.viewpagermasterdetail D/ItemListFragment: initLayout
03-16 .../com.allison.viewpagermasterdetail D/ItemListFragment: setupRecyclerView()
03-16 .../com.allison.viewpagermasterdetail D/ItemListFragment: onBindViewHolder
ClickItem
03-16 .../com.allison.viewpagermasterdetail D/ItemListFragment: onClick(): single pane
03-16 .../com.allison.viewpagermasterdetail D/MyAdapter: removed fragment
03-16 .../com.allison.viewpagermasterdetail D/MyAdapter: is instance of List Fragment
03-16 .../com.allison.viewpagermasterdetail D/ItemOneDetailFragment: newInstance
03-16 .../com.allison.viewpagermasterdetail I/Adapter: ItemPosition>>>FragmentB{41dd3810 #1 id=0x7f0c0069 android:switcher:2131492969:1}
03-16 .../com.allison.viewpagermasterdetail I/Adapter: ItemPosition>>>ItemListFragment{41df1528 #2 id=0x7f0c0069 android:switcher:2131492969:2}
03-16 .../com.allison.viewpagermasterdetail D/ItemListFragment: onPause()
03-16 .../com.allison.viewpagermasterdetail D/ItemListFragment: onStop()
03-16 .../com.allison.viewpagermasterdetail D/ItemListFragment: onDestroyView()
03-16 .../com.allison.viewpagermasterdetail D/ItemListFragment: onDestroy()
03-16 .../com.allison.viewpagermasterdetail D/ItemListFragment: onPause()
Back
03-16 .../com.allison.viewpagermasterdetail D/MyAdapter: removed fragment
03-16 .../com.allison.viewpagermasterdetail D/MyAdapter: is instance of Detail Fragment
03-16 .../com.allison.viewpagermasterdetail D/ItemListFragment: newInstance
03-16 .../com.allison.viewpagermasterdetail I/Adapter: ItemPosition>>>FragmentB{41dd3810 #1 id=0x7f0c0069 android:switcher:2131492969:1}
03-16 .../com.allison.viewpagermasterdetail I/Adapter: ItemPosition>>>ItemOneDetailFragment{41e896a8 #2 id=0x7f0c0069 android:switcher:2131492969:2}
03-16 .../com.allison.viewpagermasterdetail D/ItemListFragment: onCreate()
03-16 .../com.allison.viewpagermasterdetail D/ItemListFragment: onCreateView()
03-16 .../com.allison.viewpagermasterdetail D/ItemListFragment: initLayout
03-16 .../com.allison.viewpagermasterdetail D/ItemListFragment: setupRecyclerView()
Rotate
03-16 .../com.allison.viewpagermasterdetail D/ItemListFragment: onConfigurationChanged
03-16 .../com.allison.viewpagermasterdetail D/ItemListFragment: populateViewForOrientation
03-16 .../com.allison.viewpagermasterdetail D/ItemListFragment: initLayout
03-16 .../com.allison.viewpagermasterdetail D/ItemListFragment: setupRecyclerView()
03-16 .../com.allison.viewpagermasterdetail D/ItemListFragment: onBindViewHolder
ClickItemLand
03-16 .../com.allison.viewpagermasterdetail D/ItemListFragment: onClick(): mTwoPane
Rotate
03-16 .../com.allison.viewpagermasterdetail D/ItemListFragment: onConfigurationChanged
03-16 .../com.allison.viewpagermasterdetail D/ItemListFragment: populateViewForOrientation
03-16 .../com.allison.viewpagermasterdetail D/ItemListFragment: initLayout
03-16 .../com.allison.viewpagermasterdetail D/ItemListFragment: setupRecyclerView()
03-16 .../com.allison.viewpagermasterdetail D/ItemListFragment: onBindViewHolder
ClickIemPortrait
03-16 .../com.allison.viewpagermasterdetail D/ItemListFragment: onClick(): single pane
03-16 .../com.allison.viewpagermasterdetail D/MyAdapter: removed fragment
03-16 .../com.allison.viewpagermasterdetail D/MyAdapter: is instance of List Fragment
03-16 .../com.allison.viewpagermasterdetail D/ItemOneDetailFragment: newInstance
03-16 .../com.allison.viewpagermasterdetail I/Adapter: ItemPosition>>>FragmentB{41dd3810 #1 id=0x7f0c0069 android:switcher:2131492969:1}
03-16 .../com.allison.viewpagermasterdetail I/Adapter: ItemPosition>>>ItemListFragment{41ea8b68 #2 id=0x7f0c0069 android:switcher:2131492969:2}
03-16 .../com.allison.viewpagermasterdetail D/ItemListFragment: onPause()
03-16 .../com.allison.viewpagermasterdetail D/ItemListFragment: onStop()
03-16 .../com.allison.viewpagermasterdetail D/ItemListFragment: onDestroyView()
03-16 .../com.allison.viewpagermasterdetail D/ItemListFragment: onDestroy()
03-16 .../com.allison.viewpagermasterdetail D/ItemListFragment: onPause()
Журнал: без атрибута
First instantiation
03-16 .../com.allison.viewpagermasterdetail D/ItemListFragment: newInstance
03-16 .../com.allison.viewpagermasterdetail D/ItemListFragment: onCreate()
03-16 .../com.allison.viewpagermasterdetail D/ItemListFragment: onCreateView()
03-16 .../com.allison.viewpagermasterdetail D/ItemListFragment: initLayout
03-16 .../com.allison.viewpagermasterdetail D/ItemListFragment: setupRecyclerView()
03-16 .../com.allison.viewpagermasterdetail D/ItemListFragment: onBindViewHolder
ClickItem
03-16 .../com.allison.viewpagermasterdetail D/ItemListFragment: onClick(): single pane
03-16 .../com.allison.viewpagermasterdetail D/MyAdapter: removed fragment
03-16 .../com.allison.viewpagermasterdetail D/MyAdapter: is instance of List Fragment
03-16 .../com.allison.viewpagermasterdetail D/ItemOneDetailFragment: newInstance
03-16 .../com.allison.viewpagermasterdetail I/Adapter: ItemPosition>>>FragmentB{41dd4758 #1 id=0x7f0c0069 android:switcher:2131492969:1}
03-16 .../com.allison.viewpagermasterdetail I/Adapter: ItemPosition>>>ItemListFragment{41ded998 #2 id=0x7f0c0069 android:switcher:2131492969:2}
03-16 .../com.allison.viewpagermasterdetail D/ItemListFragment: onPause()
03-16 .../com.allison.viewpagermasterdetail D/ItemListFragment: onStop()
03-16 .../com.allison.viewpagermasterdetail D/ItemListFragment: onDestroyView()
03-16 .../com.allison.viewpagermasterdetail D/ItemListFragment: onDestroy()
03-16 .../com.allison.viewpagermasterdetail D/ItemListFragment: onPause()
Back
03-16 .../com.allison.viewpagermasterdetail D/MyAdapter: removed fragment
03-16 .../com.allison.viewpagermasterdetail D/MyAdapter: is instance of Detail Fragment
03-16 .../com.allison.viewpagermasterdetail D/ItemListFragment: newInstance
03-16 .../com.allison.viewpagermasterdetail I/Adapter: ItemPosition>>>FragmentB{41dd56b8 #1 id=0x7f0c0069 android:switcher:2131492969:1}
03-16 .../com.allison.viewpagermasterdetail I/Adapter: ItemPosition>>>ItemOneDetailFragment{41e830b8 #2 id=0x7f0c0069 android:switcher:2131492969:2}
03-16 .../com.allison.viewpagermasterdetail D/ItemListFragment: onCreate()
03-16 .../com.allison.viewpagermasterdetail D/ItemListFragment: onCreateView()
03-16 .../com.allison.viewpagermasterdetail D/ItemListFragment: initLayout
03-16 .../com.allison.viewpagermasterdetail D/ItemListFragment: setupRecyclerView()
03-16 .../com.allison.viewpagermasterdetail D/ItemListFragment: onBindViewHolder
Rotate
03-16 .../com.allison.viewpagermasterdetail D/ItemListFragment: onPause()
03-16 .../com.allison.viewpagermasterdetail D/ItemListFragment: onSaveInstanceState()
03-16 .../com.allison.viewpagermasterdetail D/ItemListFragment: onStop()
03-16 .../com.allison.viewpagermasterdetail D/ItemListFragment: onDestroyView()
03-16 .../com.allison.viewpagermasterdetail D/ItemListFragment: onDestroy()
03-16 .../com.allison.viewpagermasterdetail D/ItemListFragment: onPause()
03-16 .../com.allison.viewpagermasterdetail D/ItemListFragment: onCreate()
03-16 .../com.allison.viewpagermasterdetail D/ItemListFragment: onCreateView()
03-16 .../com.allison.viewpagermasterdetail D/ItemListFragment: initLayout
03-16 .../com.allison.viewpagermasterdetail D/ItemListFragment: setupRecyclerView()
ClickItemLand
03-16 .../com.allison.viewpagermasterdetail D/ItemListFragment: onClick(): mTwoPane
Rotate
03-16 .../com.allison.viewpagermasterdetail D/ItemListFragment: onPause()
03-16 .../com.allison.viewpagermasterdetail D/ItemListFragment: onSaveInstanceState()
03-16 .../com.allison.viewpagermasterdetail D/ItemListFragment: onStop()
03-16 .../com.allison.viewpagermasterdetail D/ItemListFragment: onDestroyView()
03-16 .../com.allison.viewpagermasterdetail D/ItemListFragment: onDestroy()
03-16 .../com.allison.viewpagermasterdetail D/ItemListFragment: onPause()
03-16 .../com.allison.viewpagermasterdetail D/ItemListFragment: onCreate()
03-16 .../com.allison.viewpagermasterdetail D/ItemListFragment: onCreateView()
03-16 .../com.allison.viewpagermasterdetail D/ItemListFragment: initLayout
03-16 .../com.allison.viewpagermasterdetail D/ItemListFragment: setupRecyclerView()
03-16 .../com.allison.viewpagermasterdetail E/ViewRootImpl: sendUserActionEvent() mView == null
03-16 .../com.allison.viewpagermasterdetail D/ItemListFragment: onBindViewHolder
ClickIemPortrait
// nothing happens