I have an activity with a viewPager inside of it, and a static ArrayList of integers that I am shuffling using Collections.shuffle(list) in the activity's onCreate method, this viewPager's fragments are using the ArrayList in parent activity.
The problem is that whenever a new fragment instantiated of the viewPager the onCreate method of parent activity is called, and I don't want that to happen because I want the list to have the same data in all fragments and not reshuffled. Do fragments call the onCreate method of their parent activities everytime there is a new instance? if Yes how can I work around this to keep the list from shuffling every time?
CODE:
Activity Code:
public static final ArrayList<Integer> IDs = new ArrayList<>();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
IDs.add(0);
IDs.add(1);
IDs.add(2);
Collections.shuffle(IDs);
setContentView(R.layout.activity_walkthrough);
pager = (ViewPager) findViewById(R.id.pager);
adapter = new ScreenSlidePagerAdapter(getSupportFragmentManager());
pager.setAdapter(adapter);
Fragment Code:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = (View) inflater.inflate(
R.layout.fragment_walkthrough, container, false);
final TypedArray imgs = getResources().obtainTypedArray(R.array.walkthrough_images);
ImageView imageView = (ImageView) v.findViewById(R.id.image);
if (page == 0) {
imageView.setImageResource(imgs.getResourceId(Walkthrough.IDs.get(0), 0));
} else if (page == 2) {
imageView.setImageResource(imgs.getResourceId(Walkthrough.IDs.get(1), 0));
} else {
imageView.setImageResource(imgs.getResourceId(Walkthrough.IDs.get(2), 0));
}
return v;
}
Now I want the ArrayList "IDs" to always have the same data and order when ever I instantiate a new fragment but it is not working, every time I create a new fragment the method onCreate gets recalled and a reshuffle happens!
Fragments are added to activity and therefore fragments get affected by activity.
Activity can cause calling any fragment callback method, but fragment can't
The lifecycle of the activity in which the fragment lives directly affects the lifecycle of the fragment.
For example, when the activity receives onPause(), each fragment in the activity receives onPause().
Fragments have a few extra lifecycle callbacks, however, that handle unique interaction with the activity in order to perform actions such as build and destroy the fragment's UI.
These additional callback methods are like onAttach(), onCreateView(), etc.
It'll clears the somewhat relation between fragment and activity.
Thanks
Related
I use a custom adapter for my ListView in a fragment. When I go to a second fragment and press the back key to go back, though, my list doubles (everything from my ArrayList is added again to the listView). This happens everytime I go to my second fragment and come back (the ArrayList gets appended to the back again). I tried checking if the adapter is null, but apparently it's always null when you go back to the fragment.
I tried moving my code into the onResume() and the onStart() functions from this post [https://stackoverflow.com/questions/21446361/android-listview-duplicates-itself-when-i-launch-a-new-activity-and-press-back], but that doesn't fix my code :(
In addition, I tried adding arr.clear() and notifyAll() in onPause but that just breaks my code (cannot transition into second fragment). Same with onStop()
public void onStart() {
super.onStart();
for (int i = 0; i < device_names.length; i++) {
arr.add(device_names[i]);
}
final ListView list = (ListView)getView().findViewById(R.id.list);
CustomListView customAdapter = new CustomListView(context, arr);
if (list.getAdapter() == null) {
Log.i("SAKEGA", String.valueOf(list.getAdapter()));
Log.i("SAKEGA", "it's null");
} else {
Log.i("SAKEGA", "it's not null waaaaaaaaaah");
list.setAdapter(null);
}
list.setAdapter(customAdapter);
Log.i("SAKEGA", "HMPH");
Button tv = (Button)getView().findViewById(R.id.add_devices_text);
ImageButton add = (ImageButton) getView().findViewById(R.id.add);
tv.setOnClickListener(add_device_click); // this click listener calls the 2nd fragment
add.setOnClickListener(add_device);
}
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
ViewGroup root = (ViewGroup) inflater.inflate(R.layout.fragment_scanneractivity, null);
return root;
}
What I would like to do is, once we go back to the fragment, I clear the ListView and repopulate the listView with the ArrayList.
Try to create an empty array list in the onStart() itself as you wrote your code there. what you are doing is you are persisting that array and every time you comeback its loading your array again making the entries again
So, what my problem is that in one fragment(w/i a viewpager, I'll call this Fragment A) I click on this dynamically created button that adds a new fragment(I'll call this Fragment B) in a framelayout which allows me to use PayPal service. On PayPal Activity result, Fragment B communicates with the main Activity via a communicator(an interface class) to call Fragment A to change that text. But I'm getting a null pointer exeception crash.
To be specific:
what I did was that I made a global TextView variable that is initialized on click. I did this b/c I have a list of other things that are dynamically inflated and to avoid the TextView from being initialized with wrong layout I initialized it on click.
bidChange.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
eventListChangeKey = keyVal;
eventListChangeIdx = eventListIdx;
eventBiddingChangeIdx = finalI;
priceToChage = (TextView) biddersLayout.findViewById(R.id.single_list_bidder_bid_price);
Bundle bundle = new Bundle();
bundle.putInt("auctionID", auctionId);
bundle.putInt("dateID", dateId);
bundle.putInt("FromWhere", 2);
Fragment fragment = new Fragment_Home_ItemInfo_Bid();
fragment.setArguments(bundle);
FragmentManager fragmentManager = getActivity().getSupportFragmentManager();
fragmentManager.beginTransaction()
.add(R.id.container_mainScreen, fragment, "itemInfo_bid")
.addToBackStack(null)
.setTransitionStyle(FragmentTransaction.TRANSIT_FRAGMENT_FADE)
.commit();
}
});
In the main activity
public void changeBidderPrice(String s) {
Fragment fragment = viewPagerAdapter.getItem(1);
((Fragment_List) fragment).changePrice(s);
}
is what I do
back in Fragment A
public void changePrice(String val) {
priceToChage.setText(val);
dataMap.get(eventListChangeKey).get(eventListChangeIdx).getBidList().get(eventBiddingChangeIdx).setPrice(val);
}
I've thought this over an over but I just can't figure this out. I've searched for similar cases in StackOverflow but I wasn't able to get a help.
Would the problem be the way I initialize that TextView? or is it the way I'm calling Fragment A from the main activity?
for fragments onViewCreated() is called after onCreateView() and ensures that the fragment's root view is non-null. Any view setup should happen here. E.g., view lookups, attaching listeners.
source : codepath
for activities onCreate()
I'm trying to add a view in fragment B from fragment A, they both belong to the same activity but they are never running simultaneously
My activity:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
fm = getSupportFragmentManager();
Fragment_Main main = new Fragment_Main();
fm.beginTransaction()
.replace(R.id.mainContainer, main)
.commit();
/* Fragment_Main is a fragment with two buttons
* to either call fragment A or fragment B and
* mainContainer is a FrameLayout occupying the whole screen
*/
}
Fragment A:
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_subjects, container, false);
ButterKnife.bind(this, view);
AddButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
FragmentB.addMyView();
}
});
return view;
}
Fragment B:
public static void addMyView(){
final RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
params.setMargins(0, 200, 0, 0);
CustomView mView = new CustomView(mContext, "english", "a1", "10:12", "14:32");
mLayout.addView(mView, params);
// CustomView is the custom view I want to add
}
Question:
I know that a fragment can't communicate with another fragment directly, how do I tell the activity to add a view permanently to FragmentB when the method addMyView() is called?
Persisting for one activity lifecycle
FragmentA should set a flag (e.g. a boolean attribute) on the Activity instance.
When FragmentB is instantiated, the Activity should pass this flag to the created instance (e.g. via Bundle). FragmentB should then decide whether or not to add the View.
Persisting permanently
FragmentA should set a SharedPreferences flag.
FragmentB should check this flag in onCreateView and potentially add the View depending on the value.
General advice:
Solving communication between instances with static values/methods is almost always bad practice.
I'm having trouble with restoring state of a View inside a ViewPager. The content of the ViewPager is a view extending FrameLayout.
The problem is the FrameLayout.onRestoreInstanceState() is not being called if added programmatically into the ViewPager
Here's the code of my Activity.java
private ViewPager vPager;
private MainPagerAdapter mAdapter;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
setContentView(R.layout.activity_layout);
// all the findViewById stuff
CustomView cv1 = new CustomView(this);
CustomView cv2 = new CustomView(this);
cv1.setId(R.id.custom_view_id_1);
cv2.setId(R.id.custom_view_id_2);
mAdapter = MainPagerAdapter();
mAdapter.addView(cv1);
mAdapter.addView(cv2);
vPager.setAdapter(mAdapter);
}
MainPagerAdapter is a class from the accepted answer of this question
Source code for CustomView.java
#Override
protected Parcelable onSaveInstanceState() {
Log.d(TAG, "onSaveInstanceState() called");
}
#Override
protected void onRestoreInstanceState(Parcelable state) {
Log.d(TAG, "onRestoreInstanceState() called");
}
Here's my findings so far:
onSaveInstanceState() will be called but onRestoreInstanceState() is not
When I tried to add the View directly to the root of the Activity, it calls both functions.
I found out that Activity's onRestoreState function will be called before the ViewPager calls the adapter's instantiateItem() function. So when the activity restore its state, the ViewPager doesn't have any children yet, thus the savedState doesn't belong to anyone
So I figure out that I need to make one of two things to work:
Make sure the ViewPager instantiate the item before trying to restore the state, or
Calls the CustomView's onRestoreInstanceState() manually.
I somehow managed to make option number 2, but is there any way to do option number 1?
If I understood your question, you can save your ViewPager items state using mPage.setOffscreenPageLimit(4); 4 is the number of my Fragments inside ViewPager.
I want to be able to setText and getText of Views of individual Fragments. As it is now, when I setText of a Framgent's TextView it changes the text of that View in all Fragments.
I've been experimenting by moving things around, but here is my code as of this moment:
Fragment class
public class TestFragment extends Fragment{
View view;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
view = inflater.inflate(R.layout.test_fragment, container, false);
TextView tv = (TextView) view.findViewById(R.id.huh);
//tv.setText("AAAAAAAAAAAAAAAAAAA");
return view;
}
public void setText(String asdf) {
TextView test = (TextView) view.findViewById(R.id.huh);
test.setText(asdf);
}
}
Activity Class
public class Manage extends BaseActivity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.manage);
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
TestFragment fragment = new TestFragment();
//fragment.setText("ASDF");
fragmentTransaction.add(R.id.test_fragment, fragment, "testtag");
fragmentTransaction.commit();
}
}
The framgent.xml is pretty plain; just a single TextView.
Fragments are added to stack with a parameter named tag. In your case you've added your fragment with "testtag".
fragmentTransaction.add(R.id.test_fragment, fragment, "testtag");
If you create multiple instances of same fragment and add them with unique tags, then you are able to get them with that unique tags. When you get a fragment then you can reach its content.
FragmentManager fm = this.getSupportFragmentManager();
Fragment testtagFragment = fm.findFragmentByTag("testtag");
View targetView = testtagFragment.getView().findViewById(R.id.anyViewInsideContentOfYourFragment);
Edit:
I want to be able to setText and getText of Views of individual
Fragments.
This question has 2 parts.
To setText while initializing you have to pass your initial
parameters to your fragment while creating its instance. I suggest
you to use a static newInstance method for this. See sample
here
To getText read my answer above. Note that, you can get the content of a fragment after its onCreateView method is executed. So If you try to call getView method of a fragment at your activities onCreate method (after you add the fragment), that will return null. You can get its content successfully under a click event to test that, and use get or set operations of any view on that fragment's content.