I'm new to Android and I'm playing with Fragments, I know that there exist many examples to learn how Fragments work, unfortunately there is not many examples using Nested-Fragments (which are in the same Activity).
This is my code below, ive encountered 2 problems:
The Root Fragment is visible after I add the secondary Fragment.
both Fragment use same design, but when I add the FragmentIi can see the dimensions of the widgets and the position on the screen are not the same.
I Need some feedback how to do it right.
Thank you!
public class LoginActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
if (savedInstanceState == null) {
getFragmentManager().beginTransaction()
.add(R.id.container, new PlaceholderFragment()).commit();
}
}
/**
* A placeholder fragment containing a simple view.
*/
public static class PlaceholderFragment extends Fragment {
public PlaceholderFragment() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_login,
container, false);
TextView tv = (TextView) rootView.findViewById(R.id.signUpText1);
tv.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
FragmentTransaction ft = getFragmentManager()
.beginTransaction();
ft.replace(R.id.loginViewPlaceholder, new SignupFragment());
ft.commit();
}
});
return rootView;
}
}
public static class SignupFragment extends Fragment {
public SignupFragment() {
// TODO Auto-generated constructor stub
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// TODO Auto-generated method stub
View signupView = inflater.inflate(R.layout.fragment_signup,
container, false);
return signupView;
}
}
}
The replace method will remove a fragment from the specified container and add the new one its place. In your example, it looks like you are adding the PlaceholderFragment to the container android.R.content and the SignupFragment to the container R.id.loginViewPlaceholder. If you want the SignupFragment to replace the PlaceholderFragment you will have to either use the same container for both, or you will have to do separate remove() and add() calls for your transaction.
Related
I create simple project using actionbar ,i've one class and one fragment class, i define custome actionbar in class, and my problem, how to call method actionbar in fragment class from class,
this code like below :
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_holder);
fragmentArray = new ConferenceFragment[8];
// Load main fragment
fragment = new HomeFragment();
fragmentArray[0] = fragment;
currentFragmentIndex = 0;
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
fragmentTransaction.replace(R.id.holder, fragment).commit();
// initMenuBar();
}
public void initMenuBar(){
ActionBar actionBar = getActionBar();
actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM);
actionBar.setCustomView(R.layout.menu_bar);
ImageButton buttonSideMenu = (ImageButton) findViewById(R.id.bt_menu);
buttonSideMenu.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
toggle();
// Toast.makeText(getApplicationContext(), "Clicked!",Toast.LENGTH_LONG).show();
}
});
actionBar.show();
}
// fragment class
public class HomeFragment extends Fragment{
View v;
MainActivity mainactivity;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// TODO Auto-generated method stub
mainActivity.initMenuBar(); // ERROR IN THIS LINE
super.onCreateView(inflater, container, savedInstanceState);
v = inflater.inflate(R.layout.home_fragment, container, false);
return v;
}
}
ActionBar is traditionally the part of Activity and is available to activity only. You can get Activity instance by calling getActivity() from fragment and cast it to your activity then call public method to do whatever you want.
But the better option is to use ToolBar explained here support library v7
You need to get your MainActivity's context reference in your fragment to access activity's method.
Try this.
public class HomeFragment extends Fragment{
View v;
Context mContext;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreateView(inflater, container, savedInstanceState);
v = inflater.inflate(R.layout.home_fragment, container, false);
mContext = getActivity();
((MainActivity) mContext).initMenuBar();
return v;
}
}
This is giving me headache. Lets say my app has a ViewPager which contains 4 tabs. I want to have multiple sub-fragments within each tab. that means each tab would have more than just one view. I totally have no clue how to achieve this....
I have a FragmentActivity which gets load first and set my ViewPager
public class FragActivity extends FragmentActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.frag_main);
ViewPager pager = (ViewPager) findViewById(R.id.pager);
PagerAdapter adapter = new FFNPagerAdapter(getSupportFragmentManager());
pager.setAdapter(adapter);
pager.setOffscreenPageLimit(3);
tabs.setViewPager(pager);
}
}
I then have my FragmentPagerAdapter
public class PagerAdapter extends FragmentPagerAdapter implements TextAndIconTabProvider {
private final String[] TITLES = { "FIRST", "SECOND", "THIRD", "FORTH" };
public PagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
Fragment frag = new Fragment();
switch (position) {
case 1:
frag = new FirstFrag();
break;
case 2:
frag = new SecondFrag();
break;
case 3:
frag = new ThirdFrag();
break;
case 4:
frag = new ForthFrag();
break;
}
return frag;
}
#Override
public int getCount() {
// TODO Auto-generated method stub
return TITLES.length;
}
}
the above would give me a working ViewPager with 4 active tabs. Then I need to do all 4 individual Fragments. I'm giving out one example below.
public class FirstFrag extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.firstfrag, container, false);
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onActivityCreated(savedInstanceState);
Button btn1 = (Button) getActivity().findViewById(R.id.btn1);
btn1.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
// what should I put in this block??????
Fragment fragment = new FirstFragSub1();
FragmentTransaction transaction = getFragmentManager().beginTransaction();
transaction.replace(R.id.pager, fragment); //<<------what should i replace???????
transaction.commitAllowingStateLoss();
}
});
}
}
The OnCreateView in FirstFragSub1 gets called but I don't see anything displayed on the screen. Basically just a blank. I know by replacing R.id.pager should not be the correct way, I'm just giving out an example of what I initial thought was.
Here is my FirstFragSub1
public class FirstFragSub1 extends ListFragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// Inflate the layout for this fragment
Log.i("FirstFragSub1","onCreateView is called");
return inflater.inflate(R.layout.subone, container, false);
}
}
Can someone please help me? I really out of idea...Thanks
there is a great tutorial and github here that covers this in detail.
http://tausiq.wordpress.com/2014/06/06/android-multiple-fragments-stack-in-each-viewpager-tab/
https://github.com/tausiq/ViewPagerMultipleFragmentDemo
this covers handling the backstack and multiple tabs with multiple views per tab. hope this help I was searching for a similar solution when i read your question.
Have FrameLayouts in the view that is being inflated by the Fragment. And onCreateView, replace the FrameLayout by the sub fragments that you want.
Also, be careful not to use the Activity's FragmentManager. getFragmentManager returns the Activity's FragmentManager and that will lead to an undesirable result (Only one fragment inside your ViewPager would have the sub fragments loaded). Use the getChildFragmentManager function instead.
What I need to achieve
A screen displaying a ListView, which can be replaced by an error screen in case of problems (missing connection, server unavailable and the like).
I need to be able to switch (programmatically) back and forth between these two screens.
Requirements
The main screen must be a Fragment.
This is because my application is composed of several sections, each one accessible from the navigation drawer.
What I have done so far
The main fragment class is named AllQueuesFragment: its XML layout consists of a FrameLayout, which I use in combination with the FragmentManager to switch between ErrorFragment (containing the error message) and QueuesViewFragment (containing the ListView).
public class AllQueuesFragment extends Fragment
{
public AllQueuesFragment()
{
super();
}
#Override
public void onStart()
{
super.onStart();
// Show the right fragment based on connectivity status
checkConnection();
}
public void checkConnection()
{
final NetworkManager netManager = NetworkManager.getInstance(this.getActivity());
if (netManager.isConnected())
showQueues();
else
showNoConnection();
}
public void showNoConnection()
{
ErrorFragment fragNoConnection = new ErrorFragment();
displayFragment(fragNoConnection);
fragNoConnection.setTitle(R.string.text_no_connection);
fragNoConnection.setIcon(R.drawable.thatfeel);
fragNoConnection.setLoaderVisibility(false);
}
public void showQueues()
{
QueuesViewFragment fragQueuesView = new QueuesViewFragment();
displayFragment(fragQueuesView);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
// Inflate the view
View rootView = inflater.inflate(R.layout.fragment_allqueues, container, false);
return rootView;
}
// Displays a new fragment
public void displayFragment(Fragment fragment)
{
if (fragment != null)
{
FragmentManager fragmentManager = getFragmentManager();
fragmentManager.beginTransaction().replace(R.id.frame_container, fragment).commit();
}
}
}
The error screen is the following:
public class ErrorFragment extends Fragment
{
private TextView textTitle;
public ErrorFragment()
{
super();
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
// Inflate the view
View rootView = inflater.inflate(R.layout.fragment_error, container, false);
// Get the widgets
textTitle = (TextView)rootView.findViewById( R.id.fragment_error_text );
return rootView;
}
// Set methods
public void setTitle(int id) { textTitle.setText(id); }
}
The problem
The setTitle() method gets called before the layout is ready, and as a result, a NullPointerException is thrown.
class AllQueuesFragment
{
....
public void displayFragment(Fragment fragment)
{
if (fragment != null)
{
FragmentManager fragmentManager = getFragmentManager();
fragmentManager.beginTransaction().replace(R.id.frame_container, fragment).commit();
}
}
public void showNoConnection()
{
ErrorFragment fragNoConnection = new ErrorFragment();
displayFragment(fragNoConnection);
// PROBLEM HERE: Before calling setTitle(), I must be sure that ErrorFragment's
// layout is inflated!
fragNoConnection.setTitle(R.string.text_no_connection);
}
....
}
class ErrorFragment
{
....
public void setTitle(String value) { textTitle.setText(value); }
....
}
I can't call setTitle() directly from ErrorFragment::onCreateView(), because I don't know beforehand which message I need to show.
How can I ensure that fragNoConnection has completed its layouting?
Is there a better way to achieve my goal?
Unsatisfying workaround
The only workaround I can think of is to use a buffer to defer the actual call:
class ErrorFragment
{
// This string will hold the title until the layout is inflated
private String titleBuffer;
private TextView textTitle = null;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
// Inflate the view
View rootView = inflater.inflate(R.layout.fragment_error, container, false);
// Get the widgets
textTitle = (TextView)rootView.findViewById( R.id.fragment_error_text );
// Do the actual set
setTitle(titleBuffer);
return rootView;
}
....
public void setTitle(String value)
{
titleBuffer = value;
// If the layout is not inflated, defer the actual set
if (textTitle != null)
textTitle.setText(titleBuffer);
}
....
}
but I don't like this solution very much (the code above is simplified; ErrorFragment has more properties).
Advices?
Thanks in advance
This is exactly the type of thing arguments are supposed to be used for:
public void showNoConnection() {
ErrorFragment fragNoConnection = new ErrorFragment();
Bundle args = new Bundle();
//you can also use putInt here if you'd rather pass a string resource id, along with anything else you can stick into a Bundle
args.putString("title", "some title");
fragNoConnection.setArguments(args);
displayFragment(fragNoConnection);
}
Then in ErrorFragment
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_error, container, false);
TextView textTitle = (TextView)rootView.findViewById( R.id.fragment_error_text );
//now retrieve the argument...
textTitle.setText(getArguments().getString("title"));
return rootView;
}
The Fragment will even remember it's arguments after an orientation change.
If you feel like being pedantic, you can create a static factory method within ErrorFragment that takes the title as an argument and then creates the Fragment and adds the argument, that way you can achieve proper encapsulation :)
You need to have a callback method in your ErrorFragment and when the view is inflated you then call the method in your callback interface in the onViewCreated and set the title.
sample:
in ErroFragment
public class ErroFragment extends Fragment
{
static interface ErrorDone{
public void doneInflating();
}
private TextView textTitle;
private ErrorDone ed;
public ErroFragment()
{
super();
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
// Inflate the view
View rootView = inflater.inflate(R.layout.fragment_error, container, false);
// Get the widgets
textTitle = (TextView)rootView.findViewById( R.id.fragment_error_text );
return rootView;
}
// Set methods
public void setTitle(int id) { textTitle.setText(id); }
public void setInterFace(ErrorDone er){ this.ed = er; }
}
Then you implement the interface in your AllQueuesFragment
public class AllQueuesFragment extends Fragment implements ErroFragment.ErrorDone
It will generate method doneInflating
and you need to set the interface:
public void showNoConnection()
{
ErrorFragment fragNoConnection = new ErrorFragment();
displayFragment(fragNoConnection);
fragNoConnection.setInterFace(this);
}
And in the generated method(doneInflating) of the AllQueuesFragment you then set the title in there:
public void doneInflating(){
fragNoConnection.setTitle(R.string.text_no_connection);
fragNoConnection.setIcon(R.drawable.thatfeel);
fragNoConnection.setLoaderVisibility(false);
}
If you want to be sure that the FragmentTransaction is commited and effective, you can use the executePendingTransactions method:
FragmentManager fragmentManager = getFragmentManager();
fragmentManager.beginTransaction().replace(R.id.frame_container, fragment).commit();
fragmentManager.executePendingTransactions();
But, the right way to do it is to send the title value to the Fragment when instantiating it. This is the default pattern when you create a Fragment from your IDE (eclipse or Android Studio)
I have seen Link1 for this issue but could understand it right. I have a fragment that loads a list. When i click the list item it opens another activity. But i press back button it loads the list again. I want it to be at the same scroll position where it was before. In above mentioned link it specifies to use flag but i haven't got the point.
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_dashboard);
android.app.Fragment fragment = new MeFragment();
getFragmentManager().beginTransaction().replace(R.id.layout_FragmentsContainer, fragment).addToBackStack(null).commit();
}
}
public class MeFragment extends Fragment
{
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
return inflater.inflate(R.layout.fragment_me, container, false);
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
meLV = (ListView) getView().findViewById(R.id.lv_Inbox);
loadingListProgress = (ProgressBar) getView().findViewById(R.id.progress_LoadingList);
meList = new ArrayList<Message>();
meAdapter = new MessagesListAdapter(getActivity(), meList);
//addFooter();
meLV.setAdapter(meAdapter);
meLV.setOnItemClickListener(this);
pageCount = 0;
loadmoreProgressDialog = new ProgressDialog(getActivity());
loadmoreProgressDialog.setTitle("Please wait ...");
loadmoreProgressDialog.setMessage("Loading more ...");
loadmoreProgressDialog.setCancelable(true);
loadUserMessages();
meLV.setOnScrollListener(new EndlessScrollListener() {
#Override
public void onLoadMore(int page, int totalItemsCount) {
// TODO Auto-generated method stub
//addFooter();
loadmoreProgressDialog.show();
loadUserMessages();
}
});
}
#Override
public void onItemClick(AdapterView<?> parent, View view, int position,
long id) {
// TODO Auto-generated method stub
Utils.showToast_msg(getActivity(), "MessageItemClicked");
ReferralDetailFragment fragment = new ReferralDetailFragment();
getFragmentManager().beginTransaction().replace(R.id.layout_FragmentsContainer, fragment).addToBackStack(null).commit();
}
}
public class ReferralDetailFragment extends Fragment implements OnClickListener {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
View view = inflater.inflate(R.layout.fragment_referraldetail,container, false);
linkToAcknowledge = (TextView) view.findViewById(R.id.lbl_Link_to_Acknowledge);
return view;
}
}
I implemented a simple solution for this in my app, basically when you press back to go to the fragment again, onCreateView() is called. Here in onCreateView() you have done all initialization, so we change
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
View view = inflater.inflate(R.layout.fragment_me, container, false);
/*
*Whatever you want to do
*
*/
return view;
}
to:
View view;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
if(view==null){
view = inflater.inflate(R.layout.fragment_me, container, false);
/*
*Whatever you want to do
*
*/
}
else{
((ViewGroup)view.getParent()).removeView(view);
}
return view;
}
Here, we move View view outside and make it a class variable. So if it is the first time the fragment is called, it is null and the initialization occurs, otherwise it goes to else black. Else block is required because onCreateView() adds whatever it returns as a child of the view's parent, so since view is already there, we remove it and onCreateView automatically adds it again.
According to our exchange in the comments, I completely deletde my answer and re-write a new one.
I copy/paste the code from one of my apps and removing the useless things and changing the names. Hope there is not too many typing mistakes, at that it is the minimum required to have it working.
When I pop back to FirstFragment from SecondFragment, the scroll position of FirstFragment is the same as when I clicked an item to load the SecondFragment.
Note that I don't extend FragmentActivity. I have an activity which loads the fragments.
Extend/modify to match your needs.
MainActivity :
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_layout);
}
}
FirstFragment Class :
public class FirstFragment extends Fragment implements OnItemClickListener {
private ListView mListView;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.first_fragment_layout, container, false);
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
mListView = (ListView) getView().findViewById(R.id.listview_first_fragment);
mListView.setAdapter(mAdapter); // depends on your adapter
mListView.setOnItemClickListener(this);
}
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
mListView.setItemChecked(position, true);
//in case you need, set the bundle here, for example pass the position
Bundle arguments = new Bundle();
arguments.putInt("position", position);
SecondFragment secondFragment = new SecondFragment();
secondFragment.setArguments(arguments);
getFragmentManager().beginTransaction().replace(R.id.fragment_container, secondFragment).addToBackStack(null).commit();
}
}
SecondFragment Class :
public class SecondFragment extends Fragment {
private Integer mPosition;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.second_fragment_layout, container, false);
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
Bundle arguments = getArguments();
if (arguments == null) {
mPosition= 0;
} else {
mPosition= arguments.getInt("Position");
}
}
}
What you are trying to achieve may be done with help of savedInstanceState. i also had this kind of problem which i resolved by using add() method instead of replace() in transition.
If you can change your method or already not using add() than give it a shot.
and if add() method didn't do the trick then check the implementation of savedInstanceState.
correctly save instance state.
How to save states of fragment views.
Hi I'm new to android and was trying to dynamically add content to a Fragment.
I've two Fragments inside an Activity. The first point to the second like this:
public static class PlaceholderFragment extends Fragment {
public PlaceholderFragment() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_welcome,
container, false);
Button searchActivityBtn = (Button) rootView.findViewById(R.id.SearchActivityBtn);
searchActivityBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Log.d("##SecondActivity", "search btn click");
getFragmentManager().beginTransaction()
.replace(R.id.welcome_fragment, new searchAnnouncementsFragment()).addToBackStack(null).commit();
}
});
return rootView;
}
}
the second Fragment class is
public static class searchAnnouncementsFragment extends Fragment {
public searchAnnouncementsFragment() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_search,
container, false);
return rootView;
}
//public void onStart(){
// super.onStart();
// RelativeLayout root = (RelativeLayout) getActivity().findViewById(R.id.annunciContainer);
// Log.d("##onstart", "root:" + (root == null));
//}
}
As you can see i tried to do something in onStart method but I've always a bunch of errors, the R.id.annunciContainer is the id of the fragment_search.xml layout
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id ="#+id/annunciContainer"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/white" >
What I'm trying to achieve is to dynamically add elements like other FrameLayout to my Fragment ( represented by the above RelativeLayout ).
Any thought?
You didn't mention the name of the activity, lets say the name as Activity1,
First create a linear layout or frame layout in the Activity1
Second add the following lines of code to your Activity1 for inflating PlaceholderFragment into your layout in Activity1
PlaceholderFragment pf = new PlaceholderFragment();
FragmentManager fm = getFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
ft.replace(R.id.annunciContainer, pf,"place_holder");
ft.commit();
For inflating searchAnnouncementsFragment replace PlaceholderFragment with searchAnnouncementsFragment.
What I was searching for are ListView