Android Fragment Basics Tutorial - android

So I've been stuck on the third tutorial on the Android Developer Site about Fragments for few days. I just can't understand how the app populates data when I run the app on a tablet (big screen layout). I can understand how the data is being populated on a smaller screen (phone screen).
How does the bigger screen list populate with data?
Here is a link of the whole project from Android.com tutorials.
MainActivity Class
public class MainActivity extends FragmentActivity
implements HeadlinesFragment.OnHeadlineSelectedListener {
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//Here, the system will decide which news_article layout it will use based on the screen size. Will use layout if small or layout-large if it's big.
setContentView(R.layout.news_articles);
// Check whether the activity is using the layout version with
// the fragment_container FrameLayout. If so, we must add the first fragment
//This check is to determine which layout to be used, either small screen or big screen.
//fragment_container used FrameLayout for small screens.
//fragment_container is the id of FrameLayout in news_article for small screen.
if (findViewById(R.id.fragment_container) != null) {
// However, if we're being restored from a previous state,
// then we don't need to do anything and should return or else
// we could end up with overlapping fragments.
if (savedInstanceState != null) {
return;
}
// Create an instance of ExampleFragment
HeadlinesFragment firstFragment = new HeadlinesFragment();
// In case this activity was started with special instructions from an Intent,
// pass the Intent's extras to the fragment as arguments
firstFragment.setArguments(getIntent().getExtras());
// Add the fragment to the 'fragment_container' FrameLayout
getSupportFragmentManager().beginTransaction()
.add(R.id.fragment_container, firstFragment).commit();
}
}
public void onArticleSelected(int position) {
// The user selected the headline of an article from the HeadlinesFragment
// Capture the article fragment from the activity layout
ArticleFragment articleFrag = (ArticleFragment)
getSupportFragmentManager().findFragmentById(R.id.article_fragment);
if (articleFrag != null) {
// If article frag is available, we're in two-pane layout...
// Call a method in the ArticleFragment to update its content
articleFrag.updateArticleView(position);
} else {
// If the frag is not available, we're in the one-pane layout and must swap frags...
// Create fragment and give it an argument for the selected article
ArticleFragment newFragment = new ArticleFragment();
Bundle args = new Bundle();
args.putInt(ArticleFragment.ARG_POSITION, position);
newFragment.setArguments(args);
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
// Replace whatever is in the fragment_container view with this fragment,
// and add the transaction to the back stack so the user can navigate back
transaction.replace(R.id.fragment_container, newFragment);
transaction.addToBackStack(null);
// Commit the transaction
transaction.commit();
}
}
}
HeadLineFragment
public class HeadlinesFragment extends ListFragment {
// The container Activity must implement this interface so the frag can deliver messages
public interface OnHeadlineSelectedListener {
/** Called by HeadlinesFragment when a list item is selected */
public void onArticleSelected(int position);
}
OnHeadlineSelectedListener mCallback;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// We need to use a different list item layout for devices older than Honeycomb
int layout = Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB ?
android.R.layout.simple_list_item_activated_1 : android.R.layout.simple_list_item_1;
// Create an array adapter for the list view, using the Ipsum headlines array
setListAdapter(new ArrayAdapter<String>(getActivity(), layout, Ipsum.Headlines));
}
#Override
public void onStart() {
super.onStart();
// When in two-pane layout, set the listview to highlight the selected list item
// (We do this during onStart because at the point the listview is available.)
if (getFragmentManager().findFragmentById(R.id.article_fragment) != null) {
getListView().setChoiceMode(ListView.CHOICE_MODE_SINGLE);
}
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
// This makes sure that the container activity has implemented
// the callback interface. If not, it throws an exception.
try {
mCallback = (OnHeadlineSelectedListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement OnHeadlineSelectedListener");
}
}
#Override
public void onListItemClick(ListView l, View v, int position, long id) {
// Notify the parent activity of selected item
mCallback.onArticleSelected(position);
// Set the item as checked to be highlighted when in two-pane layout
getListView().setItemChecked(position, true);
}
}
Layout for small screen
news_article.xml
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
Layout for bigscreen
news_article.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal" >
<fragment android:name="com.example.android.fragments.HeadlinesFragment"
android:id="#+id/headlines_fragment"
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="match_parent" />
<fragment android:name="com.example.android.fragments.ArticleFragment"
android:id="#+id/article_fragment"
android:layout_weight="2"
android:layout_width="0dp"
android:layout_height="match_parent" />
</LinearLayout>

