Hi there (and thanks in advance),
I have an application with a Google Play Store-like layout (using PagerTabStrip with 5 sub fragments). In some of those fragments I will need to display a little info regarding the last time the data was updated.
I immediately thought of creating a fragment (LastUpdatedFragment) which I would then add (nest) to the fragments I needed. At first, and since the last updated date was supposed to be the same for every screen, things were working (I simply added the fragment in the xml of the parent Fragments I needed and inside onCreateView I would put the date in the TextView), but after some changes I now need to send a specific date for each one of these LastUpdatedFragment instances.
I came up with one way - creating new custom attributes for my Fragment and then I would set the date I wanted. After some reading I stumbled across a easier way (Fragments within Fragments - dinamically adding the fragment using FragmentManager) - I simply needed the parent Fragment to handle the input parameters and pass it to the child fragment.
The problem is that, although I get no errors I also get no child fragment, it just does not display. I would be grateful if you could guide me in the right direction.
ScoreBoardFragment.java -> Parent Fragment
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
rootView = inflater.inflate(R.layout.fragment_scoreboard, container,
false);
for (int i = 0; i < tvCount; i++) {
tvName = "f_scoreboard_header_tv" + String.valueOf(i);
resID = getResources().getIdentifier(tvName, "id",
_activity.getPackageName());
header = (TextView) rootView.findViewById(resID);
header.setTypeface(MyGlobalConfig.getInstance().getHeadersFont());
}
FragmentManager childFragMan = getChildFragmentManager();
FragmentTransaction childFragTrans = childFragMan.beginTransaction();
LastUpdatedFragment fragB = new LastUpdatedFragment();
childFragTrans.add(R.id.FRAGMENT_PLACEHOLDER, fragB);
childFragTrans.addToBackStack("B");
childFragTrans.commit();
return rootView;
}
fragment_scoreboard.xml (simplified but displaying everything else)
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/FRAGMENT_PLACEHOLDER"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<LinearLayout
style="#style/ListView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<TextView
android:id="#+id/f_scoreboard_header_tv0"
style="#style/ListViewRowHeader"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="#" />
</LinearLayout>
<ListView
android:id="#+id/f_scoreboard_lvbody"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_centerHorizontal="true" >
</ListView>
</LinearLayout>
LastUpdatedFragment.java -> Child Fragment
public class LastUpdatedFragment extends Fragment{
private View rootView;
private TextView tv;
private Context ctx;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Thread.setDefaultUncaughtExceptionHandler(new ExceptionHandler(
getActivity()));
this.ctx = getActivity().getApplicationContext();
}
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
rootView = inflater.inflate(R.layout.fragment_date, container,
false);
tv = (TextView) rootView.findViewById(R.id.f_date_tv);
tv.setTypeface(MyGlobalConfig.getInstance().getBodyFont());
tv.setText(getString(R.string.lastupdated) + ": " + Util.getLastUpdated(ctx));
return rootView;
}
}
fragment_date.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<TextView
android:id="#+id/f_date_tv"
style="#style/LastUpdatedTV"
android:layout_width="match_parent"
android:text="Data de última actualização: 27/12/2014 21:30" />
</LinearLayout>
Try to use FrameLayout instead of LinearLayout as Placeholder.
Related
I am using fragments to update a text view I have so when the person clicks a button the text view moves on to the next question. I'm not sure if I am doing the correct work in one fragment instead of the other. My current screen looks like this:
I will probably have to add some more buttons/widgets to this but should I be adding it into the XML for the fragment or the fragment container?
Here is XML for fragment actions:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/fragment_question_layout1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="16dp"
tools:context=".FragmentActions"
>
<!-- this is where fragments will be shown-->
<FrameLayout
android:id="#+id/question_container1"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="4"
android:scaleType="centerInside" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="#+id/questions_yes1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:text="#string/yes" />
<Button
android:id="#+id/questions_no1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:text="#string/no" />
</LinearLayout>
</LinearLayout>
And here is the fragment details:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/button_layout1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="10dp"
tools:context=".FragmentDetails">
<!--Blank Fragment Layout-->
<TextView
android:id="#+id/questions_text_view1"
android:layout_width="match_parent"
android:layout_height="91dp"
android:gravity="center"
android:textAlignment="center"
/>
</FrameLayout>
Updated FragmentDetails
public class FragmentDetails extends Fragment {
private final String TAG = getClass().getSimpleName();
private List<Integer> mQuestionIds;
private int mListIndex;
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
//Inflate the fragment layout
View rootView = inflater.inflate(R.layout.fragment_details, container, false);
//Get a reference to the textView in the fragment layout
final TextView textView = (TextView) rootView.findViewById(R.id.questions_text_view1);
if (mQuestionIds != null) {
textView.setText(mQuestionIds.get(mListIndex));
//Increment the position in the question lisy as long as index is less than list length
if (mListIndex < mQuestionIds.size() - 1) {
mListIndex++;
setmQuestionIds(QuestionList.getQuestions());
setmListIndex(mListIndex);
} else {
//end of questions reached
textView.setText("End of questions");
}
//Set the text resource to display the list item at that stored index
textView.setText(mQuestionIds.get(mListIndex));
}
else {
//Log message that list is null
Log.d(TAG, "No questions left");
}
//return root view
return rootView;
}
public void setmQuestionIds (List < Integer > mQuestionIds) {
this.mQuestionIds = mQuestionIds;
}
public void setmListIndex ( int mListIndex){
this.mListIndex = mListIndex;
}
}
Fragment Actions activity
public class FragmentActions extends AppCompatActivity {
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.fragment_actions);
Button yes = findViewById(questions_yes1);
// Only create new fragments when there is no previously saved state
if (savedInstanceState == null) {
//Create Question Fragment
final FragmentDetails fragmentDetails = new FragmentDetails();
fragmentDetails.setmQuestionIds(QuestionList.getQuestions());
yes.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//set the list of question Ids for the head fragent and set the position to the second question
//Fragment manager and transaction to add this fragment
FragmentManager fragmentManager = getSupportFragmentManager();
fragmentManager.beginTransaction()
.replace(R.id.question_container1, fragmentDetails)
.commit();
}
});
}
}
}
If your Buttons remain the same while the TextView changes, you may add your Buttons to the fragment container.
Remember that, your fragments will be presented inside the FrameLayout of the fragment container. You gotta keep your Buttons, outside the FrameLayout.
Or if you want to have different Buttons for different fragments (Questions, in your case), you can also add the Buttons to the fragments. But in that case, you gotta add them separately to each of the fragments.
I guess there's no right answer to your question. You could try different approaches.
Maybe you could implement the buttons in the fragment container, as #smmehrab pointed out. I see this as a more difficult solution, because when you click on an item from the container you can manage the views of the container, not the fragment's views. You would get NullPointer if I recall correctly. This happens because the context when the button is clicked in the fragment container is different than the context when clicking from within the fragment. So you should implement an interface on the fragment container that listens to clicks, and the fragment catches the click. You could do this, and I actually am doing it in my current app, but I have no choice.
You could instead use Motion Layout (which extends from Constraint Layout) as the root view of your fragment, instead of CardView. This way you could set all the fragment's views with a flat hierarchy (flat hierarchies improves rendering time, so that's an improvement, and you can use CardView as one child) and set the buttons right there, in the Motion Layout (remember, the motion layout would be the fragment's root view). You could set the click listener right there and implement animations between different textViews.
I'm sure there are plenty of other solutions, take this only as a contribution.
If you're unfamiliar with Motion Layout you can just google it, android official documentation about it is great.
I have a search interface (with an android search widget). When I retrieve results, each results is wrapped into a folding-cell. Once the folding-cell is loaded, I fill the cell-title with a simple TextView. Then, I fill the content with a TextView (which works). In addition to the TextView, my cell-content also contains a LinearLayout. I use this layout to add a fragment, using a FragmentTransaction.
My problem is that each fragment is supposed to fill a different folding-cell, but only they all adds to the first folding-cell (so I guess that FragmentTransaction manager is adding all fragments to the same LinearLayout, the one of the first Folding-Cell).
Here is a screen of 1st and 2nd folding-cells, unfolded:
Here is the code for the search results :
try {
JSONArray jsonarr = new JSONArray(response);
for (int i = 0; i < jsonarr.length(); i++) {
JSONObject row = jsonarr.getJSONObject(i);
Bundle args = new Bundle();
args.putString("email", row.getString("email"));
args.putString("name", row.getString("nom_organisme"));
Fragment newFragment = new FragmentOrganisationFoldingCell();
newFragment.setArguments(args);
getFragmentManager().beginTransaction().add(R.id.container_organisation_presentation_for_search, newFragment).commit();
organisationList.add(newFragment);
}
if(jsonarr.length() == 0){
errorText.setVisibility(View.VISIBLE);
}
} catch (JSONException e) {
e.printStackTrace();
}
Here is the FramgmentOrganisationFoldingCell:
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_organisation_folding_cell, container, false);
final FoldingCell fc = (FoldingCell) view.findViewById(R.id.folding_cell);
Bundle args = getArguments();
TextView organisationName = view.findViewById(R.id.search_organisation_default_name);
organisationName.setText(args.getString("name"));
TextView t = view.findViewById(R.id.cell_content);
t.setText(args.getString("name"));
LinearLayout fragContainer = (LinearLayout) view.findViewById(R.id.cell_content_frag);
getFragmentManager().beginTransaction().replace(fragContainer.getId(), new FragmentProfileOrganisation(), "Organisation" + args.getString("name")).commit();
fc.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
fc.toggle(false);
}
});
return view;
}
Here is fragment_organisation_folding_cell, which is the view I inflate in FragmentOrganisationFoldingCell :
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#drawable/block">
<com.ramotion.foldingcell.FoldingCell
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/folding_cell"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clipChildren="false"
android:clipToPadding="false">
<!-- CONTENT (UNFOLDED) LAYOUT (MUST BE AT LEAST 2x times BIGGER than content layout bellow)-->
<include layout="#layout/cell_content_layout" />
<!-- TITLE (FOLDED) LAYOUT (MUST BE AT LEAST 2x times SMALLER than content layout above) -->
<include layout="#layout/cell_title_layout" />
</com.ramotion.foldingcell.FoldingCell>
</LinearLayout>
Here is the XML code for the cell_content_layout, which contains the LinearLayout I use to add my fragments. (So I beleive that this is the file that creates the trouble).
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:visibility="gone">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="60sp"
android:layout_margin="40dp"
android:id="#+id/cell_content"/>
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/cell_content_frag">
</LinearLayout>
</LinearLayout>
Here is the fragment_organisation_search XML file :
...
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#drawable/block"
android:id="#+id/container_organisation_presentation_for_search"
android:layout_margin="5dp">
</LinearLayout>
...
Do anyone have any trouble adding fragments into Folding-cells too? If anyone have any idea I would be really thankful !
The following command in your code that is parsing search results will add newFragment to a view with ID R.id.container_organisation_presentation_for_search. That ID is in your XML for the actual fragment.
getFragmentManager().beginTransaction().add(R.id.container_organisation_presentation_for_search, newFragment).commit();
I think instead what you want may be:
getFragmentManager().beginTransaction().add(R.id.cell_content_frag, newFragment).commit();
I have a view pager consisting of 4 tabs. Each tab holds its own fragment.
How would I be able to use fragment transaction to replace the fragment in tab 3 with a new fragment?
I've tried a lot of things, but I have not been able to come up with a solution at all. I've tried looking around on google and stackoverflow, but with no success.
I assume that your fragment has a button that is put in the center. By clicking on it, you can change the layout stays under of this button. The content/layout of the first fragment you mentioned should be replaced with wrapperA and the content/layout of the second one should be replaced with wrapperB. I put a simple red background for wrapperA to distinguish it with wrapperB, wrapperB is also green due to the same reason. I hope this is what you want:
public class SwitchingFragment extends Fragment {
Button switchFragsButton;
RelativeLayout wrapperA, wrapperB;
View rootView;
#Override
public View onCreateView(LayoutInflater inflater, final ViewGroup container, Bundle savedInstanceState) {
rootView = inflater.inflate(R.layout.layout_switch, container, false);
wrapperA = (RelativeLayout) rootView.findViewById(R.id.wrapperA);
wrapperB = (RelativeLayout) rootView.findViewById(R.id.wrapperB);
switchFragsButton = (Button) rootView.findViewById(R.id.switchFragsButton);
switchFragsButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View arg0) {
if (wrapperB.getVisibility() == View.GONE) {
wrapperB.setVisibility(View.VISIBLE);
// There is no need to change visibility of wrapperA since it stays behind when wrapperB is visible.
// wrapperA.setVisibility(View.GONE);
}
else {
wrapperB.setVisibility(View.GONE);
// Again, there is no need.
// wrapperA.setVisibility(View.VISIBLE);
}
}
});
return rootView;
}
}
The layout:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:orientation="vertical">
<!-- The content of first fragment should be in "wrapperA". -->
<RelativeLayout
android:id="#+id/wrapperA"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/red"
android:visibility="visible">
</RelativeLayout>
<!-- The content of second fragment should be in "wrapperB". -->
<RelativeLayout
android:id="#+id/wrapperB"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/green"
android:visibility="gone">
</RelativeLayout>
<!-- As I said, I assume that the layout switcher button is stable
and so it should be in front of the switching layouts. -->
<Button
android:id="#+id/switchFragsButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:background="#color/black"
android:text="Switch"
android:textSize="20sp"
android:textStyle="bold"
android:textColor="#color/white"/>
</RelativeLayout>
Alternatively, you can try to change the fragment directly by notifying your FragmentPagerAdapter as described in this link:
Replace fragment with another fragment inside ViewPager
I have the following fragment working in view pager:
<RelativeLayout
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:background="#color/window_background"
android:id="#+id/font"
tools:context="com.cuentos.lib.cuentosmusicales.Libro">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="5dp"
android:gravity="center"
android:id="#+id/titleBook"
android:textSize="25sp"
android:textColor="#2211CC"
android:text="#string/hello_blank_fragment" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="#+id/titleBook"
android:id="#+id/textBook"
android:textSize="15sp"
android:textColor="#44BB11"
android:text="#string/hello_blank_fragment" />
</RelativeLayout>
and i want to modify textview values from fragment class
public class Libro extends Fragment {
String titleLibro;
String text;
TextView title, text1;
public Libro() {}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_libro, container, false);
title = (TextView) view.findViewById(R.id.titleBook);
text1 = (TextView) view.findViewById(R.id.textBook);
title.setText("hi friends");
text1.setText("text");
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_libro, container, false);
}
}
So, I detect im not able to change data because there is no change, so how I can instanciate textview in fragment?
First, you're inflating your view two times. There's one on the first line of the onCreateView, which is the one that you modify the TextViews, but in the return statement you create and return another, you're not returning the one you modified, but a new one instead, so the view that will be presented in the UI is the not modified.
Also, you're calling the findViewById in the wrong lifecycle stage. Notice that onCreateView returns the view that will be present in your fragment. It's not possible to retrieve a view component like yours TextViews that way, because the view itself doesn't exists yet.
The right method to modify your view components is in the onViewCreated:
#Override
public void onViewCreated(View view, Bundle savedInstanceState)
{
title = (TextView) view.findViewById(R.id.titleBook);
text1 = (TextView) view.findViewById(R.id.textBook);
title.setText("hi friends");
text1.setText(text);
}
Try setting the text when overriding your Fragments onActivityCreated or onResume methods.
So the error that is thrown is
java.lang.IllegalStateException: Content view not yet created
This is thrown when I uncomment the getListView().setOnScrollListener(this); line.
public class MyCategoryFragment extends ListFragment implements OnScrollListener {
private ArrayList<Article> m_articles = new ArrayList<Article>();
public ArticleAdapter m_artadapter;
public MyCategoryFragment() {
super();
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_category, container, false);
m_articles = manager.loadCategory(getActionBar().getTitle().toString(), 10);
m_artadapter = new ArticleAdapter(this.getActivity(), R.layout.article_button, m_articles);
this.setListAdapter(m_artadapter);
//getListView().setOnScrollListener(this);
return rootView;
}
Here is the layout just so you can see it is the correct listview
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<ListView
android:id="#android:id/list"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
/>
<TextView
android:id="#android:id/empty"
android:textColor="#FFFFFF"
android:textSize="30sp"
android:gravity="center_vertical|center_horizontal"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:text="It's not you it's us..."/>
</LinearLayout>
I have researched this extensively and I believe in must be something so simple but it is taking more than a day to fix this. Thanks
as the onCreateView doc stays:
creates and returns the view hierarchy associated with the fragment
so since the method does not return, you will are not able to access the ListView through getListView(). You can obtain a valid reference in the OnActivityCreate callback.