I'm trying to adapt my app to the Adaptative UI Flows example, but I'm missing something and I don't know what it is. As you'll see below, the NewsListFragment loads its layout depending on whether the device is in portrait or landscape mode, and since I'm not handling manually configuration changes, going from portrait to landscape changes such layout.
Expected behaviour: When the layout is portrait, show only the list. When the layout is landscape, show both the list and the webview.
Actual behaviour: When the layout is portrait, only the list is shown. BUT, when the layout is landscape, the NewsListFragment disappears, which does not seem to make any sense since I've set a fixed width for it and its height is set to match_parent.
NewsListFragment:
public class NewsListFragment extends ListFragment implements OnRefreshListener {
private static PullToRefreshLayout mPullToRefreshLayout;
private NewsFragmentArrayAdapter listAdapter;
private NewsFeedProvider newsFeedProvider;
private NewsListFragmentListener mCallback;
/**
* Called to do initial creation of a fragment. This is called after
* {@link #onAttach(android.app.Activity)} and before
* {@link #onCreateView(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle)}.
* <p/>
* <p>Note that this can be called while the fragment's activity is
* still in the process of being created. As such, you can not rely
* on things like the activity's content view hierarchy being initialized
* at this point. If you want to do work once the activity itself is
* created, see {@link #onActivityCreated(android.os.Bundle)}.
*
* @param savedInstanceState If the fragment is being re-created from
* a previous saved state, this is the state.
*/
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (listAdapter == null) {
listAdapter = new NewsFragmentArrayAdapter(getActivity());
setListAdapter(listAdapter);
}
if (newsFeedProvider == null) {
newsFeedProvider = new NewsFeedProvider(getActivity());
}
}
@Override
public void onListItemClick(ListView l, View v, int position, long id) {
super.onListItemClick(l, v, position, id);
getListView().setItemChecked(position, Boolean.TRUE);
mCallback.onNewsArticleSelected(position);
}
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
mCallback = (NewsListFragmentListener) activity;
}
catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement NewsListFragmentListener");
}
((NewsReaderActivity) activity).onSectionAttached(
new ArrayList<>(
Arrays.asList(
Utils.getStringArray(
getActivity().getApplicationContext(),
"navigation_drawer_items", new String[]{""})
)
).indexOf(Utils.getString(getActivity().getApplicationContext(), "title_section1",
"Home"))
);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View ret = inflater.inflate(R.layout.fragment_news_feed, container, false);
listAdapter.updateShownNews();
return ret;
}
/**
* Attach to list view once the view hierarchy has been created.
*
* @param view
* @param savedInstanceState
*/
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
// This is the View which is created by ListFragment
ViewGroup viewGroup = (ViewGroup) view;
// We need to create a PullToRefreshLayout manually
mPullToRefreshLayout = new PullToRefreshLayout(viewGroup.getContext());
Options.Builder optionsBuilder = Options.create();
int retrieved = Utils.getInt(getActivity(), "feed_refresh_distance_percentage",
R.integer.feed_refresh_distance_percentage);
float scrollDistance = (float) retrieved / 100;
optionsBuilder = optionsBuilder
.scrollDistance(scrollDistance);
optionsBuilder = optionsBuilder.headerTransformer(new TranslatableHeaderTransformer());
// We can now setup the PullToRefreshLayout
ActionBarPullToRefresh.from(getActivity())
// We need to insert the PullToRefreshLayout into the Fragment's ViewGroup
.insertLayoutInto(viewGroup)
// We need to mark the ListView and its empty view as pullable
// This is because they are not direct children of the ViewGroup
.theseChildrenArePullable(getListView(), getListView().getEmptyView())
// Set the OnRefreshListener
.listener(this).useViewDelegate(ImageView.class, new ViewDelegate() {
@Override
public boolean isReadyForPull(View view, float v, float v2) {
return Boolean.TRUE;
}
}).options(optionsBuilder.build())
// Finally commit the setup to our PullToRefreshLayout
.setup(mPullToRefreshLayout);
getListView().setChoiceMode(
ListView.CHOICE_MODE_SINGLE);
}
@Override
public void onRefreshStarted(View view) {
new AsyncTask<Void, Void, Void>() {
/**
* Override this method to perform a computation on a background thread. The
* specified parameters are the parameters passed to {@link #execute}
* by the caller of this task.
* <p/>
* This method can call {@link #publishProgress} to publish updates
* on the UI thread.
*
* @param params The parameters of the task.
* @return A result, defined by the subclass of this task.
* @see #onPreExecute()
* @see #onPostExecute
* @see #publishProgress
*/
@Override
protected Void doInBackground(Void... params) {
if (newsFeedProvider.requestFeedRefresh()) {
listAdapter.updateShownNews();
}
return null;
}
/**
* <p>Runs on the UI thread after {@link #doInBackground}. The
* specified result is the value returned by {@link #doInBackground}.</p>
* <p/>
* <p>This method won't be invoked if the task was cancelled.</p>
*
* @param aVoid The result of the operation computed by {@link #doInBackground}.
* @see #onPreExecute
* @see #doInBackground
* @see #onCancelled(Object)
*/
@Override
protected void onPostExecute(Void aVoid) {
mPullToRefreshLayout.setRefreshComplete();
}
/**
* <p>Applications should preferably override {@link #onCancelled(Object)}.
* This method is invoked by the default implementation of
* {@link #onCancelled(Object)}.</p>
* <p/>
* <p>Runs on the UI thread after {@link #cancel(boolean)} is invoked and
* {@link #doInBackground(Object[])} has finished.</p>
*
* @see #onCancelled(Object)
* @see #cancel(boolean)
* @see #isCancelled()
*/
@Override
protected void onCancelled() {
mPullToRefreshLayout.setRefreshComplete();
}
}.execute();
}
public interface NewsListFragmentListener {
public void onNewsArticleSelected(int index);
}
}
NewsReaderActivity:
public class NewsReaderActivity extends FragmentActivity implements
NewsListFragment.NewsListFragmentListener,
NavigationDrawerFragment.NavigationDrawerCallbacks {
private Boolean isDualPane = Boolean.FALSE;
private NewsListFragment NEWS_FRAGMENT;
private WebViewerFragment WEB_FRAGMENT;
private int lastSelectedNavDrawerItem = 0;
private NavigationDrawerFragment mNavigationDrawerFragment;
private CharSequence mTitle;
private void restoreActionBar() {
ActionBar actionBar = getActionBar();
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);
actionBar.setDisplayShowTitleEnabled(true);
actionBar.setTitle(mTitle);
}
/**
* Called when an item in the navigation drawer is selected.
*
* @param position
*/
@Override
public void onNavigationDrawerItemSelected(int position) {
if (position == lastSelectedNavDrawerItem) {
//We don't want to perform a useless fragment reload
return;
}
else {
lastSelectedNavDrawerItem = position;
}
//TODO The rest of the stuff
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
if (!mNavigationDrawerFragment.isDrawerOpen()) {
getMenuInflater().inflate(R.menu.standard, menu);
restoreActionBar();
return true;
}
return super.onCreateOptionsMenu(menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_settings:
startActivity(new Intent(this, SettingsPreferenceActivity.class));
break;
default: //Up button
return super.onOptionsItemSelected(item);
}
return true;
}
public void onSectionAttached(int number) {
int shiftedPos = number + 1;
mTitle = Utils.getString(this, "title_section" + shiftedPos, "");
if (mTitle.toString().isEmpty()) {
mTitle = getString(R.string.title_section1);
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_news);
FragmentManager fragmentManager = getFragmentManager();
mNavigationDrawerFragment = (NavigationDrawerFragment)
fragmentManager.findFragmentById(R.id.navigation_drawer);
mTitle = getTitle();
// Set up the drawer.
mNavigationDrawerFragment.setUp(
R.id.navigation_drawer,
(DrawerLayout) findViewById(R.id.drawer_layout));
NEWS_FRAGMENT =
(NewsListFragment) getFragmentManager().findFragmentById(R.id.fragment_news);
WEB_FRAGMENT =
(WebViewerFragment) getFragmentManager().findFragmentById(R.id.fragment_web_viewer);
View webView = findViewById(R.id.fragment_web_viewer);
isDualPane = webView != null && webView.getVisibility() == View.VISIBLE;
restoreState(savedInstanceState);
}
private void restoreState(Bundle savedInstanceState) {
if (savedInstanceState != null) {
int index = savedInstanceState.getInt("index", 0);
NEWS_FRAGMENT.setSelection(index);
onNewsArticleSelected(index);
}
}
@Override
public void onNewsArticleSelected(int index) {
showUrlInWebViewerFragment(index);
}
private void showUrlInWebViewerFragment(int index) {
if (isDualPane) {
WEB_FRAGMENT.loadUrl(SQLiteBridge.getSingleton().getNews().get(index).getLink());
}
else {
Intent singleViewIntent = new Intent(this, WebViewerActivity.class);
singleViewIntent.putExtra("index", index);
startActivity(singleViewIntent);
}
}
}
values-land/layouts.xml:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<item name="activity_news" type="layout">@layout/news_double_pane</item>
<bool name="has_two_panes">true</bool>
</resources>
values/layouts.xml:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<item name="activity_news" type="layout">@layout/news_single_pane</item>
<bool name="has_two_panes">true</bool>
</resources>
news_double_pane.xml:
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout
android:id="@+id/drawer_layout"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginBottom="@dimen/activity_vertical_margin"
android:layout_marginLeft="@dimen/activity_horizontal_margin"
android:layout_marginRight="@dimen/activity_horizontal_margin"
android:layout_marginTop="@dimen/activity_vertical_margin"
tools:context="org.jorge.lolin1.activities.MainActivity">
<fragment
android:id="@+id/fragment_news"
android:name="org.jorge.lolin1.frags.NewsListFragment"
android:layout_width="@dimen/feed_item_length_percentage"
android:layout_height="match_parent"/>
<fragment
android:id="@+id/fragment_web_viewer"
android:name="org.jorge.lolin1.frags.WebViewerFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/nothing_to_do_here"/>
<fragment
android:id="@+id/navigation_drawer"
android:name="org.jorge.lolin1.frags.NavigationDrawerFragment"
android:layout_width="@dimen/navigation_drawer_width"
android:layout_height="match_parent"
android:layout_gravity="left"/>
</android.support.v4.widget.DrawerLayout>
news_single_pane.xml:
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout
android:id="@+id/drawer_layout"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="org.jorge.lolin1.activities.MainActivity">
<fragment android:id="@+id/fragment_news"
android:name="org.jorge.lolin1.frags.NewsListFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<fragment
android:id="@+id/navigation_drawer"
android:name="org.jorge.lolin1.frags.NavigationDrawerFragment"
android:layout_width="@dimen/navigation_drawer_width"
android:layout_height="match_parent"
android:layout_gravity="left"/>
</android.support.v4.widget.DrawerLayout>