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
Related
I am using NavigationDrawer in my application and each menu item in drawer is a fragment.Whenever user chooses a menu item I replace the current fragment in the main container with the requested one but it recreates the fragment every-time, so i updated my code to reuse the existing fragments instead of creating them again and again as content of fragments remain same. My updated code to show fragment is :
public void showTabFragment() {
TabFragment Tf = (TabFragment) mFragmentManager.findFragmentByTag(Constants.TAB_FRAGMENT);
mFragmentTransaction = mFragmentManager.beginTransaction();
if (Tf != null) {
mFragmentTransaction.replace(R.id.containerView, Tf, Constants.TAB_FRAGMENT);
} else {
mFragmentTransaction.replace(R.id.containerView, new TabFragment(), Constants.TAB_FRAGMENT);
}
mFragmentTransaction.commit();
}
In above code I am trying to get fragments by Tag but it always returns null and executes the else case(new fragment).Could someone please guide me what am I doing wrong in my code?
I guess the code you've shown is for one of your menu fragment? If that's the case, what is probably happening is every time you open a menu item, the container is replaced with the new fragment(say, Fragment B) with its new tag(say, TAG 'B'). So, when you try to open the previous fragment(say, Fragment A) using it's tag(TAG 'A'), it won't be there, because that's what you replaced.
One possible solution is to hold references to the fragment as they are created, in, say a hashmap, and reuse them instead.
private HashMap<String, Fragment> menuFragments = new HashMap<>();
public void showMenu(String fragmentID)
{
MenuFragment fragment = menuFragments.get(fragmentID);
if(fragment == null)
{
fragment = new MenuFragment(); //Create the respective menu fragment based on the ID.
menuFragments.put(fragmentID, fragment);
}
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.containerView, fragment, fragmentID);
transaction.commit();
}
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.
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();
}
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?
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