Notice the placement of the two layouts.
Large screen is in a tablet bin (folder) res/layout-large/main.xml while small screen layout is in generic res/layout/main.xml
Since the java asks if the findViewById is null we know if the device is a large screen or normal layout.
ArticleFragment articleFrag = (ArticleFragment) getSupportFragmentManager().findFragmentById(R.id.article_fragment);
if (articleFrag != null) {
/* not null because we are in res/layout-large */
} else {
/* we are in single pain view /res/layout/... */
}
When you call setContentView(int); they system handles loading the best layout you provided for the device based on the DPI bins provided.

I think, this question is related with this post: http://developer.android.com/training/multiscreen/screensizes.html
Note: large qualifier mean that layout will be selected on devices with screens classified as large (for example, 7" tablets and above). The other layout (without qualifiers) will be selected for smaller devices;
Also if you want to use for small screens two-pane layout, create directory:
res/layout-sw600dp/main.xml;
About Data Population:
In MainActivity you have implemented interface OnHeadlineSelectedListener, when you click on item from list (HeadLinesFragment class)
interface mCallback call function from MainActivity onArticleSelected(position);
in this function you have articleFlag
ArticleFragment articleFlag = (ArticleFragment) getSupportFragmentManager(). findFragmentById(R.id.article_fragment);
if(articleFlag != null){
//we have 2 panes (big screen)
articleFlag.updateArticleView(position);
}else{
//small screen
ArticleFragment newFragment = new ArticleFragment();
Bundle args = new Bundle();
args.putInt(ArticleFragment.ARG_POSITION, position);
newFragment.setArguments(args);
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
// Replace whatever is in the fragment_container view with this fragment,
// and add the transaction to the back stack so the user can navigate back
transaction.replace(R.id.fragment_container, newFragment);
transaction.addToBackStack(null);
//Commit the transaction
transaction.commit();
}

Related

how to save android recycler view state (scroll position) in an activity with two fragments

I am new to Android and, as a first step, am building an app, for running in a handset, with an activity in which I put two fragments. The first fragment has a recycler view of items that are supposed to represent article titles. When I click on one, the second fragment opens and shows the title (in a text view) and the content (in another text view) of the article (for the moment, for simplicity, I put the article title as fake titles and I have a setting for which the content is not shown but it is shown the title in the content text view too).
I want to save the scroll position of the recycler view.
When I scroll down, having on top of the screen an article title different from the first, I choose an article and my second fragment opens with the expected contents, and that's ok. When I rotate to landscape, the same fragment contains the same content, ok. So:
1) when I press the back button from the landscape, returning to the first fragment, I get the same setting for the recycler view, ok;
2) when I rotate again to portrait, remaining on the second fragment, it is ok too. Now, if I press the back button to return to the first fragment, to the list of articles, the recycler view is NOT set to start with the item I initially scrolled to. What can I do?
I have this code in the first fragment, the one containing the recycler view:
#Override
public void onSaveInstanceState(Bundle state) {
int lastFirstVisiblePosition = ((LinearLayoutManager) recyclerView.getLayoutManager()).findFirstCompletelyVisibleItemPosition();
state.putInt(Articles.RECYCLER_POSITION_KEY, lastFirstVisiblePosition);
super.onSaveInstanceState(state);
}
#Override
public void onActivityCreated(Bundle state) {
super.onActivityCreated(state);
if (state != null) {
int lastFirstVisiblePosition = state.getInt(Articles.RECYCLER_POSITION_KEY);
((LinearLayoutManager) recyclerView.getLayoutManager()).scrollToPosition(lastFirstVisiblePosition);
}
}
What am I missing? Thanks.
[EDIT] In my listener is this, for invoking the second fragment:
ArticleReaderFrag newFragment = new ArticleReaderFrag();
Bundle args = new Bundle();
args.putString(NEW_FRAG_TITLE_KEY, item);
args.putString(NEW_FRAG_BODY_KEY, itemb);
newFragment.setArguments(args);
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.main_activity, newFragment);
transaction.addToBackStack(null);
transaction.commit();
Reuse Fragment
When the Activity launches for the first time create a new instance of Fragment and use a TAG to save it with FragmentManager. When the activity gets recreated after orientation change. Retreive the old instance using the Tag.
Here is a sample code which you should have in your activity.
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.first_activity);
fragmentManager = getSupportFragmentManager();
if(savedInstanceState==null) {
userFragment = UserNameFragment.newInstance();
fragmentManager.beginTransaction().add(R.id.profile, userFragment, "TAG").commit();
}
else {
userFragment = fragmentManager.findFragmentByTag("TAG");
}
}
How to save scroll position?
That happens by default. Read this answer for more detail.
EDIT
I believe this is your method
public createSecondFragment(int position){
ArticleReaderFrag newFragment = new ArticleReaderFrag();
Bundle args = new Bundle();
args.putString(NEW_FRAG_TITLE_KEY, item);
args.putString(NEW_FRAG_BODY_KEY, itemb);
newFragment.setArguments(args);
FragmentTransaction transaction =
getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.main_activity, newFragment);
transaction.addToBackStack(null);
transaction.commit();
}
Save the position of on rotation and use that position to load the specific fragment.
Call the method when the activity is recreated after orientation change
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.first_activity);
if(savedInstanceState!=null) {
int lastSavedPosition = // Your logic
createSecondFragment(lastSavedPosition )
}
}

