I'm currently trying to programmatically add a fragment which has a view that I need to initializate immediately after the fragment is created. I tried to use FragmentManager.executePendingTransactions() right after FragmentTransition.commit(), but it doesn't work as it supposed to. I've made a research and found this, so I realized that FragmentManager.executePendingTransactions() doesn't work from onCreate() and then put code to onStart(), but it still doesn't work. Although in my case it's called from onItemClickListener(), I don't think it makes any difference. Here's my code:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.vk_music_list);
musicProgressBar = new MusicProgressBar();
listView = (ListView) findViewById(R.id.listView);
}
#Override
protected void onStart() {
super.onStart();
listView.setOnItemClickListener(this);
}
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
String audioUrl = ((VKApiAudio) parent.getAdapter().getItem(position)).url;
serviceIntent.putExtra("audioUrl", audioUrl);
//Inflate music progress bar
fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragment = fragmentManager.findFragmentById(R.id.musicBarContainer);
if(fragment == null) {
fragmentTransaction.add(R.id.musicBarContainer, musicProgressBar)
.setTransition(R.anim.move_from_the_bottom)
.commit();
}
//This guy doesn't work
fragmentManager.executePendingTransactions();
//Never get into this, because fragment is always == null
if(fragment != null) {
SeekBar seekBar = (SeekBar) fragment.getView().findViewById(R.id.progressBar);
seekBar.setMax(((VKApiAudio) parent.getAdapter().getItem(position)).duration);
}
startService(serviceIntent);
}
Alright, I finally found out what the issue is. The reason of the problem is that neither fragmentTransaction.commit() nor fragmentManager.executePendingTransactions() change reference that is stored in fragment variable. Therefore, after executePendingTransactions() I need to call findFragmentById() again, so reference that it stores will refer to the created fragment. Here's a working code:
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
String audioUrl = ((VKApiAudio) parent.getAdapter().getItem(position)).url;
serviceIntent.putExtra("audioUrl", audioUrl);
//Inflate music progress bar
fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragment = fragmentManager.findFragmentById(R.id.musicBarContainer);
if(fragment == null) {
fragmentTransaction.add(R.id.musicBarContainer, musicProgressBar)
.commit();
}
//Force to create fragment without scheduling so it's not necessarily to wait until it get scheduled
fragmentManager.executePendingTransactions();
//This is what I needed
fragment = fragmentManager.findFragmentById(R.id.musicBarContainer);
//Set song duration as a seekBar's maximum
if (fragment != null) {
SeekBar seekBar = (SeekBar) fragment.getView().findViewById(R.id.progressBar);
seekBar.setMax(((VKApiAudio) parent.getAdapter().getItem(position)).duration);
}
startService(serviceIntent);
}
I hope it will be helpful for somebody.
Related
I have a fragment with a mapview. I can open another fragment (call it listfragment) from the action bar, it works fine. But if I rotate the screen, and then try to open the listfragment, it does not load, instead the mapview flickers on time (the map in the view goes blank and then appears again). If I try to load the listfragment again by clicking the menuitem on the action bar, the app crashes with
java.lang.IllegalStateException: Fragment already added:
Part of MainActivity that loads the fragment:
#Override
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
mNewTrackFragment = (NewTrackFragment) getFragmentManager()
.findFragmentByTag(TAG_NEW_TRACK_FRAGMENT);
if (mNewTrackFragment == null) {
mNewTrackFragment = NewTrackFragment.newInstance();
}
FragmentManager fragmentManager = getFragmentManager();
fragmentManager.beginTransaction()
.add(R.id.fragment_container, mNewTrackFragment)
.commit();
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == R.id.action_track_list) {
if (null == mTrackListFragment) {
mTrackListFragment = TrackListFragment.newInstance();
}
FragmentManager fragmentManager = getFragmentManager();
fragmentManager.beginTransaction()
.replace(R.id.fragment_container, mTrackListFragment, TAG_TRACK_LIST_FRAGMENT)
.addToBackStack(null)
.commit();
return true;
}
Part of NewTrackFragment with the MapView:
public NewTrackFragment() {
}
public static NewTrackFragment newInstance() {
NewTrackFragment fragment = new NewTrackFragment();
return fragment;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_new_track, container, false);
mTrackTitleTV = (TextView) view.findViewById(R.id.tv_track_title);
mDistanceTV = (TextView) view.findViewById(R.id.tv_distance);
mElapsedTimeTV = (TextView) view.findViewById(R.id.tv_elapsed_time);
mSpeedTV = (TextView) view.findViewById(R.id.tv_speed);
mAscentTV = (TextView) view.findViewById(R.id.tv_ascent);
mDescentTV = (TextView) view.findViewById(R.id.tv_descent);
mAltitudeTV = (TextView) view.findViewById(R.id.tv_altitude);
mStartStopFab = (FloatingActionButton) view.findViewById(R.id.fab_startstop);
mMapView = (MapView) view.findViewById(R.id.new_track_mapview);
setupMapView(savedInstanceState);
return view;
}
private void setupMapView(Bundle savedInstanceState) {
mMapView.onCreate(savedInstanceState);
mMapView.getMapAsync(this);
}
I googled the best of the evening for some info about it but found nothing similar.
There's a couple of things wrong here. Right now, you are not passing TAG_NEW_TRACK_FRAGMENT as a tag in the call to add within onCreate. Because of this, you are adding a NewTrackFragment instance without an associated tag, and therefore the call to findFragmentByTag will always return null. Essentially, you are creating a new instance of NewTrackFragment every time you rotate the device. This is not good, because the state of the FragmentManager is preserved across device rotations, meaning it is still holding on to each Fragment you add to it. Because you are unconditionally calling add, the FragmentManager will end up holding multiple instances of NewTrackFragment.
With that said, what you should do is add the tag in the call to add and only call add once you know the FragmentManager is not currently holding on to an instance of NewTrackFragment:
mNewTrackFragment = (NewTrackFragment) getFragmentManager()
.findFragmentByTag(TAG_NEW_TRACK_FRAGMENT);
if (mNewTrackFragment == null) {
mNewTrackFragment = NewTrackFragment.newInstance();
FragmentManager fragmentManager = getFragmentManager();
fragmentManager.beginTransaction()
.add(R.id.fragment_container, mNewTrackFragment, TAG_NEW_TRACK_FRAGMENT)
.commit();
}
You might want to do something similar with your TrackListFragment as well:
if (id == R.id.action_track_list) {
FragmentManager fragmentManager = getFragmentManager();
mTrackListFragment = (TrackListFragment) fragmentManager.findFragmentByTag(TAG_TRACK_LIST_FRAGMENT);
if (null == mTrackListFragment) {
mTrackListFragment = TrackListFragment.newInstance();
fragmentManager.beginTransaction()
.replace(R.id.fragment_container, mTrackListFragment, TAG_TRACK_LIST_FRAGMENT)
.addToBackStack(null)
.commit();
}
}
I know this question has been asked, but I haven't succeeded with the answers.
I have a fragment with a recycler view in it. I have a button which can show and hide this fragment. This all works fine until the orietation of the screen is changed. Then the fragment is recreated, and the one on top is shown and hidden, but there is one behind which stays there.
I understand I need to use
if (savedInstanceState == null)
somewhere, but cannot manage to succeed where. Thanks very much,
Here is my code.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_maps);
recyclerViewFragment = new RecyclerViewFragment();
FragmentTransaction trans = getSupportFragmentManager().beginTransaction();
trans.add(R.id.recycle_view_container, recyclerViewFragment, RECYCLER_FRAGMENT);
trans.commit();
trans.show(recyclerViewFragment);
Button showHideButton = (Button)findViewById(R.id.button_show_hide);
showHideButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
showHideFragment(recyclerViewFragment);
}
});
showHideButton.playSoundEffect(SoundEffectConstants.CLICK);
}
public void showHideFragment(final Fragment fragment){
FragmentTransaction trans = getSupportFragmentManager().beginTransaction();
trans.setCustomAnimations(android.R.anim.slide_in_left , android.R.anim.slide_out_right);
if (fragment.isHidden()) {
trans.show(fragment);
Log.d("hidden","Show");
} else {
trans.hide(fragment);
Log.d("Shown","Hide");
}
trans.commit();
}
Thanks very much guys!!!!!!!
I suggest make some changes to your code
I suppose that RECYCLER_FRAGMENT is a constant that contains a tag used to mark your fragment
RecyclerViewFragment recyclerViewFragment;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_maps);
if(savedInstanceState == null) {
recyclerViewFragment = new RecyclerViewFragment();
FragmentTransaction trans = getSupportFragmentManager().beginTransaction();
trans.add(R.id.recycle_view_container, recyclerViewFragment, RECYCLER_FRAGMENT);
trans.commit();
}else{
recyclerViewFragment = getSupportFragmentManager().findFragmentByTag(RECYCLER_FRAGMENT);
if(savedInstanceState.getString("vi").equals("hid")){
getSupportFragmentManager().beginTransaction().hide(recyclerViewFragment).commit();
}
}
Button showHideButton = (Button)findViewById(R.id.button_show_hide);
showHideButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
FragmentManager manager = getSupportFragmentManager();
if (fragment.isVisible()) {
manager.beginTransaction().hide(recyclerViewFragment).commit();
} else {
manager.beginTransaction().show(recyclerViewFragment).commit();
}
}
});
}
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
if(recyclerViewFragment.isVisible() == true){
outState.putString("vi","vis");
}else{
outState.putString("vi", "hid");
}
}
Instead of this:
trans.add(R.id.recycle_view_container, recyclerViewFragment, RECYCLER_FRAGMENT);
try this:
trans.replace(R.id.recycle_view_container, recyclerViewFragment, RECYCLER_FRAGMENT);
The name is misleading. Replace actually works as add also, if the first time. What is happening is that when your activity rotate, onCreate() is called again so you are adding the same fragment on top of the existing one
I have showing list of items which is displayed in grid and the data is coming from webservice . And when i click on the item it takes to the new screen but when coming from this screen to the screen having Grid the layout is recreated but i want to save the instance like activity so that the screen is not created again. what i can do to achieve this ?
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
mfragmentManager = getActivity().getSupportFragmentManager();
mhandler=new Handler(this);
mLvAllDeals = (GridView) mview.findViewById(R.id.xLvAllDeals);
mLvAllDeals.setCacheColorHint(0);
mLvAllDeals.setOnItemClickListener(this);
new MyDealsAsyncTask().execute();
}
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mAlDealName = new ArrayList<AllDealsData>();
}
public void onItemClick(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
if(arg0==mLvAllDeals)
{
position = arg2;
AllDealsData bean;
bean = mAlDealName.get(position);
Fragment frag = getFragmentManager().findFragmentById(R.id.inner_content2);
FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.setCustomAnimations(R.anim.slide_in_right, R.anim.slide_out_left, R.anim.slide_in_left, R.anim.slide_out_right);
if (!frag.getTag().equals("dealsinfo"))
{
Bundle args = new Bundle();
args.putString("dealid", bean.getId());
mdealinfo.setArguments(args);
ft.replace(R.id.inner_content2, mdealinfo, "dealsinfo");
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
ft.addToBackStack(null);
ft.commit();
}
}
}
}
When performing transactions in fragments, onCreateView() and onActvityCreated() will be called again but it wont be called onCreate() method. So maintain data in onCreate() method. If data is already loaded, set directly to adapter as well as loading webservices data manage it using some extra flag.
For more information on fragment life cycle refer this link
I am using ActionBarSherlock's action bar tabs in my application with each tab populated by a single fragment inside a SherlockActivity Tabs.
One of my Tabs contains a fragment, FragmentHome, with a list of news articles. When an article is selected, FragmentHome is replaced by another fragment, FragmentNews.
FragmentNews just contains a webview to load the selected article. The article is loaded fine. I override the onBackPressed in my activity so that FragmentHome is reattached and FragmentNews is removed.
While there are no errors, the webview inside FragmentHome is never removed from the view and overlaps with other fragments. (See screenshots)
Its weird because the same code works for a another SherlockFragment with ListView in it but is messed up when using a WebView. Here is the code to replace FragmentHome with FragmentNews initially:
#Override
public void onItemClick(AdapterView<?> parent, View view, int position,
long id) {
listNews.setItemChecked(position, true);
Bundle bundle = new Bundle();
bundle.putStringArray("NEWS",
new String[] {
mNews.newsFeed.get(position).getTitle(),
mNews.newsFeed.get(position).getLink()
.toExternalForm() });
FragmentTransaction ft = getSherlockActivity()
.getSupportFragmentManager().beginTransaction();
Fragment frag = SherlockFragment.instantiate(getSherlockActivity(),
FragmentNews.class.getName(), bundle);
ft.detach(getSherlockActivity().getSupportFragmentManager()
.findFragmentById(getId()));
ft.add(android.R.id.content, frag, Tabs.FRAG_NEWS);
ft.commit();
}
Overriden onBackPressed in Tabs:
#Override
public void onBackPressed() {
Fragment frag = getSupportFragmentManager().findFragmentByTag(
FRAG_DETAILS);
if (frag != null && frag.isVisible()) {
FragmentTransaction ft = getSupportFragmentManager()
.beginTransaction();
ft.remove(frag);
Fragment mFragment = getSupportFragmentManager().findFragmentByTag(
TAB_PORTFOLIO);
if (mFragment == null) {
mFragment = SherlockFragment.instantiate(this,
FragmentPortfolioList.class.getName(), null);
ft.add(android.R.id.content, mFragment, TAB_PORTFOLIO);
} else {
ft.attach(mFragment);
}
ft.commit();
} else {
frag = getSupportFragmentManager().findFragmentByTag(FRAG_NEWS);
if (frag != null && !frag.isDetached()) {
Log.e("onBackPressed", "for " + frag.getTag());
FragmentTransaction ft = getSupportFragmentManager()
.beginTransaction();
ft.remove(frag);
Fragment mFragment = getSupportFragmentManager()
.findFragmentByTag(TAB_HOME);
if (mFragment == null) {
mFragment = SherlockFragment.instantiate(this,
FragmentHome.class.getName(), null);
ft.add(android.R.id.content, mFragment, TAB_HOME);
} else {
ft.attach(mFragment);
}
ft.commit();
} else {
Log.e("onBackPressed", "inside else");
super.onBackPressed();
}
}
}
Snippet from FragmentNews
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
arr = getArguments().getStringArray("NEWS");
setRetainInstance(true);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_news, container);
newsView = (WebView) view.findViewById(R.id.news_WV_Brief);
newsView.getSettings()
.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);
newsView.getSettings().setGeolocationEnabled(false);
newsView.getSettings().setAllowFileAccess(false);
newsView.setWebViewClient(new WebViewClient() {
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
Log.e("override", url);
return true;
}
});
return super.onCreateView(inflater, container, savedInstanceState);
}
#Override
public void onResume() {
super.onResume();
newsView.loadUrl(arr[1]);
}
I have seen posts talking about FlashPlayer causing issues because of SurfaceView cutting a hole but I am just displaying simple webpages without any videos. Help highly appreciated.
I figured out the problem while going over the source code of WebViewFragment. I realized there isn't much going on other than pause/resume of webview.
I had two serious errors in my code:
I never returned the inflated view in onCreatView in
FragmentNews. I was returning super.onCreateView(inflater,
container, savedInstanceState).
I forgot to set attachToRoot to false in onCreateView when
inflating the XML layout - View view =
inflater.inflate(R.layout.fragment_news, container, **false**)
Therefore, the inflated view was just standing on its own without being attached to the fragment. When the fragment was replaced, it resulted in a mashed up display because the inflated layout containing the WebView was never removed. Unfortunately, this complied without errors.
I'm working on an application where in layout layout-small-portrait I want to launch different fragments contained in a single "container activity", named SingleActivity. I will handle this differnetly in layouts layout-land, layout-large etc. but that is unrelated to my problem.
I have an activity MainActivity which is, as the name indicates, the main activity (launcher) of my application. This will initially contain a ListFragment with different items for the user to press.
Based on the item that the user presses the SingleActivity will launch and its content will correspond to a specific Fragment related to this item. My problem starts here. When the user presses an item I have a reference to the corresponding fragment I want to be displayed in SingleFragment. Illustrated below:
String tag = myFragmentReference.getTag();
Intent i = new Intent(this, SingleActivity.class);
i.putExtra(SingleActivity.CONST_TAG, tag);
startActivity(i);
The activity launches successfully. In SingleActivity I have the following onCreate() method:
...
// Retrieve the fragment tag from the intent
String tag = getIntent().getStringExtra(CONST_TAG);
Fragment fragment = getSupportFragmentManager().findFragmentByTag(tag);
if(fragment == null) {
// always end up here, this is my problem.
}
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.add(R.id.fragmentContainer, fragment);
ft.commit();
...
I suspect that the fact that fragment is always null is because the fragment has not been inflated yet. If I am right what I need to do is define a fragment's tag before it is inflated, so that it can be found by findFragmentByTag(). Is that possible?
If anything is unclear please let me know.
I look forward to hearing some good ideas! If there are better or more clever ways to implement this I would love to hear your thoughts! Thanks :)
Since you are jumping to another activity, it will have its own Fragment BackStack and that fragment will not exist.
You will have to inflate the fragment in the new activity something along these lines:
String tag = intent.getStringExtra(CONST_TAG);
if (getSupportFragmentManager().findFragmentByTag(tag) == null) {
Fragment fragment = Fragment.instantiate(this, tag, extras);
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.add(R.id.fragmentContainer, fragment, tag);
ft.commit();
}
The tag string will need to have the package location of the fragment such as "com.android.myprojectname.myfragment"
First use SlidingMenu library: https://github.com/jfeinstein10/SlidingMenu
This will help you, and your app will be more cool, that´s the only way that I can help you make what you need so, here is the code:
Here is your MainActivity:
I´ll try to explain this sample code and you use for your need.
This is the ListFragment of your BehindContent (SlidingMenu):
public class ColorMenuFragment extends ListFragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.list, null);
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
String[] colors = getResources().getStringArray(R.array.color_names);
ArrayAdapter<String> colorAdapter = new ArrayAdapter<String>(getActivity(),
android.R.layout.simple_list_item_1, android.R.id.text1, colors);
setListAdapter(colorAdapter);
//This array is only to fill SlidingMenu with a Simple String Color.
//I used MergeAdapter from Commonsware to create a very nice SlidingMenu.
}
#Override
public void onListItemClick(ListView lv, View v, int position, long id) {
//This switch case is a listener to select wish item user have been selected, so it Call
//ColorFragment, you can change to Task1Fragment, Task2Fragment, Task3Fragment.
Fragment newContent = null;
switch (position) {
case 0:
newContent = new ColorFragment(R.color.red);
break;
case 1:
newContent = new ColorFragment(R.color.green);
break;
case 2:
newContent = new ColorFragment(R.color.blue);
break;
case 3:
newContent = new ColorFragment(android.R.color.white);
break;
case 4:
newContent = new ColorFragment(android.R.color.black);
break;
}
if (newContent != null)
switchFragment(newContent);
}
// the meat of switching the above fragment
private void switchFragment(Fragment fragment) {
if (getActivity() == null)
return;
if (getActivity() instanceof FragmentChangeActivity) {
FragmentChangeActivity fca = (FragmentChangeActivity) getActivity();
fca.switchContent(fragment);
} else if (getActivity() instanceof ResponsiveUIActivity) {
ResponsiveUIActivity ra = (ResponsiveUIActivity) getActivity();
ra.switchContent(fragment);
}
}
}
Here is your BaseActivity Class:
It dont have swipe, as I could understand, you don't need this.
public class FragmentChangeActivity extends BaseActivity {
private Fragment mContent;
public FragmentChangeActivity() {
super(R.string.changing_fragments);
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// set the Above View
if (savedInstanceState != null)
mContent = getSupportFragmentManager().getFragment(savedInstanceState, "mContent");
if (mContent == null)
mContent = new ColorFragment(R.color.red);
// set the Above View
//This will be the first AboveView
setContentView(R.layout.content_frame);
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.content_frame, mContent)
.commit();
// set the Behind View
//This is the SlidingMenu
setBehindContentView(R.layout.menu_frame);
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.menu_frame, new ColorMenuFragment())
.commit();
// customize the SlidingMenu
//This is opcional
getSlidingMenu().setTouchModeAbove(SlidingMenu.TOUCHMODE_FULLSCREEN);
}
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
getSupportFragmentManager().putFragment(outState, "mContent", mContent);
}
public void switchContent(Fragment fragment) {
// the meat of switching fragment
mContent = fragment;
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.content_frame, fragment)
.commit();
getSlidingMenu().showContent();
}
}
Ok, So If you want to change the ColorFragment to anything else, do this:
First, choice the item that you want to use:
case 0:
newContent = new ColorFragment(R.color.red);
break;
to:
case 0:
newContent = new ArrayListFragment();
break;
I have made just a arraylist, it is just a simple example, you can do a lot of thing, then you can read about Fragment to learn how to do different things.
public class ArrayListFragment extends ListFragment {
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
setListAdapter(new ArrayAdapter<String>(getActivity(),
android.R.layout.simple_list_item_1, Listnames.TITLES));
//Listnames is a class with String[] TITLES;
}
#Override
public void onListItemClick(ListView l, View v, int position, long id) {
Log.i("FragmentList2", "Item clicked: " + id);
String item = (String) getListAdapter().getItem(position);
Toast.makeText(getActivity(), item, Toast.LENGTH_LONG).show();
}
}
As you see, it can display a different fragment based on which item in the ListFragment (MainActivity) the user presses.
Well, if you misunderstood something, just tell me.