I just started learning android.Hope this isn't silly question.Here it is
My FragmentAdapter has a List of Fragments,Say 3 Fragments 1,2,3.
I have set this FragmentAdapter for my ViewPager using myViewPager.setAdapter(myFragmentAdapter);
So this application kicks off by displaying view of Fragment 1.How do I start the Application by displaying Fragment 2's View first.
You can use setCurrentItem(index) to switch your current page.
In this case, you would want to do:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// ... init code ...
myViewPager.setCurrentItem(1);
}
See the ViewPager docs for more information. You may wish to use the variation of this method which allows you specify where there should be a smooth scroll, and set it as false, otherwise the user may see the animation the moment the app opens and it could be confusing. For example,
myViewPager.setCurrentItem(1, false);
Related
I am absolutely in love with these new components Android is introducing. So, I am building a standard mobile application with solely one activity using the Navigation components and Architecture components such as a View Model as I am performing a lot of communication with my data that I stored in room.
In one of my bottom navigation tabs, I have a list that is loaded from all my data in room. So far, I have set up my RecyclerView and my adapter in the OnCreateView() (only function used in this fragment) of this list fragment and every thing shows successfully.
The problem is that every time (especially more at first view) the fragment takes a solid 10 seconds to display all the data (which is normal considering there is a lot of it).
My question: Is there a way the adapter and and RecylcerView of this specific fragment could be setup (and load all my data) in the OnCreate() of my sole activity? So that when I view the fragment for the first time, everything pops up right away.
Also, how would I go about using OnPause() of the list fragment so that when I am on another tab, the list fragment doesn't get destroyed and when we go back on it, it displays right away?
Fetch all data from room inside onCreate() method of fragment. The onDestroyView() method calls everytime you moves away from the fragment.
To prevent recreation of views inside fragment store view in a variable.
Example:
class YourFragment extends Fragment{
View rootView;
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
//fetch data from room
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState){
if(rootView == null)
rootView = inflater.inflate(R.layout.your_fragment_layout, container, false);
return rootView;
}
}
I'm a beginner in Android, so I apologize for the mistakes and I'd appreciate any constructive criticism.
I'm writing a basic application with a ListView of images, and when the user clicks on an item in the list, I want to display that image in a ViewPager, where the user can swipe back and forth to browse the whole list of images. Afterwards when the user presses the back button, I want to switch back to the ListView.
I manage the business logic in the MainActivity, which uses MainActivityFragment for the ListView and ImageHolderFragment for ViewPager.
The simplified code so far is as follows:
#Override
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mListItems = new ArrayList<>();
mListItemAdapter = new ListItemAdapter(this, R.layout.list_item, R.id.list_item_name, mListItems);
mListView = (ListView) findViewById(R.id.list_view_content);
mListView.setAdapter(mListItemAdapter);
mDeletedListItems = new ArrayList<>();
mViewPager = (ViewPager) getLayoutInflater().inflate(R.layout.image_display, null, true);
mImageAdapter = new ImageAdapter(getSupportFragmentManager(), mListItems);
mViewPager.setAdapter(mImageAdapter);
mViewPager.setOffscreenPageLimit(3);
mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
mViewPager.setCurrentItem(position);
setContentView(mViewPager); // TODO: this is very wrong!
}
});
loadImages();
noContentText = (TextView) findViewById(R.id.no_content_text);
if (mListItems.isEmpty()) {
noContentText.setText(R.string.no_images);
} else {
mImageAdapter.notifyDataSetChanged();
}
}
Although this does work to some extent, meaning that it manages to display the ViewPager when an item in the list is clicked, there are two things about it ringing the alarm bells:
I've read that calling setContentView() for the second time in the same class is pretty much a sin. Nobody explained me why.
The back button doesn't work in this case. When it's pressed, the application is terminated instead of going back to the list view. I believe this is connected to the first point.
I would appreciate any help, explanations if my idea is completely wrong, and if my case is hopeless, I'd like to see a successful combination of ListView and ViewPager with transitions between each other.
Your activity already has R.layout.activity_main set as content view, which rightly displays the list view - that's what the responsibility of this activity is as you defined it. If we want to change what's shown on the screen, we should use a different instance of a building block (activity or fragment) to display the view pager images.
To say the least, imagine if you wanted to change the view to a third piece of functionality or UI, or a fourth... it would be a nightmare to maintain, extend and test as you're not separating functionality into manageable units. Fields that are needed in one view are mixed with those needed in another, your class file would grow larger and larger as each view brings its click listeners, callbacks, etc., you'd also have to override the back button so it does what you want - it's just not how the Android framework was designed to help you. And what if you wanted to re-use UI components in different contexts whilst tapping in to the framework's activity lifecycle callbacks? That's why fragments were introduced.
In your case, the list view could continue to run in your MainActivity and in your click listener, onItemClick you could start a new activity that will hold a viewPager:
Intent i = new Intent(MainActivity.this, MyLargePhotoActivityPager.class);
i.putExtra(KEY_POSITION, position);
// pass the data too
startActivityForResult(i, REQUEST_CODE);
Notice how you could pass the position to this activity as an int extra, in order for that second activity to nicely set the viewPager to the position that the user clicked on. I'll let you discover how to build the second activity and put the ViewPager there. You also get back button functionality assuming your launch modes are set accordingly, if needed. One thing to note is that when you do come back to the list View, you'd probably want to scroll to the position from the view pager, which is why you could supply that back as a result via a request code. The returned position can be supplied back to the list view.
Alternatively, you could use the same activity but have two fragments (see the link further above) and have an equivalent outcome. In fact, one of your fragments could store the list view, and the second fragment could be a fullscreen DialogFragment that stores a viewPager, like a photo gallery (some details here).
Hope this helps.
I've read that calling setContentView() for the second time in the
same class is pretty much a sin. Nobody explained me why.
Well, you kind of get an idea as to why.
When you use setContentView() to display another 'screen' you do no have a proper back stack.
You also keep references to Views (like mListView) that are not visible anymore and are therefore kind of 'useless' after you setContentView() for the second time.
Also keep in mind orientation changes or your app going to the background - you'll have to keep track of the state that your Activity was in which is way more complicated than it has to be if you have one Activity that does two different things.
You won't be arrested for doing things like you do right now, but it's just harder to debug and keep bug free.
I'd suggest using two different Activities for the two different things that you want to do, or use one Activity and two Fragments, swapping them back and forth.
If you insist on having it all in one Activity you need to override onBackPressed() (called when the user presses the back button) and restore the first state of your Activity (setContentView() again, pretty much starting all over).
I am using ViewPager in my app. In each fragment there is a toolbar. On a single tap on the image the the toolbar is animated to the top out of the screen. But I have to notify all the remaining fragments to do the same thing. So that when the user scrolls to the next fragment he doesn't see the toolbar.
I tried adding setUserVisibleHint(), but it did not work as it was called only when the fragment was completely visible, thus showing the toolbar exiting to the user.
Then I tried it in onResume and setting pager.offscreenpagelimit=1, it worked fine for the fragment next to next but did not work for the next fragment.
Thanks!!
First Notify your activity from fragment using:
In Fragment on Animation End:
((YourActivity)getActivity()).hideToolbar();
In Activity:
public void hideToolbar() {
// Redraw view pager without toolbar (notify your adapter create pager without toolbar)
}
Well, why don't you get your toolbar out of your fragments and just create one in activity and change its state on page change (definitely it will not slide but may
You have 2 problems to face.
How to call existing fragments to hide/show their toolbars.
How to create another fragments with hidden toolbar.
First problem can be easly done by using Otto event library found here. Just paste this code in your viewpager fragments:
Bus bus = new Bus();
#Override
protected void onResume() {
super.onResume();
bus.register(this);
}
#Override
protected void onPause() {
super.onPause();
bus.unregister(this);
}
#Subscribe
public void onToolbarEvent(ToolbarEvent event) {
//do your toolbar logic
}
Then in your onClick event on image just put (of course creating bus object before)
bus.post(new ToolbarEvent());
ToolbarEvent can be just empty class. If you read about Otto events you will understand how it works.
Another problem is how to know that the toolbar should be hidden/shown, when viewpager instantiates new fragments? Simply add a boolean flag in your shared prefferences, so every time fragment is created, it can check if it can show toolbar or not e.g. in onViewCreated() method. The example how to use shared prefferences can be found here
Hope I helped a little bit.
In my activity I have several fullscreen fragments, each of them downloads some data from web (using an async task) and shows them to the user. The fragments are showed one at a time.
To be more specific, each of the fragment readings some urls from a sqlite database, and fetch the content before showing them in a list, if that matters. The data loading tasks can be done in the OnCreate() function.
I would like to preload all the fragment (at least starting the downloading), when I show a splash screen. Pretty much like a viewpager preload its fragments.
I am wondering how to achieve this? I tried initialize/create all the fragments in the OnCreate() function of my activity, hoping the OnCreate() of fragments could be called earlier, but the OnCreate() and OnCreateView() function of the fragments are not called until a fragment is about to show to the user.
It sounds like you need to separate your model (the data which is downloaded) from your view (the fragments). One way to do this is to start the downloading AsyncTasks in your activity, rather than starting them in each fragment. Then when the fragments are eventually displayed they can show the data which has been downloaded (or a spinner or some other indication that the download process is still executing).
Fragment's onActivityCreated(Bundle) tells the fragment that its activity has completed its own Activity.onCreate().
So your solution to this problem is initialize or create or do your stuffs which you want to preload before fragments are created, inside your Fragment's onActivityCreated(Bundle)
see documents for fragment's lifecyle
The earliest pace you can start loading is either in a static singleton or in the Application Class
What I end up doing is the following, (1) add all the fragments into the container. So they (and their view) will be created and initialized. (2) hide those not in use and only show the one I would like the user to see. (3) use FragmentTrasaction.show()/FragmentTrasaction.hide() to manipulate the visibility instead of FragmentTrasaction.add() or FragmentTrasaction.replace().
If you following this approach, be warn that all the fragments will be cached in memory. But the benefit is the switch between fragment will be fast and efficient.
I was facing the same problem and then I used this method, suppose we are having an EditText in the fragment, then we can use codes like this
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
//this method allows you to input or instantiate fragments before showing this to an activity conidering id is "editTextEditProfileFirstName"
EditText firstName = (EditText) getActivity().findViewById(R.id.editTextEditProfileFirstName);
firstName.setText("This is my first name", TextView.BufferType.EDITABLE);
super.onViewCreated(view, savedInstanceState);
}
I'm currently dealing with an issue with Android & It's Re-Creation Cycle on screen rotation:
I have one single Activity and lots of Fragments (Support-V4) within.
For example, the Login it's on a Single Activity with a Fragment, when the logs-in then the App changes it's navigation behavior and uses multiple fragments, I did this, because passing data between Fragment A to Fragment B it's way much easier than passing data Between an Activity A to an Activity B.
So My issue it's presented when I rotate the device, on my first approach, the initial fragment was loaded, but what would happen, if the user it's on Page 15 and it rotates it's device, it would return to Fragment 1 and give a very bad user-experience. I set all my fragments to retain their instance and added this on the MainActivity on Create:
#Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.main_layout);
initBackStackManager();
initControllers();
mayDownloadData();
setTitle();
if(savedInstanceState == null){
addAreaFragment();
}
}
Now, the first fragment is not loaded after screen orientation change, but If I try to make a fragment transaction, it says Can not perform FragmentTransaction.commit() after onSaveInstanceState(), is there a way to handle this? Or Do I really really need to use multiple Activities with a Fragment embedded within?
Thank you very much!
EDITED
I forgot to add that this happens only on a specific Fragment... For example I have the following fragment flow:
AreaFragment -> WaiterSelectionFragment -> WaiterOptionsFragment.
If I'm in the AreaFragment and I rotate the device I can still add/replace fragments and nothing happens, no error it's being thrown. If I'm on the WaiterSelectionFragment no error happens too. BUT, If I'm on the WaiterOptionsFragment the error it's being thrown. The WaiterSelectionFragment has the following structure:
LinearLayout
FragmentTabHost
Inside the FragmentTabHost there are some fragments, and that's where the error it's happening. You might wonder Why FragmentTabHost? easy, the Customer wants that App to show the TabBar, If I use Native Android Tabs the Tabs get rearranged to the ActionBar when on
Landscape position.
EDIT 2
I've used the method provided by #AJ Macdonald, but no luck so far.
I have my Current Fragment being saved at onSaveInstanceState(Bundle) method and restore my fragment on onRestoreInstanceState(Bundle) method on the Android Activity, I recover my back button and the current Fragment but when I get to the third Fragment the error still occurs. I'm using a ViewPager that holds 4 Fragments, Will this be causing the Issue? Only on this section of the App Happens. I've 4 (main workflow) fragments, on the First, Second and Third Fragment no error it's being presented, only on the ViewPager part.
Give each of your fragments a unique tag.
In your activity's onSaveInstanceState, store the current fragment. (This will probably be easiest to do if you keep a variable that automatically updates every time the fragment changes.)
In your activity's onCreate or onRestoreInstanceState, pull the tag out of the saved bundle and start a new fragment of that type.
public static final int FRAGMENT_A = 0;
public static final int FRAGMENT_B = 1;
private int currentFragment;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//other stuff
if(savedInstanceState == null){
addAreaFragment();
currentFragment = FRAGMENT_A;
}else{
currentFragment = savedInstanceState.getInt("currentFragment");
switch(currentFragment){
case FRAGMENT_A:
addAreaFragment();
break;
case FRAGMENT_B:
addFragmentB();
}
}
}
// when you switch fragment A for fragment B:
currentFragment = FRAGMENT_B;
#Override
public void onSaveInstanceState(Bundle savedInstanceState) {
savedInstanceState.putInt("currentFragment", currentFragment);
super.onSaveInstanceState(savedInstanceState);
}
A suggestion to try is to use FragmentTransaction.commitAllowingStateLoss() in place of FragmentTransaction.commit(). That should stop the Exception from being thrown, but the downside is if you rotate the device again the most recent state of the UI may not return. That is a suggestion given that I am not sure of the effect of using FragmentTabHost, if it has any effect at all.