Android fragments using v7 appCompat library

I downloaded the fragmentbasics.zip from the Android Training site. At the bottom of the page (from provided link) there is a section stating "If you're using the v7 appcompat library, your activity should instead extend ActionBarActivity, which is a subclass of FragmentActivity". I am using the android-support-v7-appcompat.jar in my project.
I did as the tutorial page says and updated 'MainActivity.java' to extend ActionBarActivity instead of FragmentActivity, I am now getting the below errors...
The hierarchy of the type MainActivity is inconsistent
The type android.support.v4.app.TaskStackBuilder$SupportParentable cannot be resolved. It is indirectly referenced from required .class files
MainActivity.java
public class MainActivity extends ActionBarActivity
implements HeadlinesFragment.OnHeadlineSelectedListener {
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.news_articles);
// Check whether the activity is using the layout version with
// the fragment_container FrameLayout. If so, we must add the first fragment
if (findViewById(R.id.fragment_container) != null) {
// However, if we're being restored from a previous state,
// then we don't need to do anything and should return or else
// we could end up with overlapping fragments.
if (savedInstanceState != null) {
return;
}
// Create an instance of ExampleFragment
HeadlinesFragment firstFragment = new HeadlinesFragment();
// In case this activity was started with special instructions from an Intent,
// pass the Intent's extras to the fragment as arguments
firstFragment.setArguments(getIntent().getExtras());
// Add the fragment to the 'fragment_container' FrameLayout
getSupportFragmentManager().beginTransaction()
.add(R.id.fragment_container, firstFragment).commit();
}
}
public void onArticleSelected(int position) {
// The user selected the headline of an article from the HeadlinesFragment
// Capture the article fragment from the activity layout
ArticleFragment articleFrag = (ArticleFragment)
getSupportFragmentManager().findFragmentById(R.id.article_fragment);
if (articleFrag != null) {
// If article frag is available, we're in two-pane layout...
// Call a method in the ArticleFragment to update its content
articleFrag.updateArticleView(position);
} else {
// If the frag is not available, we're in the one-pane layout and must swap frags...
// Create fragment and give it an argument for the selected article
ArticleFragment newFragment = new ArticleFragment();
Bundle args = new Bundle();
args.putInt(ArticleFragment.ARG_POSITION, position);
newFragment.setArguments(args);
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
// Replace whatever is in the fragment_container view with this fragment,
// and add the transaction to the back stack so the user can navigate back
transaction.replace(R.id.fragment_container, newFragment);
transaction.addToBackStack(null);
// Commit the transaction
transaction.commit();
}
}
}
Based on the tutorial I guess I am doing things correctly, any ideas why I would be getting these errors?
Steps to reproduce:
download the sample from provided link (top of post)
right click project -> build path -> add external arhchives
find your v7 appCompat support jar and click open
observe errors
Try to update the android.support.v4

Get data result from second Fragment

