Basicly I have very same code as described here :
In each of the fragments I have different asynctask, that just fetches data from website.
It looks like this :
TextView text;
String content;
View today;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
today = inflater.inflate(R.layout.today, container, false);
text = (TextView) today.findViewById(R.id.textView);
// ((TextView) today.findViewById(R.id.textView)).setText("Today");
new RetriveSiteData().execute("http://menza.lupajz.eu/?den=dnes");
content = (String) text.getText();
return today;
}
I am wondering if the data is always fetched, while swiping through fragments if the TabPagerAdapter class (on the website)
has this methode implemented the same way
#Override
public Fragment getItem(int i) {
switch (i) {
case 0:
//Fragement for Android Tab
return new Android();
case 1:
//Fragment for Ios Tab
return new Ios();
case 2:
//Fragment for Windows Tab
return new Windows();
}
return null;
}
In case if the data is always fetched and the asynctask is always executed how can I prevent that ? Maybe adding some onResume() methods to each fragment ?
Basically you have two options:
Execute the async tasks only once in the activity that contains the view pager, and then pass the data to your fragment. You can do it with some lazy loading as well.
Use the ViewPager.setOffscreenPageLimit(int) function to set the number of fragment to be retained in the view pager. Default value is set to 1, and thus the getItem() method is called every time you switch. I don't recommend going with this option though, especially when you have complex and memory consuming views.
Related
I currently got a ViewPager that has 3 tabs. Each tab is a fragment. I made a custom dialogFragment to login. I want that logindialog to open only when i go on my 3rd tab. In my onCreateView, i created a new object of my logindialog and show. My problem is, whenever i switch from my 1st tab to 2nd, the dialog also appears and i don't want that.
This is my viewpager adapter
public Fragment getItem(int position) {
switch (position) {
case 0:
return frag1;
case 1:
return frag2;
case 2:
return frag3;
default:
return null;
}
}
My 3rd Frag
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
root = inflater.inflate(R.layout.fragment_tab_annonce, container, false);
DialogFragment loginDialog = new DialogFragment();
loginDialog .show(getActivity().getSupportFragmentManager(), "customLogin");
return root;
}
ViewPager will create and retain "offscreen" pages as a performance optimization (with one offscreen page in either direction by default). What this means in practice is that when you are on Page 1, Page 2 has already been created off-screen. When you switch from Page 1 to Page 2, now Page 3 is created off-screen (and Page 1 is retained in case you want to switch back to it).
That means that onCreateView() is simply not the right place to show the error message.
You could perhaps create your own subclass of ViewPager.SimpleOnPageChangeListener and override onPageSelected() to show the dialog.
ViewPager.OnPageChangeListener listener = new ViewPager.SimpleOnPageChangeListener() {
#Override
public void onPageSelected(int position) {
if (position == 2) {
// show dialog
}
}
};
viewPager.addOnPageChangeListener(listener);
I'm trying to use Fragments in android.
So in the method "getItem()" into the class "TabAdapter", I have:
public Fragment getItem(int position) {
switch (position) {
case 0:
Browse tab1 = new Browse();
return tab1;
case 1:
Create tab2 = new Create();
return tab2;
default:
return null;
}
}
So, in class "Browse", I use inflate to put into TabLayout fragment, the layout that I want to use:
public class Browse extends Fragment {
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.activity_browse, container, false);
return rootView;
}
And in this class, a inner class that has the method "onCreate()"
public class BrowseInner extends AppCompatActivity{
DBManager db = new DBManager(this);
DBManager.DatabaseHelper dbh = new DBManager.DatabaseHelper(this);
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
System.out.println("TRYTOSTAMP");
setContentView(R.layout.activity_browse);
Cursor cursor = dbh.giveAllItemFromDB();
... //"and all the logics to interact with layout"...
}
But, in this way, I have only the layout in the Fragment TabLayout, as it is defined in .xml design, but I can't interact whit it (query not processed, so fields in the layout not fill, an event on button not called and so on...)
Where am I doing wrong?
Thanks in advance.
If I am correct you want to put the data that you get in BrowseInner into the fields of the two Fragments?
In general you are doing everything wrong.
You have no meaningful names like BrowseInnerActivity, BrowseFragment and CreateFragment
You can return like this. No need to assign.
public Fragment getItem(int position) {
switch (position) {
case 0:
return new Browse();
case 1:
return new Create();
default:
return null;
}
}
Why are you trying to inflate the same layout both in the Activity and the Fragment? Even if the views should be the same you need to create separate layouts and name them accordingly. You can not inflate in Fragment: R.layout.activity_browse
And the above is basic programming. Your design is not correct. The Activities and Fragments are "Views" in terms of MVP, MVVM and etc. They need to be passive. Only present some data and notify something, for example the ViewModel about some actions executed on them.
So in your case the Activity can only create the Fragments. You can create a ViewModel, which is shared between the two Fragments. The ViewModel can hold a reference to some Repository and update LiveData objects in itself. Then Fragments can observe the LiveData and do with the data what they need to do. There are pretty good design guidelines:
https://developer.android.com/jetpack/docs/guide
And here are some sample apps:
https://github.com/android/architecture-samples
And in general you should understand what is MVVM,ViewModel, LiveData and etc.
I have five fragments a user can switch between. One of these fragments loads a list of users from the server to populate the UI list on the fragment. I need the list information to persist if a user swipes to a different fragment and then swipes back to the original. I do not want the fragment to reload the users every time a user leaves the fragment and goes back.
I am looking at setRetainInsance(true) and was wondering if this is possible solution? What would be the best way for the fragment to retain the information without being created from scratch each time.
I am using this to switch between fragements -getSupportFragmentManager().beginTransaction().replace(R.id.searchLayout, ratingFragment).commit();
A Fragment is Just like any other object.
on Fragment transaction , the Fragment does not call OnCreate() method instead it starts from onCreateView , therefore , load your users and save it an instance variable and assign it in onCreate()
Example
class MyFragment extends Fragment{
List<users> userList;
void onCreate(){
userList = getUserList();}
//the list is loaded during Oncreate();
now imagine you have replaced the Fragment
now According to Andorid Framework , onCreate() is not Called again
instead onCreateView() is called
void onCreateView(){
//you can check whether instance Variable is initialised or not
if(userList != null) {
listview.setAdapter(new Myadapter(this,userList);
Replace the fragment by adding it to backstack.
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.addToBackStack(tag);
fragmentTransaction.replace(container, fragment, tag);
fragmentTransaction.commit();
Also create object of View and return it if it's not null.
private void View view ;
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
if (view != null)
return view;
view = inflater.inflate(R.layout.fragment_browse_recipe, container, false);
//initialize layout views
return view;
}
I'm using a ViewPager to show content fetched from a website with jsoup.
In the onCreateView of each page I call an AsyncTask that fetches the data and updates the View for each page.
The problem is that when the user slides the pages faster than usual the AsyncTask is called several times and, consequently, several useless requisitions are made with jsoup, since the only useful is the last.
I tried using setUserVisibleHint on the Fragment class and adding setOnPageChangeListener in the Activity class but these methods make me lose the ViewPager behaviour of preloading the next page and I don't want that.
Is there a way to know when the user stopped sliding and only call the AsynTask at that moment?
public class ScreenSlidePageFragment extends Fragment {
public static final String PAGE_NUMBER = "page";
private int mProblemNumber;
public static ScreenSlidePageFragment create(int pageNumber) {
ScreenSlidePageFragment fragment = new ScreenSlidePageFragment();
Bundle args = new Bundle();
args.putInt(PAGE_NUMBER, pageNumber);
fragment.setArguments(args);
return fragment;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mPageNumber = getArguments().getInt(PAGE_NUMBER);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
ViewGroup rootView = (ViewGroup) inflater.inflate(
R.layout.fragment_screen_slide_page, container, false);
new GetPageTask(url).execute();
return rootView;
}
}
I think the best way would be just to check whether the AsyncTask is running or not. Store a reference to your AsyncTask and then if user scrolls back to this page you can check its status using AsyncTask.Status (http://developer.android.com/reference/android/os/AsyncTask.Status.html).
Also, if you want to avoid starting new tasks when user scrolls too fast, you can use handler.postDelayed(yourRunnable, longMs). Each time user selects a page you can do something like this:
handler.removeCallbacks(yourRunnable);
handler.postDelayed(yourRunnable, longMs);
This way you will remove previous pending task and schedule a new one in longMs time. E.g. if you put 1000 ms then your tasks will start only in a second after user selected a page.
You might want to delay the request to fetch the content. For instance, if you are swiping quickly, waiting like half a second to load the content (instead of right away) would give the system a chance to breathe and check if the page is still visible.
Something like this:
handler.postDelayed(new Runnable(){
if (isVisible){
new GetPageTask(url).execute();
}
}, 500);
I am creating an app in which the user should be able add people for meetings.
The structure consists of several fragments managed in the same activity (list_people, person_detail, create_meeting).
I would like to reuse the fragment showing the list of people as a dialog in the create_meeting fragment. And add a person to a meeting by clicking on the person item.
When the list_people fragment is embedded in the view, a click on a person item replace the list_people fragment with a person_detail fragment. This behavior is already implemented with an interface for the main activity.
I am looking for a solution to change the behavior of the click listener whether the list_people fragment is displayed as an embedded fragment or as a dialog. Any ideas of how I could do that?
Any help would be greatly appreciated.Thanks.
Ok I have found a solution. It is to use a constructor (newInstance) for the fragment in which you can pass variables.
public class ListPeopleFragment extends Fragment {
public static ListPeopleFragment newInstance(boolean nested){
ListPeopleFragment f = new ListPeopleFragment();
Bundle args = new Bundle();
args.putBoolean("nested", nested);
f.setArguments(args);
return f;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){
View view = inflater.inflate(R.layout.fragment_list_people, container, false);
boolean nested = false;
Bundle arguments = getArguments();
if (arguments != null)
{
nested = getArguments().getBoolean("nested");
}
displayListViewPeople(view, nested);
return view;
}
}
The displayListViewPeople set the click listener depending on the value of nested.
You instantiate the fragment this way:
ListPeopleFragment nestedFrag = ListPeopleFragment.newInstance(true);