I couldn't think of a better question title :(
In my application I need to have a questioner like thing with 10+ questions, 1 question each time.
What I did is:
Have 1 Activity say MainActivity, in its onCreate() programatically add a Fragment say Frag_1
In Frag_1 i have an Interface onButtonClickedListener with a method onClicked to get a call back to the Activity to know which option is clicked etc..,
Implemented the method onClicked of this Interface in my MainActivity and in that Method i changed the Fragment to Frag_2 (2nd question) and so on.
The problem is:
I ended up having 10 Fragments and my MainActivity implements 10 interfaces 1 each of these Fragments. In future I may have 30-40 question.
Edit:
Every question will not be similar. Answer options for questions are EditText/Spinner/RadioButtons etc.., so i cannot use the same Fragment and just change the Question and Answer options Text.
Is there a better and more efficient way to approach this
Thank You
You're almost there.
Make a more broad/generic interface for the MainActivity to implement.
Maybe something like that public void fragClickCallback(int fragId, Bundle data);
that way on you implementation you can make a switch() case to know which question is from and your bundle can pack different keys for different types of answers (int for radion button/spinners, String for open answer, etc).
And mare sure that on every fragment transaction you use .replace(...) to remove the fragment before.
edit
furthermore, there are some clever ways you can built your fragments to not have to create billions of them. How many different types of questions there can be? let's count:
multiple choice (single answer) -> integer (index)
multiple choice (multiple answers) -> integer (flags)
Number input -> float/double
Edit Text -> String
Date Picker -> long (epoch)
time picker -> 3 integers(?)
so we have 6 fragments and 6 ways of handling the switch statement, now it's just pass the right parameters to the fragments
edit:
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt("curChoice", mCurCheckPosition);
}
and then on your Fragment ActivityCreated()
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
if (savedInstanceState != null) {
// Restore last state for checked position.
mCurCheckPosition = savedInstanceState.getInt("curChoice", 0);
}
}
those code was copied from http://developer.android.com/reference/android/app/Fragment.html I suggest you further reading that page.
That example is restoring a cursor position, but you can use it to restore your previous user answers (text, integer, float, whatever).
Related
I have an enhanced loop, which will dynamically inflate however many layouts relevant to the number of values held in my array.
This works perfectly however, there is a method being called on each iteration, which also works but there is a big bug that I need help resolving.
Imagine there are 5 items in my array, therefore 5 layouts are inflated, in these layouts there is a little scratchcard type section on the layout.
Now if the user is on page 1, uses the scratchcard, then moves on to page 2, uses the scratchcard etc etc, it works fine.
But if the user is on page 1 and then goes to say, page 5 and then back to page 1 (basically in a random order), the scratchcard doesn't work.
From my understanding, the reason for this is that the method is being called an implemented on each iteration and the view is losing its state if the user scrolls back or scrolls in random orders.
Therefore I need a way to save the created view state in my viewpager.
Is this possible for my scenario? I have tried my best to find a solution, but cannot find something that feels relevant to my question.
Here is a snippet of the code in question. Thanks for any guidance or suggestions!
for (String x : array1) {
//loop out the number of layouts relative to the number of questions held in x
View current_layout = LayoutInflater.from(getActivity()).inflate(R.layout.question_fragment, null);
//use the pageAdapter to add the layout to the users view
pagerAdapter.addView(current_layout);
//call method to add functionality to the scratchcard
isCorrect(current_layout);
}
public void isCorrect(View current_layout) {
ScratchoffController controller1 = new ScratchoffController(getActivity())
.setThresholdPercent(0.40d)
.setTouchRadiusDip(getActivity(), 30)
.setFadeOnClear(true)
.setClearOnThresholdReached(true)
.setCompletionCallback(() -> {
})
.attach(current_layout.findViewById(R.id.scratch_view1), current_layout.findViewById(R.id.scratch_view_behind1));
ScratchoffController controller2 = new ScratchoffController(getActivity())
.setThresholdPercent(0.40d)
.setTouchRadiusDip(getActivity(), 30)
.setFadeOnClear(true)
.setClearOnThresholdReached(true)
.setCompletionCallback(() -> {
})
.attach(current_layout.findViewById(R.id.scratch_view2), current_layout.findViewById(R.id.scratch_view_behind2));
ScratchoffController controller3 = new ScratchoffController(getActivity())
.setThresholdPercent(0.40d)
.setTouchRadiusDip(getActivity(), 30)
.setFadeOnClear(true)
.setClearOnThresholdReached(true)
.setCompletionCallback(() -> {
})
.attach(current_layout.findViewById(R.id.scratch_view3), current_layout.findViewById(R.id.scratch_view_behind3));
ScratchoffController controller4 = new ScratchoffController(getActivity())
.setThresholdPercent(0.40d)
.setTouchRadiusDip(getActivity(), 30)
.setFadeOnClear(true)
.setClearOnThresholdReached(true)
.setCompletionCallback(() -> {
})
.attach(current_layout.findViewById(R.id.scratch_view4), current_layout.findViewById(R.id.scratch_view_behind4));
}
I ussually use ViewPager with Fragments and what you mention has happend to me when I try to keep references to the Fragment instances (in my case) outside of the viewpager.
This happens because the viewpager may create new instances of the Fragment it contains when you re-vist the tab in the way you mention. When this happens, the instance reference you hold outside of the viewpager is not anymore what the viewpager is showing.
In your case , according to this question, you have to oveeride instatiateItem and destroyItem. I think you can use these methods to save state restore state, and also you could update any external reference when instantiateItem is called.
My activity has a spinner and an empty container where fragments should be added when selecting drop down items from spinner.
My plan was to try to make switch construction inside into override method "public void onItemSelected()", where each case represents one drop down item from spinner, and sets correct fragment into container, like this:
String itemSelectedFromSpinner = parent.getSelectedItem().toString();
switch (itemSelectedFromSpinner) {
case "first item": // First and second item put same fragment into container, but do other methods when used
case "second item": // my code
}
My other taught was to put it in if construction like this:
String itemSelectedFromSpinner = parent.getSelectedItem().toString();
if (itemSelectedFromSpinner.equals("first item") || itemSelectedFromSpinner.equals("second item")){
// my code }
Since I've never done something like this, and I believe you can understand from my question what needs to be done, tell me what is the best practice to do that.
Am I doing it right by putting a String itemSelectedFromSpinner into switch construction? Also if user selects one item and first fragment is loaded, when selecting other item will the first fragment disappear and put second fragment into container automatically? (sorry if this sound little silly to you, I have lack of experience with fragments)
Don't use string like "first item" in code directly - move them to string resources.
For components like spinner use Adapter. The concept of Adapters use widely in Android so it's a good idea to be familiar with it. And also it allow you to compare your data by some integer asigned id's, and not by strings (which is unefficient, slow and ugly - correcting string representation everywhere is hard).
To replace or add Fragments dynamically use FragmentManager. See the simple replace() / add() / commit() code
I'm using the default android NumberPicker and am having issues when using the setValue() method.
Just to note, the issues are occurring when i run API Level 16(Galaxy SII), I have tried an API Level 14 emulator and there are no issues so i'm a bit baffled.
My fragment contains number pickers and the numbers are saved to a DB table. I am returning the values to the number pickers when the fragment is accessed again.
Initially this works but if i replace the fragment in the activity, and then call the fragment again, the number pickers will display "0" but if i increase or decrease it, it will update from the number that it should be displaying. So 0 would jump to 5 if the number that is saved to the DB is 4.
Has anybody come across this and could they direct me on how to resolve this issue?
Thanks.
I can't be certain without seeing your code, but my guess is that your Fragment has not overridden the onAttach() Fragment class method.
By overriding that method and performing my setValue() in that function like so, I was able to fix this issue:
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
if (number_picker != null)
number_picker.setValue(desired_level);
}
I'm not familiar with FragmentPagerAdapter, so this is going to be one of those questions that we (you) read the description critically.
Structure: I have a FragmentPagerAdapter (code below), that will hold two fragments at a time. The first displays book excerpts, and the second a list of book titles.
Goal: I want to achieve what is described in the title: the user can navigate to the second fragment in the pager, click on a title, and then I want to move the user back to the first fragment and tell the first fragment to update the text. The first fragment has a triggerRefresh method for that.
Code: I believe my problem happens because of the way FragmentPagerAdapter reuses/creates the Fragments (which I don't understand). This is my class:
static class MyFragmentPagerAdapter extends FragmentPagerAdapter {
public MyFragmentPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public int getCount() {
return NUM_ITEMS;
}
#Override
public Fragment getItem(int position) {
switch(position) {
case 0:
return new ExcerptsFragment();
case 1:
return new BookListFragment();
default:
throw new IllegalArgumentException("not this many fragments: " + position);
}
}
}
This is how I created the relevant members:
ViewPager mViewPager = (ViewPager) findViewById(R.id.pager);
MyFragmentPagerAdapter mFragmentPagerAdapter = new MyFragmentPagerAdapter(getSupportFragmentManager());
mViewPager.setAdapter(mFragmentPagerAdapter);
And this is what I've tried elsewhere in my Activity, when I receive the callback from the book titles Fragment with the title selected:
mViewPager.setCurrentItem(0); // back to excerpts screen page. It's OK.
// Here's the problem! How to identify the fragment 0
// to ExcerptsFragment and call its triggerRefresh()?!?
Series of problems:
Calling the adapter's getView() won't work because it will return a new instance of ExcerptsFragment, which is not the one currently attached (as expected, throws the exception).
I've seen many people here (example) just storing fragments in the getView(). Is that right? Because by looking at the official examples, seems like an anti-pattern to me (defeat the automatic reference by holding the items). And that is also the opinion here and here (and looks right to me).
Any suggestions? I wouldn't be surprised if I'm not understanding all of this one bit...
Disclaimer: Although this had worked perfectly fine for me before, you should be aware of the classic pitfalls of depending on internal, private behavior. While I wrote tests that would eventually warn me if the internal implementation changed, I have since moved on to greener pastures. And you should, too. As such, the value of this question and its answer is only historical, in my opinion.
Sorry about that question, I think it was the hour.
To solve that problem, I implemented this solution as is. Seems to work just fine. So, I believe it was just a matter of finding the (currently attached) fragment instance by figuring out how its Id is named. The link above explains how it's made.
I opted to answer my own question instead of deleting it because I believe novices like me on these pagers will benefit from a "real case scenario". Most of the answers I've seen talk most about the theory, which is the right way BTW... but without a real example to work on sometimes people like me get lost.
Anyway, here is the last piece of code that I needed (the commented part above):
int n = 0;
mViewPager.setCurrentItem(n); // in the question I had stopped here.
ExcerptsFragment f = (ExcerptsFragment) ContainerActivity.this
.getSupportFragmentManager().findFragmentByTag(getFragmentTag(n));
f.triggerRefresh();
// ... below the helper method: used the solution from the link.
private String getFragmentTag(int pos){
return "android:switcher:"+R.id.pager+":"+pos;
}
So, I'm having a feeling that this is a robust solution, because I'm not holding any references to fragments (thus risking the references being outdated). I kept my own code at a minimum, therefore minimizing the chances of me doing something stupid.
Of course, if you have something to add, to show us, to tell what is wrong in doing it or what can be improved, I'll be glad to hear from you.
I searched for a solution to this problem a while myself. Your approach in principle works, but it will break your code if ever the code of the fragment tag creation in the Android base class implementation changes. This is a quite nasty dependency!
A more elegant approach would be to turn the problem around and keep an instance of your base activity in your fragment. Implement a setter for the tag in your activity and call that inside the fragment upon creation - the tag there is simply available with getTag().
An example implementation can be found here.
I solved this problem by using WeakReferences to the fragments upon creation. See : https://stackoverflow.com/a/23843743/734151
If you find anything wrong with this approach, please comment.
This is my first question to stackoverflow, so please do not see my acceptance rate.
What I am trying to create is a quiz in android. Here I am having the same layout where I have a question and 3 answers. I have a next button at the bottom. When I click on that the same layout should get loaded with new question and options for it.
I am thinking of how to do it. I saw something called view flipper, but can anyone tell me the exact solution for this?
Here is my code
public class TriviaQuiz extends Activity {
Trivia trivias = TourDescription.currentTour.getTriviaArray().get(0);
Here trvias is an object which contains all the data.
}
// send the trivias to the activity where I set the question and options
public void playquiz(View v) {
Intent quizIntent = new Intent(TriviaQuiz.this, Quiz.class);
Bundle bundle = new Bundle();
bundle.putSerializable("trivia", trivias);
quizIntent.putExtras(bundle);
startActivity(quizIntent);
}
public class Quiz extends Activity
]
In this last activity I have the layout and the next button. Hope it is clear now.
Note:Here am trying to update the array value at get(0)
Please help me a way for this.
You can just do it like this, call a method with the array index and then inside the method set the values as needed. Next when tap on the button,update the value of array index in the required manner.
You should update values on the button click for the next question. Do you see any problem with this approach?