In my MainActivity extends FragmentActivity, I have a FragmentA, When I press a Button in FragmentA, I call to FragmentB.
FragmentB f = FragmentB.newInstance(1);
getSupportFragmentManager().beginTransaction().replace(R.id.llMain, f).addToBackStack(null).commit();
In FragmentB, I create a Object People p1(with Name and age) . And When I press a Button B in FragmentB, I call
getFragmentManager().popBackStack();
It will return FragmentA,
So, I want to pass data Object People p1 from FragmentB to FragmentA. What do i have to do?
I try to search but can't find a solution.
create CallBack in your Fragment and handle it in FragmentActivity,
google example has this realization
declaring OnHeadlineSelectedListener callback
public class HeadlinesFragment extends ListFragment {
OnHeadlineSelectedListener mCallback;
// The container Activity must implement this interface so the frag can deliver messages
public interface OnHeadlineSelectedListener {
/** Called by HeadlinesFragment when a list item is selected */
public void onArticleSelected(int position);
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// We need to use a different list item layout for devices older than Honeycomb
int layout = Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB ?
android.R.layout.simple_list_item_activated_1 : android.R.layout.simple_list_item_1;
// Create an array adapter for the list view, using the Ipsum headlines array
setListAdapter(new ArrayAdapter<String>(getActivity(), layout, Ipsum.Headlines));
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
// This makes sure that the container activity has implemented
// the callback interface. If not, it throws an exception.
try {
mCallback = (OnHeadlineSelectedListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement OnHeadlineSelectedListener");
}
}
#Override
public void onListItemClick(ListView l, View v, int position, long id) {
// Notify the parent activity of selected item
mCallback.onArticleSelected(position);
// Set the item as checked to be highlighted when in two-pane layout
getListView().setItemChecked(position, true);
}
Realize callback method in FragmentActivity and send (by .setArguments()) data from HeadLinesFragment to ArticleFragment, if ArticleFragment is available
public class MainActivity extends FragmentActivity
implements HeadlinesFragment.OnHeadlineSelectedListener {
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.news_articles);
// Check whether the activity is using the layout version with
// the fragment_container FrameLayout. If so, we must add the first fragment
if (findViewById(R.id.fragment_container) != null) {
// However, if we're being restored from a previous state,
// then we don't need to do anything and should return or else
// we could end up with overlapping fragments.
if (savedInstanceState != null) {
return;
}
// Create an instance of ExampleFragment
HeadlinesFragment firstFragment = new HeadlinesFragment();
// In case this activity was started with special instructions from an Intent,
// pass the Intent's extras to the fragment as arguments
firstFragment.setArguments(getIntent().getExtras());
// Add the fragment to the 'fragment_container' FrameLayout
getSupportFragmentManager().beginTransaction()
.add(R.id.fragment_container, firstFragment).commit();
}
}
public void onArticleSelected(int position) {
// The user selected the headline of an article from the HeadlinesFragment
// Capture the article fragment from the activity layout
ArticleFragment articleFrag = (ArticleFragment)
getSupportFragmentManager().findFragmentById(R.id.article_fragment);
if (articleFrag != null) {
// If article frag is available, we're in two-pane layout...
// Call a method in the ArticleFragment to update its content
articleFrag.updateArticleView(position);
} else {
// If the frag is not available, we're in the one-pane layout and must swap frags...
// Create fragment and give it an argument for the selected article
ArticleFragment newFragment = new ArticleFragment();
Bundle args = new Bundle();
args.putInt(ArticleFragment.ARG_POSITION, position);
newFragment.setArguments(args);
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
// Replace whatever is in the fragment_container view with this fragment,
// and add the transaction to the back stack so the user can navigate back
transaction.replace(R.id.fragment_container, newFragment);
transaction.addToBackStack(null);
// Commit the transaction
transaction.commit();
}
}
You should use an interface within Activity for communication between fragments. Check this android training lesson.
All Fragment-to-Fragment communication is done through the associated
Activity. Two Fragments should never communicate directly.
You can pass arguments to a Fragment with Bundle. Change your code to:
FragmentB f = FragmentB.newInstance(1);
Bundle args = new Bundle();
args.putString("NAME", name);
args.putInt("AGE", age);
f.setArguments(args);
getSupportFragmentManager().beginTransaction().replace(R.id.llMain, f).addToBackStack(null).commit();
and then retrieve the arguments for example in FragmentA's onCreateView with:
int age = getArguments().getInt("AGE");
//or with a second parameter as the default value
int age = getArguments().getInt("AGE", 0);
If you want to pass the whole People object to the Bundle, you need to make the class serializable. I think it's easier to pass the variables and then recreate the object.

All fragments visible after rotation

i have a problem with fragments when my device is rotate:
i have 5 fragments:
Menu is the first fragment, when activity start, it is launched.
AddUser (simple fragment that i can see when i press a button in menu fragment)
DetailApp (simple fragment that i can see when i press a button in menu fragment)
DetailUser (simple fragment that i can see when i press a button in menu fragment)
ListUser (ListFragment that i can see when i press a button in menu fragment)
When i start my activity in vertical mode, i can see correctly my fragments, when i rotate my smartphone, i can see.. all fragments overlapping!
if i launch ListUser i have:
IllegalArgumentException: No view found for id 0x... for fragment ...
when start my Menu fragments:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_manager);
this is oncreate
// Check whether the activity is using the layout version with
// the fragment_container FrameLayout. If so, we must add the first fragment
if (findViewById(R.id.fragment_container) != null) {
// However, if we're being restored from a previous state,
// then we don't need to do anything and should return or else
// we could end up with overlapping fragments.
if (savedInstanceState != null) {
return;
}
//first frame show a menu
Menu firstFragment = new Menu();
// In case this activity was started with special instructions from an Intent,
// pass the Intent's extras to the fragment as arguments
firstFragment.setArguments(getIntent().getExtras());
// Add the fragment to the 'fragment_container' FrameLayout
getSupportFragmentManager().beginTransaction()
.add(R.id.fragment_container, firstFragment).commit();
}
fragment container at start is void..
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
when i press a button and one of fragments start:
#Override
public void onDetailApp(int position) {
// The user selected the headline of an article from the HeadlinesFragment
// Capture the article fragment from the activity layout
DetailApp appFrag = (DetailApp)
getSupportFragmentManager().findFragmentById(R.id.detailapp_fragment);
if (appFrag != null) {
// If article frag is available, we're in two-pane layout...
// Call a method in the ArticleFragment to update its content
appFrag.updateAppointmentView(position);
} else {
// If the frag is not available, we're in the one-pane layout and must swap frags...
// Create fragment and give it an argument for the selected article
DetailApp newFragment = new DetailApp();
Bundle args = new Bundle();
args.putInt(DetailApp.ARG_APPOINTMENT, position);
newFragment.setArguments(args);
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
// Replace whatever is in the fragment_container view with this fragment,
// and add the transaction to the back stack so the user can navigate back
transaction.replace(R.id.fragment_container, newFragment);
transaction.addToBackStack(null);
// Commit the transaction
transaction.commit();
}
}
this is onCreateView from DetailUser, AddUser, DetailApp:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// If activity recreated (such as from screen rotate), restore
// the previous article selection set by onSaveInstanceState().
// This is primarily necessary when in the two-pane layout.
if (savedInstanceState != null) {
mCurrentPosition = savedInstanceState.getInt(ARG_POSITION);
}
// Inflate the layout for this fragment
return inflater.inflate(R.layout.detail_adduser, container, false);
}
Who can help me?
replace
getSupportFragmentManager().beginTransaction()
.add(R.id.fragment_container, firstFragment).commit();
by
getSupportFragmentManager().beginTransaction()
.replace(R.id.fragment_container, firstFragment).commit();
99% of times it solves duplication issues.
i add this on my Manifest activity and override onConfigurationChanged:
android:configChanges="keyboardHidden|orientation|screenSize"
<activity
android:name="com.map.manager.ManagerActivity"
android:configChanges="keyboardHidden|orientation|screenSize"
android:label="#string/title_activity_manager" >
</activity>
and i have no more problems! i can see correctly layout vertical and horizontal (when i rotate the smartphone).
but i found this: why-not-use-always-androidconfigchanges
must i put in "blacklist" all possible configuration change? locale touchscreen screenSize etc?

