I'm trying make an app that displays a big amount of text and images in a specific layout when the user clicks on a corresponding listview item. Since I want specific .xml layouts for separate 'chapters', I want only the layout of a fragment to change to the corresponding chapter.
Googling tells me I can do this with fragments, but as far as I understand, I need separate fragment classes and .xml layouts for every chapter I want to implement. With 2 or 3 chapters, that can be done, but with more chapters that will become I thought, isn't it simpler to just keep two fragments (one with a listview and one with the chapter text), but dynamically change the layout of the second fragment if the user clicks on an item in the listview.
Can this be done with some code like this (just thinking out loud)?
Int[] layouts = {
{R.layout.chapter1, R.layout.chapter2, R.layout.chapter3}
};
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
index = intent.getIntent("index", 0);
return inflater.inflate(layouts[index], container, false);
}
Or is there another way to achieve this?
Fragments at their core are View wrappers that contains states of Views. You can use them for other purposes like resource handling, but mostly they're just segments of an Activity state. It most likely would not be a good idea to have a Fragment for every single chapter unless each chapter has their own unique state that needs to be kept. However, if the Views are static, then a single Fragment is all you really need.
In this case, you simply have to have a method like this:
public void setChapter(int chapter)
{
Context ctx = getActivity();
if(ctx == null) {
// detached from Activity, so bail.
return;
}
if(chapter > layouts.length) {
return;
}
ViewGroup container = (ViewGroup) getView().findViewById(R.id.chapter_container);
container.removeAllViews();
View chapterInflater = LayoutInflater.from(ctx).inflate(layouts[chapter], container);
}
So this will wipe out all views currently in your container, inflate a new one, and put it in the container (most likely a simple FrameLayout).
The code in the original question can be used to initialize a Fragment if you want to open it at a certain point. onCreate_() methods are called only when the items is being "built" or "created". onCreateView() won't be called again though, so you need a method to change the layout once it's set.
Related
I have some content in an Android app which is shown in a list. Each list entry has similar fields - let's say a picture, some text, and a text box. However, some list entries are different than others. The order of the content is based on the result of a server call.
The list itself needs to be fairly dynamic, and I'm currently using a linearlayout rather than a listview for a few reasons. My code looks something like this:
LinearLayout list = findViewById(android.R.id.list);
while (more content to add) {
switch (content type) {
case A:
View v = layoutInflater.inflate(R.layout.list_item_type_a, list, false);
EditText editText = (EditText)v.findViewById(android.R.id.edit);
// Do stuff with editText
list.addView(v);
break;
case B:
View v = layoutInflater.inflate(R.layout.list_item_type_b, list, false);
....
}
}
This works great. Except - when I put this in a fragment, and rotate the screen, now my app crashes because I have multiple fields with the same android.R.id.edit identifier.
I had thought this was a fairly elegant solution and the Android gods seem to disagree. Do I need to rip out the ID for all of my xml sublayouts? If I go this route, how should I grab references to the content?
So obviously, ListView or RecyclerView would be preferable to use here, but since you've stated you have reasons not to, I'd suggest that you disable automatic state saving for each of the views.
You can just call editText.setSaveEnabled(false), which will fix the issue, but have the side effect of not automatically retaining the view's state (e.g. input data will be lost). If you're maintaining this data yourself and restoring it on configuration changes or state restoration, this should be a totally workable solution.
I believe you could also just call setSaveFromParentEnabled(false) on the containing LinearLayout (although I haven't used that flag myself), which should disable state saving for any view in the sub-hierarchy. Same caveat applies.
I would suggest using a ListView instead of the LinearLayout and creating a custom adapter to fill the ListView. You could still have the list_item layouts that you have and then add them to the list in the newView method of your adapter. Pass your content type through a method, say getItemViewType(). Something like this:
#Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
// Choose the layout type
int contentType = getItemViewType();
int layoutId = -1;
switch (contentType) {
case A: {
View v = layoutInflater.inflate(R.layout.list_item_type_a, list, false);
EditText editText = (EditText)v.findViewById(android.R.id.edit);
// Do stuff with editText
list.addView(v);
break;
}
case B: {
View v = layoutInflater.inflate(R.layout.list_item_type_b, list, false);
....
}
}
I think this problem occur due to device screen rotation
When the phone rotates and the screen changes orientation, Android usually destroys your application’s existing Activities and Fragments and recreates them. Android does this so that your application can reload resources based on the new configuration.
The most important aspect of handling orientation changes is saving state. In most cases this involves implementing the onSaveInstanceState method (this could be in your Activity, Fragment or both) and placing the values you need to save in the Bundle argument that gets passed to the method.
For more details and code examples...
Please read this article
If you have a dynamic list, you should be using a ListView or RecyclerView.
My FragmentActivity loops and creates 8 Fragments from the same xml and activity. The fragment has a TextView, by passing parameters to the Fragment, I want to display different text inside each TextView of the Fragments. With this method, I can save creating 139 identical fragments with different texts.
Problem, all 8 fragment's TextView changes when I setText(), because they all share the same template (xml and activity).
Solution - see my answer below.
Extremis
It's because when you duplicate your Fragment, you refer to the same view for each fragment you create. You have to change the id of the fragment. If you don't do that you call the same fragment all the time.
Final Answer : On onCreateView method
private static int id = 0;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
LinearLayout mLinearLayout = new LinearLayout(this);
TextView mTextView = new TextView(this);
mTexTView.setId(Id);
id++;
mLinearLayout.addView(mTextView);
return mLinearLayout;
}
After you can use the id of your textview and set the text.
Okay, so I've learned that something was wrong in my code when I tried to duplicate fragments in th way attempted above.
My Solution
I created a new dummy Android project and selected the Navigation type as Scrollable Tabs + Swipe.
I then learned that it had 1 activity:
main.java - whcih extends:
FragmentActivity
and, 2 layout's:
main.xml - which contained:
<android.support.v4.view.ViewPager> , and
<android.support.v4.view.PagerTitleStrip>
Fragment.xml - which contained:
TextView
By adjusting the adapter, I was able to create unique Fragments based on the Same layout (xml).
So ultimately - Create a new dummy project with the Scrollable Tabs + Swipe and adjust your code based on that example.
Hope this help.
Extremis
Context: I'm using the Wizard with in Eclipse and ADT to get a master detail view framework.
I have a good understanding of the fragments need to sit with in an activity and that with in a view etc how ever looking at the example I'm trying to work out how best to change it so I can start developing a app my self.
Is the 'dummy' content view actually needed ? All it seems to contain is the menu options ?
The questions I have is does any one know how the detail view is created ? From what I am reading the app is just taking the item ID and placing it in the detail view as text ?
Extract from Itemdetailfragment:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_item_detail, container, false);
if (mItem != null) {
((TextView) rootView.findViewById(R.id.item_detail)).setText(mItem.content);
}
return rootView;
}
Is the best way to re do a detail fragment for each new screen I need and place some logic behind that or code the existing one to look at the menu option selected and display the right code ?
Finally is this the best example to start working with ?
The questions I have is does any one know how the detail view is
created ? From what I am reading the app is just taking the item ID
and placing it in the detail view as text ?
Yes, that is exactly what it is doing. This is done in the detail fragment
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_team_detail,
container, false);
// Show the dummy content as text in a TextView.
if (mItem != null) {
((TextView) rootView.findViewById(R.id.team_detail))
.setText(mItem.content);
}
return rootView;
}
You need to change it to get whatever data you need from whatever source you want to use (website, database or change the hard coded values
If you look at the DummyContent.java class you will see the comment
/** TODO: Replace all
uses of this class before publishing your app. */
Basically you should re-write this class to get the data that you want
I've actually added action bar sherlock to make this compatible with api 8 with a little help from this question Two questions about Master/Detail Flow Template
There is a hint that you could use ContentLoader in conjunction with a ContentProvider to get your data from another source such as a database or a web service
I'm writing a calculator application in which I would like to be able to switch between 4 modes of calculation: Decimal, Binary, Octal, and Hex. In order to manage the different UIs for the different modes, I have 4 Fragment subclasses in my Activity. Each Fragment has its own XML layout file, in addition to the main XML file for the Activity. I found a guide on the Android Developer site for inflating layouts for Fragments, and I've followed that guide. However, I would like to add listeners and so on to the various components of the layouts, preferably within the onCreateLayout method of the Fragment, or somewhere else where I could do it easily and minimize code duplication.
It appears, however, that when I try to call findViewByID to access one of the inflated Views (after I've called LayoutInflater.inflate, obviously), I get a null return value. This issue occurs whether I call findViewByID from within onCreateLayout or from elsewhere in the Activity (after the Views have, theoretically, been created). What's going wrong here?
One issue I think might be a problem is that I've overloaded the names of the Views between the various Fragment layouts. For example, the "1" button in the Binary layout has the same ID as the "1" button in the Hex layout. Is this allowed, assuming the Binary and Hex layouts are never both part of the Activity at the same time?
Thanks.
I think same id in different layout is not problem in Fragement. First you have to catch the inflated view then find whatever inside this. For example --
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
view = inflater.inflate(R.layout.frg1, container, false);
android.util.Log.v("", "!!!!!!!!!! Frg1 !!!!!!!!!");
Button b = (Button) view.findViewById(R.id.b1);
b.setOnClickListener(new View.OnClickListener(){
public void onClick(View v) {
Toast.makeText(getActivity(), "here", Toast.LENGTH_SHORT).show();
}
});
return view;
}
I'm building and Android app that needs to go through steps like a wizard.
Current structure:
At the moment I'm using one activity with separate views.xml for each step, then I'm using setContentView(activeStep) to display the active step.
I ran into some difficulties when trying to animate between the steps. I used the following code:
LayoutInflater inflater = getLayoutInflater();
View view = inflater.inflate(activeStep, null, false);
view.startAnimation(AnimationUtils.loadAnimation(activity, R.anim.slide_in));
setContentView(view);
The result: the first view was gone and the new one animated, not a smooth transition.
My goal is to animate both views, one slides out the other in.
Question: Is it possible to do it with my current structure (reminder: one activity, many views) or should I treat each step as a separate activity?
I guess there is more the one way of implementing step progress with animation, here is how I did it:
private static ViewAnimator viewAnimator;
public void onCreate(Bundle savedInstanceState) {
viewAnimator = new ViewAnimator(this);
View step1 = View.inflate(activity, R.layout.step_1, null);
View step2 = View.inflate(activity, R.layout.step_2, null);
View step3 = View.inflate(activity, R.layout.step_3, null);
viewAnimator.addView(step1);
viewAnimator.addView(step2);
viewAnimator.addView(step3);
viewAnimator.setInAnimation(activity, R.anim.slide_in);
viewAnimator.setOutAnimation(activity, R.anim.slide_out);
setContentView(viewAnimator);
}
then clicking a button I call viewAnimator.showNext() and viewAnimator.showPrevious()
ViewSwitcher was not good for my purpose, because it can hold only 2 views at a time
It would probably be best to use one Activity and a few different View structures if each step in the process is related.
You probably shouldn't use setContentView to change views with each step. Instead, possibly hide or unhide each item, or move it off the screen.