Android fragment - different layout into fragment

I'm trying to develop an application compatible with smartphones (one pane layout) and tablets (two panes layout). My app is displayed as shown below:
(source: android10.org)
I used the example available in Google developers website.
The example works fine. But for my app, I need to use different layout in the WebView fragment. Each element selected in ListView fragment shows a specific layout into the right fragment.
This is my code called when an element is clicked :
public void onArticleSelected(int position) {
// The user selected the headline of an article from the
// HeadlinesFragment
// Capture the article fragment from the activity layout
BodyFragment articleFrag = (BodyFragment) getSupportFragmentManager().findFragmentById(R.id.body_fragment);
if (articleFrag != null) {
// If article frag is available, we're in two-pane layout...
// Call a method in the ArticleFragment to update its content
articleFrag.updateArticleView(position);
} else {
// If the frag is not available, we're in the one-pane layout and
// must swap frags...
// Create fragment and give it an argument for the selected
// article
BodyFragment newFragment = new BodyFragment();
Bundle args = new Bundle();
args.putInt(BodyFragment.ARG_POSITION, position);
newFragment.setArguments(args);
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
// Replace whatever is in the fragment_container view with this
// fragment,
// and add the transaction to the back stack so the user can
// navigate back
transaction.replace(R.id.fragment_container, newFragment);
transaction.addToBackStack(null);
// Commit the transaction
transaction.commit();
}
}
How can I should make it? I thought to replace dynamically the layout into the fragment, but it is not possible.
Is anybody has a solution?
thanks in advance

Categories

Resources