Progress bar loading before I select the Tab - android

Here is my code for Tab Initialization:
private void initTabs() {
serviceTabs.addTab(serviceTabs.newTab().setText("Request Appointment"));
serviceTabs.addTab(serviceTabs.newTab().setText("Online Booking"));
serviceTabs.addTab(serviceTabs.newTab().setText("Feedback"));
serviceViewpager.setOffscreenPageLimit(serviceTabs.getTabCount());
final ViewPageAdapter adapter = new ViewPageAdapter(getChildFragmentManager(), serviceTabs.getTabCount(),
mContext);
serviceViewpager.setAdapter(adapter);
serviceViewpager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(serviceTabs));
serviceTabs.setOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
#Override
public void onTabSelected(TabLayout.Tab tab) {
serviceViewpager.setCurrentItem(tab.getPosition());
}
#Override
public void onTabUnselected(TabLayout.Tab tab) {
Log.i("", "on tab unselected Service");
}
#Override
public void onTabReselected(TabLayout.Tab tab) {
Log.i("", "on tab reselected Service ");
}
});
}
private class ViewPageAdapter extends FragmentStatePagerAdapter {
int mNumOfTabs;
Context context;
ViewPageAdapter(FragmentManager fm, int numOfTabs, Context context) {
super(fm);
this.mNumOfTabs = numOfTabs;
this.context = context;
}
#Override
public Fragment getItem(int position) {
switch (position) {
case 0:
return new CustomizeFragment();
case 1:
Bundle url = new Bundle(); url.putString(Constants.WEB_URL,Constants.APPOINTMENT_ONLINE_BOOKING);
return WebViewFragment.createInstance(url);
case 2:
return new CustomizeFragment();
default:
return null;
}
}
#Override
public int getCount() {
return mNumOfTabs;
}
}
#Override
public void onDestroyView() {
super.onDestroyView();
unbinder.unbind();
}
And I'm having the progress bar loading inside the WebViewFragment as Follows:
public static WebViewFragment createInstance(Bundle urlArgs) {
WebViewFragment webView = new WebViewFragment();
webView.setArguments(urlArgs);
return webView;
}
#Override
public View onCreateView(LayoutInflater layoutInflater, ViewGroup container, Bundle saveInstanceState) {
rootView = layoutInflater.inflate(R.layout.load_web_view, container, false);
activity = getActivity();
url=getArguments().getString(Constants.WEB_URL);
mContext = activity.getApplicationContext();
serviceAppointmentView = (WebView) rootView.findViewById(R.id.webView);
loadData();
return rootView;
}
public void loadData()
{
serviceAppointmentView.clearView();
webSettings = serviceAppointmentView.getSettings();
webSettings.setJavaScriptEnabled(true);
webSettings.setSupportZoom(true);
webSettings.setBuiltInZoomControls(true);
webSettings.setLoadWithOverviewMode(true);
webSettings.setUseWideViewPort(true);
progress = new Progress(activity);
pDialog = progress.showProgress();
pDialog.setCanceledOnTouchOutside(true);
serviceAppointmentView.loadUrl(url);
serviceAppointmentView.setWebViewClient(new
DealerWebViewClient());
}
private class DealerWebViewClient extends WebViewClient {
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url)
{
view.loadUrl(url);
return true;
}
#Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
progress.dismissProgress(pDialog);
}
}
Im Using the custom progress bar for Online Booking Tab. The problem here is the Progress begins to load before I tap the Online Booking tab. Please tell me where i'm doing the mistake or how could be this solved...Thanks for patiently looking on my code and any suggestions will be helpful for me...!

ViewPage has DEFAULT_OFFSCREEN_PAGES value which is 1, and it is the min value, you can not set it to 0.
It means that when you are initing the viewpager and the fragments are added, when it does init fragment at postion 0 it does also init fragment at position 1.
There is a method, inside Fragment.class
setUserVisibleHint(boolean)
which is called when the fragment is visible to user(getting selected in viewPager).
You can ovveride it to achieve your loading progress start whenever you need.
Keep inmind that it is also fired before
onCreateView()
You can check if view is already inflated checking if any view is not null.

View pager will by default load atleast 1 on the right and one on the left tab of current tab. It is done so, mostly because there is a point when u slide viewpager, when certain area of both tabs is visible. For those smooth transitions preloading is required.
To prevent this you can use this method setUserVisibleHint().
Like below code:
#Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (isVisibleToUser) {
// load data here
}else{
}
}

I hope it will help you.
As viewpager loads adjacent fragments of current fragment , Its better to use progressbar in xml like this following while working with the viewpager inseted of progressdialog or something :
<ProgressBar
android:id="#+id/progressbar"
style="?android:attr/progressBarStyleLarge"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical|center_horizontal"
android:visibility="visible" />

By default Viewpager can load the three pages at a time.one is the current page and remaining are left and right side of the current page.So if you open your activity opposite sides of your fragments will also intializes.so overrride this method in your fragment below your onCreate()
#Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (isVisibleToUser) {
loaddata();
}else{
}
}
This will call whenever your fragment is visible to user

Related

load fragment in ViewPager only when its selected

I have a ViewPager with 3 tabs.
I'm loading big data in every fragments.
The app load every fragments but I want to load fragment only when its selected.
How can I do that? I have no idea.
I'm waiting for your help. Thank you
my code:
public class Fragments extends AppCompatActivity {
private TabLayout tabLayout;
private ViewPager viewPager;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.fragment_tabs);
viewPager = (ViewPager) findViewById(R.id.viewpager);
setupViewPager(viewPager);
tabLayout = (TabLayout) findViewById(R.id.tabs);
tabLayout.setupWithViewPager(viewPager);
}
private void setupViewPager(ViewPager viewPager) {
ViewPagerAdapter adapter = new ViewPagerAdapter(getSupportFragmentManager());
adapter.addFragment(new FirstFragment(), "1");
adapter.addFragment(new SecondFragment(), "2");
adapter.addFragment(new ThirdFragment(), "3");
viewPager.setAdapter(adapter);
}
class ViewPagerAdapter extends FragmentPagerAdapter {
private final List<Fragment> mFragmentList = new ArrayList<>();
private final List<String> mFragmentTitleList = new ArrayList<>();
public ViewPagerAdapter(FragmentManager manager) {
super(manager);
}
#Override
public Fragment getItem(int position) {
return mFragmentList.get(position);
}
#Override
public int getCount() {
return mFragmentList.size();
}
public void addFragment(Fragment fragment, String title) {
mFragmentList.add(fragment);
mFragmentTitleList.add(title);
}
#Override
public CharSequence getPageTitle(int position) {
return mFragmentTitleList.get(position);
}
}
}
If no one has given you a good answer I know this is 5 years later, but anyone reading this. I'm actually having the opposite problem. I need to load all tabs at the same time. What is happening to me now is only one tab is loading at a time. I need to use this adapter for viewpager2 with tablayout so that I can disable the viewpager swiping left to right for my specific usecase.
Here is my code:
WARNING CODE IS IN KOTLIN NOT JAVA
class RecipeTabAdapter internal constructor(fm: FragmentManager, lifecycle: Lifecycle) : FragmentStateAdapter(fm, lifecycle) {
val fragmentsList: ArrayList<Fragment> = arrayListOf()
fun addFragment(fragment: Fragment) {
fragmentsList.add(fragment)
}
override fun getItemCount(): Int {
return fragmentsList.size
}
override fun createFragment(position: Int): Fragment {
when (position) {
position -> fragmentsList[position]
position -> fragmentsList[position]
}
return fragmentsList[position]
}
}
Is it possible to disable the offscreen page limit?
No. It is already set to the minimum possible value: one page to each side of the viewed page. This is necessary to have the animation effects work -- you see parts of two fragments (original and new) at the same time.
In
OncreateView(){
//.... dont initialize your view here
if(isMenuVisible()) { // menu should be visible if current fragment is visible right now
setUserVisibleHint(true); // manually set value to true
}
}
in
oncreate(){
setHasOptionsMenu(true);
}
Finally there is method called setuservisiblehint() which is only called when the fragment is visible to the user this is the only method where you shud initialize all your views.
#Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if(isVisibleToUser && v != null) {
// initialize your view here and call you fetch_data_function()
}
Here's my solution and it works as expected. In all of your child fragments create a boolean variable:
private boolean loadFragmentExecuted = false;
in the child fragments create a generic method called loadFragment and move all of the logic you added in onCreateView to that method:
public void loadFragment()
{
if(!loadFragmentExecuted)
{
//Add your logic to manipulate the UI or load data etc...
loadFragmentExecuted = true;
}
}
in your pageview logic create the fragments dynamically like:
//add the fragment
String fragmentName = "com.something." + fragmentId;
//check if the class exists
try
{
Class myFragmentClass = Class.forName(fragmentName);
Fragment myFragment = (Fragment) myFragmentClass.newInstance();
mFragments.add(myFragment);
}
catch (ClassNotFoundException e)
{
e.printStackTrace();
}
catch (IllegalAccessException e)
{
e.printStackTrace();
}
catch (InstantiationException e)
{
e.printStackTrace();
}
then set your pager adapter and attach a tablayout with it:
//set our pager adapter that contains different fragments
mPagerAdapter = new BasePagerAdapter(mFragmentManager, mFragments);
//link the adapter to the viewpager
mViewPager.setAdapter(mPagerAdapter);
//cache fragments
int limit = (mPagerAdapter.getCount() > 0 ? mPagerAdapter.getCount() : 1);
mViewPager.setOffscreenPageLimit(limit);
//add the page listner to the viewPager and link it to the tabLayout
mViewPager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(mTabLayout));
//on tab selected select current viewpager item
mTabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener()
{
#Override
public void onTabSelected(TabLayout.Tab tab)
{
mViewPager.setCurrentItem(tab.getPosition());
//get fragment for the selected tab
Fragment f = mPagerAdapter.getItem(tab.getPosition());
//load the content of the fragment
try
{
Class c = f.getClass();
Method loadFragment = c.getMethod("loadFragment");
loadFragment.invoke(f);
}
catch (IllegalAccessException e){}
catch (InvocationTargetException e){}
catch (NoSuchMethodException e){}
}
#Override
public void onTabUnselected(TabLayout.Tab tab)
{
}
#Override
public void onTabReselected(TabLayout.Tab tab)
{
}
});
I have a solution that does not require subclassing, and it's easy to introduce into existing projects. It uses the child fragment mechanism.
The basic idea is to replace your content fragment with an almost empty fragment, and add your content fragment as a child fragment when it is really visible to the user
The gist of the code is like this
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
boolean hasFragment = getChildFragmentManager().findFragmentById(R.id.container) != null;
if (hasFragment) {
return;
}
if (getUserVisibleHint()) {
addFragment("onViewCreated");
}
}
#Override
public void onResume() {
super.onResume();
if (getUserVisibleHint()) {
addFragment("onResume");
}
}
#Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (isVisibleToUser && isResumed()) {
addFragment("setUserVisibleHint");
}
}
private void addFragment(String cause) {
if (getChildFragmentManager().findFragmentById(R.id.container) == null) {
getChildFragmentManager().beginTransaction()
.setCustomAnimations(android.R.animator.fade_in, android.R.animator.fade_out)
.add(R.id.container, createFragment()).commit();
}
}
Check out my full solution here, which also contains a progressbar, and the option to automatically start loading of background fragments with a specified delay: Lazy Load Fragment

Menu Button on ViewPager toasts wrong position

I am loading local htmls in webview using Viewpager. This works fine and the right page is loaded from the WebViewFragment into viewpager activity. Scroll or swipe works fine, but I added a menu button to get the title of the current webview, and to toast it. This menu button returns the webview title of the next 2 webview pages in the viewpager.
Reducing the setOfScreenLimit to 1 toasts the webview title of the next webview page in the viewpager. I cant setOfScreenLimit to 0. What is the most probable way to get the current item's webview title when the menu button is clicked. I need to get this and save to a DB.
Code:
ViewPager
ViewPager pager = (ViewPager) findViewById(R.id.pager);
TabsPagerAdapterEnglish pageAdapter = new TabsPagerAdapterEnglish(getSupportFragmentManager());
Bundle extras = getIntent().getExtras();
int value = 0;
if (extras != null) {
value = extras.getInt("keyHTML");
}
pager.setAdapter(pageAdapter);
pager.setCurrentItem(value);
pager.setOffscreenPageLimit(1);
Adapter
public class TabsPagerAdapterEnglish extends FragmentPagerAdapter {
private static int NUM_ITEMS = 3
public TabsPagerAdapterEnglish(FragmentManager fragmentManager) {
super(fragmentManager);
}
// Returns total number of pages
#Override
public int getCount() {
return NUM_ITEMS;
}
// Returns the fragment to display for that page
#Override
public Fragment getItem(int position) {
switch (position) {
case 0:
return WebFragmentEnglish.newInstance(0, "file:///android_asset/page1.html");
case 1:
return WebFragmentEnglish.newInstance(1, "file:///android_asset/page2.html");
case 2:
return WebFragmentEnglish.newInstance(2, "file:///android_asset/page3.html");
default:
return null;
}
}
#Override
public CharSequence getPageTitle(int position) {
}
}
WebFragment
public class WebFragmentEnglish extends Fragment {
// newInstance constructor for creating fragment with arguments
public static WebFragmentEnglish newInstance(int position, String url) {
WebFragmentEnglish fragmentFirst = new WebFragmentEnglish();
Bundle args = new Bundle();
args.putInt("page_position", position);
args.putString("keyHTML", url);
fragmentFirst.setArguments(args);
return fragmentFirst;
}
// Store instance variables based on arguments passed
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
Toolbar toolbar = (Toolbar) getActivity().findViewById(R.id.toolbar);
if (null != toolbar) {
toolbar.setNavigationIcon(R.drawable.abc_ic_ab_back_mtrl_am_alpha);
}
position = getArguments().getInt("page_position", 0);
url = getArguments().getString("keyHTML");
toolbar.setOnMenuItemClickListener(new Toolbar.OnMenuItemClickListener() {
#Override
public boolean onMenuItemClick(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
getActivity().finish();
case R.id.action_addtofav:
webView = (WebView) getActivity().findViewById(R.id.webView1);
htitle = webView.getTitle();
saveData();
Toast.makeText(getActivity(), htitle + " added to favorite",
Toast.LENGTH_SHORT).show();
return true;
}
return false;
}
});
}
// Inflate the view for the fragment based on layout XML
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.webview, container, false);
webView = (WebView) view.findViewById(R.id.webView1);
progressBar = (ProgressBar) view.findViewById(R.id.progressBar);
progressBar.setMax(100);
webView.setWebViewClient(new WebViewClientDemo());
webView.setWebChromeClient(new WebChromeClientDemo());
webView.setInitialScale(1);
webView.getSettings().setJavaScriptEnabled(true);
webView.getSettings().setLoadWithOverviewMode(true);
webView.getSettings().setUseWideViewPort(true);
webView.setScrollBarStyle(WebView.SCROLLBARS_OUTSIDE_OVERLAY);
webView.getSettings().setPluginState(WebSettings.PluginState.ON);
webView.getSettings().setLoadsImagesAutomatically(true);
webView.setScrollBarStyle(WebView.SCROLLBARS_OUTSIDE_OVERLAY);
webView.setScrollbarFadingEnabled(false);
webView.getSettings().setBuiltInZoomControls(true);
webView.setScrollBarStyle(View.SCROLLBARS_INSIDE_OVERLAY);
webView.setOnLongClickListener(new View.OnLongClickListener() {
#Override
public boolean onLongClick(View arg0) {
// TODO Auto-generated method stub
return true;
}
});
webView.setLongClickable(false);
webView.setWebChromeClient(new WebChromeClient() {
public void onProgressChanged(WebView view, int progress) {
progressBar.setProgress(progress);
}
});
webView.loadUrl(url);
return view;
}
ViewPager will load the current page and the next page(s) up to the offScreenLimit. To get the name of the current page you need to get the current position of the adapter.
It's hard to give more specific help without having code posted. But you can use the ViewPager getCurrentItem method to get the visible index. From there you can either compare it to the index of the page you're displaying (this works if the view pager is static and pages are known in advance), or you can use that index to get the Fragment from the PagerAdapter and call a method on it to save the name.
I was able to solve the problem with the ViewPager.
I removed the toolbar instance from the fragment to use the one in the viewpager's activity. Then in my options menu, you would notice I used
webView = (WebView) **getActivity().**findViewById(R.id.webView1);
I removed the this completely, since i have already defined it in the onCreateView, I think maybe its because of the getActivity field I added to locate the webview ID.
I was updating an old code, I'm glad I was able to solve this. Thank you #Chisko and #Ben

How to load fragment data only when its tab is clicked in PagerSlidingTabStrip

I am using PagerSlidingTabStrip in my project and am showing data in fragments. Its a great library which works great. Each Fragment consists of a scholar's lectures which are loaded from a web service. The code to access the web service is in onCreateView method of the Fragment. Currently all the fragment's data is loaded at the same time when they are added to ViewPager. I was wondering if there is any way of loading the Fragment's data only when a user clicks/swipes to a specific tab. Not everything at a time.
Current code is pretty standard, I took help from the provided example [android solution].1
Pager Adapter is as follows:
public class ScholarDetailPagerAdapter extends FragmentPagerAdapter {
private final String[] TITLES = { "Scholar Info", "Lectures"};
Scholar _scholar;
public ScholarDetailPagerAdapter(FragmentManager fm, Scholar scholar) {
super(fm);
_scholar = scholar;
}
#Override
public CharSequence getPageTitle(int position) {
return TITLES[position];
}
#Override
public Fragment getItem(int arg0) {
if(arg0 == 0)
return ScholarsInfoFragment.netInstance(_scholar.Name, _scholar.Information, _scholar.ThumbnailUrl);
else
return ScholarLecturesFragment.newInstance(_scholar.ScholarId);
}
#Override
public int getCount() {
return TITLES.length;
}
}
It returns TWO fragments, Info and Lectures. Lectures fragment calls a web service to get a scholar's lectures.
Activity code is:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// set the Above View
setContentView(R.layout.tabbed_view);
Scholar scholar = getScholar(getIntent());
_tabs = (PagerSlidingTabStrip) findViewById(R.id.tabs);
_pager = (ViewPager) findViewById(R.id.pager);
_adapter = new ScholarDetailPagerAdapter(getSupportFragmentManager(), scholar);
_pager.setAdapter(_adapter);
final int pageMargin = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 4, getResources()
.getDisplayMetrics());
_pager.setPageMargin(pageMargin);
_tabs.setViewPager(_pager);
}
I tried adding _tabs.setOnPageChangeListener(this); and ended up having onPageSelected(int arg0) in my activity as well. I thought It would be easy now. Then I modified public Fragment getItem(int arg0) in ScholarDetailPagerAdapter above and returned null instead of returning two fragments. But that raised an exception. I thought It would render empty views in pager .. but of course I was wrong.
To make sure Fragment loads data when its respective tab is clicked and is visible, I also have tried overriding its following events and wrote the data loading call in them:
onResume() Not worked. Still loads data when added in activity
Tried overriding onHiddenChanged
#Override
public void onHiddenChanged(boolean hidden) {
super.onHiddenChanged(hidden);
if(!hidden){
loadLectures();
}
}
This did not work too. No data loaded as if this event is not fired at all.
How do I do that? I simply want to load a fragment when user clicks on a tab.
Thanks in advance
You could override setUserVisibleHint event of the fragment to know if its visible to the user and then load your data. something like following:
boolean _areLecturesLoaded = false;
#Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (isVisibleToUser && !_areLecturesLoaded ) {
loadLectures();
_areLecturesLoaded = true;
}
}
private boolean isLoaded =false,isVisibleToUser;
#Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
this.isVisibleToUser=isVisibleToUser;
if(isVisibleToUser && isAdded() ){
loadData();
isLoaded =true;
}
}
#Override
public void onViewCreated(View view, #Nullable Bundle savedInstanceState) {
if(isVisibleToUser && (!isLoaded)){
loadData();
isLoaded=true;
}
}

Using backstack and back button in viewpager

I'm using a viewpager to swipe between fragments and would like the back button to navigate to the previously viewed fragment rather than ending the activity. Sorry if this is a duplicate of this question however I didn't find the answer very helpful. Obviously onBackPressed needs to be overridden, but I don't know how to get and display the correct fragment. I assume that I should use the fragmentmanager's backstack, but getSupportFragmentManager().getBackStackEntryCount() always returns 0. Do I need to manually add fragments to the backstack using FragmentTransaction.addToBackStack()? If so, where would I add this in my adapter?
Here is the code for my activity,
public class PagerActivity extends FragmentActivity {
ArrayList<Sale> sales;
MyAdapter mAdapter;
ViewPager mPager;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent it = getIntent();
this.sales = (ArrayList<Sale>) it.getExtras().get("sales");
int position = it.getExtras().getInt("position");
ActionBar actionBar = getActionBar();
actionBar.setDisplayHomeAsUpEnabled(true);
setContentView(R.layout.fragment_pager);
mAdapter = new MyAdapter(getSupportFragmentManager());
mPager = (ViewPager) findViewById(R.id.pager);
mPager.setAdapter(mAdapter);
mPager.setCurrentItem(position);
}
public class MyAdapter extends FragmentPagerAdapter {
public MyAdapter(FragmentManager fragmentManager) {
super(fragmentManager);
}
#Override
public int getCount() {
return sales.size();
}
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
super.destroyItem(container, position, object);
}
#Override
public Fragment getItem(int position) {
SalesThumbFragment frag = new SalesThumbFragment();
return frag.newInstance(sales.get(position));
}
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
finish();
return true;
}
return super.onOptionsItemSelected(item);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.sales_controller, menu);
return true;
}
#Override
public void onBackPressed() {
if(getSupportFragmentManager().getBackStackEntryCount() != 0) {
getSupportFragmentManager().popBackStack();
} else {
super.onBackPressed();
}
}
}
In new design support library, i use this
I have same issue and i follow this step
In the main activity where there are 3 fragment in viewpager i create stack
and push and pop data.
//private Stack<Integer> stackkk; ==> As i get edit suggestion
private Stack<Integer> stackkk = new Stack<>(); // Edited
private ViewPager mPager;
private int tabPosition = 0;
mTabLayout.setupWithViewPager(mPager);
mPager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(mTabLayout));
mTabLayout.setOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
#Override
public void onTabSelected(TabLayout.Tab tab) {
tabPosition = tab.getPosition();
mPager.setCurrentItem(tab.getPosition());
if (stackkk.empty())
stackkk.push(0);
if (stackkk.contains(tabPosition)) {
stackkk.remove(stackkk.indexOf(tabPosition));
stackkk.push(tabPosition);
} else {
stackkk.push(tabPosition);
}
}
#Override
public void onTabUnselected(TabLayout.Tab tab) {
tabPositionUnselected = tab.getPosition();
}
#Override
public void onTabReselected(TabLayout.Tab tab) {
}
});
}
and in the onBackPressed in activity,
#Override
public void onBackPressed() {
if (stackkk.size() > 1) {
stackkk.pop();
mPager.setCurrentItem(stackkk.lastElement());
} else {
}
}
Use this code in Fragment Activity class. Don't forget to add return true;
public boolean onKeyDown(int keyCode, KeyEvent event) {
// TODO Auto-generated method stub
if ((keyCode == KeyEvent.KEYCODE_BACK)) {
mViewPager.setCurrentItem(viewPageSelected - 1);
return true;
}
return super.onKeyDown(keyCode, event);
}
I had a similar problem, this is how I solved it. I think you can adapt the code to your problem, if I understood what you problem is. I had a ViewPager with 6 fragments and wanted to keep track of the page history and to be able to use the back button to navigate backwards in the history. I create a java.util.Stack<Integer> object, add fragment numbers to it (except when you use the back button, see below), and override onBackPressed() to make it pop the last viewed fragment instead of using the back stack, when my history stack is not empty.
You want to avoid pushing elements on the Stack when you press the back button, otherwise you will get stuck between two fragments if you keep using the back button, instead of eventually exiting.
My code:
MyAdapter mAdapter;
ViewPager mPager;
Stack<Integer> pageHistory;
int currentPage;
boolean saveToHistory;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mAdapter = new MyAdapter(getSupportFragmentManager());
mPager = (ViewPager)findViewById(R.id.container);
mPager.setAdapter(mAdapter);
mPager.setOffscreenPageLimit(5);
pageHistory = new Stack<Integer>();
mPager.setOnPageChangeListener(new OnPageChangeListener() {
#Override
public void onPageSelected(int arg0) {
if(saveToHistory)
pageHistory.push(Integer.valueOf(currentPage));
}
#Override
public void onPageScrolled(int arg0, float arg1, int arg2) {
}
#Override
public void onPageScrollStateChanged(int arg0) {
}
});
saveToHistory = true;
}
#Override
public void onBackPressed() {
if(pageHistory.empty())
super.onBackPressed();
else {
saveToHistory = false;
mPager.setCurrentItem(pageHistory.pop().intValue());
saveToHistory = true;
}
};
If you use a field to keep track of the index of the previous page using mPager.getCurrentItem() after each time the user navigates to a new fragment, then in the onBackPressed() method, you should be able to call mPager.setCurrentItem(previousPage)
Or, if the user can only page in order, then you don't need a field at all, and you could just do mPager.setCurrentItem(mPager.getCurrentItem()-1)
I've made custom ViewPager and implement stack functionality in it.
public class CustomViewPager extends ViewPager {
private Stack<Integer> stack = new Stack<>();
#Override
public void setCurrentItem(int item, boolean smoothScroll) {
stack.push(getCurrentItem());
super.setCurrentItem(item, smoothScroll);
}
public int popFromBackStack(boolean smoothScroll) {
if (stack.size()>0) {
super.setCurrentItem(stack.pop(), smoothScroll);
return getCurrentItem();
} else return -1;
}

ViewPager.setOffscreenPageLimit(0) doesn't work as expected

The fragments I use in my ViewPager instance are quite resource intensive, so I'd only like to load one at a time. When I try the following:
mViewPager.setOffscreenPageLimit(0);
mViewPager.setAdapter(mPagerAdapter);
My FragmentStatePagerAdapter.getItem(int position) override function is called 3 times, which is what happens when I call mViewPager.setOffscreenPageLimit(1). I would expect it to only be called once, because I specified 0 offscreen pages.
I believe I'm calling everything correctly, because if I call mViewPager.setOffscreenPageLimit(2), FragmentStatePagerAdapter.getItem(int position) is called 5 times as I would expect.
Does ViewPager require a minimum of 1 offscreen pages, or am I doing something wrong here?
The best way that I found was setUserVisibleHint
add this to your fragment
#Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (isVisibleToUser) {
// load data here
}else{
// fragment is no longer visible
}
}
Does ViewPager require a minimum of 1 offscreen pages
Yes. If I am reading the source code correctly, you should be getting a warning about this in LogCat, something like:
Requested offscreen page limit 0 too small; defaulting to 1
You can try like this :
public abstract class LazyFragment extends Fragment {
protected boolean isVisible;
/**
* 在这里实现Fragment数据的缓加载.
* #param isVisibleToUser
*/
#Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if(getUserVisibleHint()) {
isVisible = true;
onVisible();
} else {
isVisible = false;
onInvisible();
}
}
protected void onVisible(){
lazyLoad();
}
protected abstract void lazyLoad();
protected void onInvisible(){}
protected abstract void lazyLoad();
protected void onInvisible(){}
First Add
boolean isFragmentLoaded = false;
than
#Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (isVisibleToUser && !isFragmentLoaded) {
//Load Your Data Here like.... new GetContacts().execute();
isFragmentLoaded = true;
}
else{
}
}
this may be old thread but this seems to work for me. Override this function :
#Override
public void setMenuVisibility(boolean menuVisible) {
super.setMenuVisibility(menuVisible);
if ( menuVisible ) {
/**
* Load your stuffs here.
*/
} else {
/**
* Fragment not currently Visible.
*/
}
}
happy codings...
ViewPager is default to load the next page(Fragment) which you can't change by setOffscreenPageLimit(0). But you can do something to hack.
You can implement onPageSelected function in Activity containing the ViewPager. In the next Fragment(which you don't want to load), you write a function let's say showViewContent() where you put in all resource consuming init code and do nothing before onResume() method. Then call showViewContent() function inside onPageSelected. Hope this will help.
in my case i wanted to start some animations in views, but with setUserVisibleHint got some issues ...
my solution is :
1/ addOnPageChangeListener for your adapter :
mViewPager.addOnPageChangeListener(this);
2/ implement OnPageChangeListener :
public class PagesFragment extends Fragment implements ViewPager.OnPageChangeListener
3/ override the 3 methodes :
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels)
{
}
#Override
public void onPageSelected(int position)
{
}
#Override
public void onPageScrollStateChanged(int state)
{
}
4/ declare and initialize this variable on your class
private static int mTabState = 1;
notice : i have three fragments in my adapter, and use mTabState for setCurrentItem and current position of adapter which recognize which fragment is show to user in time ...
5/ in onPageSelected method add this codes :
if (mTabState == 0 || position == 0)
{
Intent intent = new Intent("animation");
intent.putExtra("current_position", position);
LocalBroadcastManager.getInstance(mContext).sendBroadcast(intent);
}
if previous page or current page is page 0(fragment in position 0) then do this stuff
6/ now in your fragment class (fragment in position 0 of adapter), you must create broadcast receiver and register it in onResume method and unregister it onPause methos :
BroadcastReceiver broadcastReceiver = new BroadcastReceiver()
{
#Override
public void onReceive(Context context, Intent intent)
{
if (Objects.equals(intent.getAction(), "animation"))
{
int currentPosition = intent.getIntExtra("current_position", 0);
if (currentPosition == 0)
{
startAnimation();
setViewsVisible();
} else
{
setViewsInvisible();
}
}
}
};
#Override
public void onResume()
{
super.onResume();
LocalBroadcastManager.getInstance(mContext).registerReceiver(broadcastReceiver, new IntentFilter("animation"));
}
#Override
public void onPause()
{
super.onPause();
LocalBroadcastManager.getInstance(mContext).unregisterReceiver(broadcastReceiver);
}
summary : i have Fragment Pager Adapter witch shows Three Fragments in it, I want show some Animations on Views in Fragment in Position 0 of Adapter, For this I use BroadcastReceiver. When Fragment is Picked I start the Animation method and shows the Views to User, When Fragment is not Showing to User I try to Invisible Views...
View Pager With only one Page :
This is February 2021: I have able to add only one page with viewPager. The approach is with ViewPager, FragmentPagerAdapter, Tablayout, and a fragment. In my case, I can populate many Pages with many tabs, or only one Page with one Tab. When one tab and one page, on swipe left or right, I can manage to change the chapter of my document (which I want to show next). And when many pages and many tabs, I can change the entire book of documents.
In main Activity Oncreate: (Here is my working code approach, I have changed nothing here from my working code):
if(getIntent()!=null){
if(getIntent().getStringExtra("ONLY_TAFHEEM")!=null)
sectionsPagerAdapter = new SectionsPagerAdapter(this, getSupportFragmentManager(), suraName, suraId, ayatId, true);
else
sectionsPagerAdapter = new SectionsPagerAdapter(this, getSupportFragmentManager(), suraName, suraId, ayatId, false);
}else {
sectionsPagerAdapter = new SectionsPagerAdapter(this, getSupportFragmentManager(), suraName, suraId, ayatId, false);
}
final ViewPager viewPager = findViewById(R.id.view_pager);
viewPager.setAdapter(sectionsPagerAdapter);
tabsLayout = findViewById(R.id.tabs);
tabsLayout.animate();
tabsLayout.setupWithViewPager(viewPager);
In Adapter :
#NonNull
#Override
public Fragment getItem(int position) {
// getItem is called to instantiate the fragment for the given page.
// Return a PlaceholderFragment (defined as a static inner class below).
return PlaceholderFragment.sendData(mContext, postion, suraName, suraId, ayahId, ARABIC_AYAH, BENGALI_AYAH, actualDbNames[position], tafsirDisplayNames[position]);
}
#Nullable
#Override
public CharSequence getPageTitle(int position) {
return tafsirDisplayNames[position];
}
#Override
public int getCount() {
// this is the tricky part // Show pages according to array length. // this may only one // this is the tricky part :
return tafsirDisplayNames.length;
}
And at last the fragments public constructor :
public static PlaceholderFragment sendData(Context mContext, int tabIndex, String suraName, String suraId, String ayahNumber, String arabicAyah, String bengaliAyah, String actualDbName, String displayDbName) {
Log.i("PlaceHolder", "Tafhim sendData: " + bengaliAyah);
PlaceholderFragment fragment = new PlaceholderFragment();
Bundle bundle = new Bundle();
mContext_ = mContext;
BENGALI_AYAH = bengaliAyah;
_DISPLAY_DB_NAME = displayDbName;
bundle.putInt(ARG_SECTION_NUMBER, tabIndex);
bundle.putString(SURA_NAME, suraName);
bundle.putString(SURA_ID, suraId);
bundle.putString(AYAH_NUMBER, ayahNumber);
bundle.putString(ARABIC_AYAH, arabicAyah);
bundle.putString(ACTUAL_DB_NAME, actualDbName);
bundle.putString(DISPLAY_DB_NAME, displayDbName);
fragment.setArguments(bundle);
return fragment;
}
That's all, just passing the array (of Tab Labels) to the adapter, (it may only one element, in case, for one page), with my need, I can populate one page or more page, and according to this it populate one tab or many tabs : in the above code the array is: tafsirDisplayNames. I can also create the array manually in adapter, when the adapter first called, Or, recreate the array with +-elements, on recreate the MainActivity.
Please Try This Code for resolve issue of refreshing view in Viewpager....
/* DO NOT FORGET! The ViewPager requires at least “1” minimum OffscreenPageLimit */
int limit = (mAdapter.getCount() > 1 ? mAdapter.getCount() - 1 : 1);
mViewPager.setOffscreenPageLimit(limit);
for the "instantiateItem" function, just prepare the fragment, but don't load the heavy content.
Use "onPageChangeListener" , so that each time you go to a specific page, you load its heavy content and show it.
I kind of have the same problem. I found some useful code on this site and transform it.
The min int for mViewPager.setOffscreenPageLimit(...); is 1, so even if you change it to 0 you will still have 2 pages loaded.
First thing to do is to create a static int we will call maxPageCount and override FragmentStatePagerAdapter method getCount() to return maxPageCount:
#Override
public int getCount() {
return maxPageCount;
}
Create then a static method accessible from any where in the program that will allow you to change this maxCount:
public static void addPage(){
maxPageCount++; //or maxPageCount = fragmentPosition+2
mFragmentStatePagerAdapter.notifyDataSetChanged(); //notifyDataSetChanged is important here.
}
Now initialize maxPageCount to 1. When ever you want you can add another page.
In my case when I needed the user to treat the current page first before generated the other. He do it and then, without problem can swipe to the next page.
Hope it help someone.
Use This
// create boolean for fetching data
private boolean isViewShown = false;
#Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (getView() != null) {
isViewShown = true;
// fetchdata() contains logic to show data when page is selected mostly asynctask to fill the data
fetchData();
} else {
isViewShown = false;
}
}
// step 1: add BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT FragmentPagerAdapter contractor
public class BottomTabViewPager extends FragmentPagerAdapter {
private final List<Fragment> mFragmentList = new ArrayList<>();
private final List<String> mFragmentTitleList = new ArrayList<>();
public BottomTabViewPager(FragmentManager manager) {
super(manager, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
}
#Override
public Fragment getItem(int position) {
return mFragmentList.get(position);
}
#Override
public int getCount() {
return mFragmentList.size();
}
public void addFragment(Fragment fragment, String title) {
mFragmentList.add(fragment);
mFragmentTitleList.add(title);
}
public void addTabs(String title) {
mFragmentTitleList.add(title);
}
#Override
public CharSequence getPageTitle(int position) {
return mFragmentTitleList.get(position);
// return null;
}
}
// step 2: You can try like this :
public class MyFragment extends Fragment {
public MyFragment() {
// Required empty public constructor
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_ui, container, false);
return view;
}
#Override
public void onResume() {
super.onResume();
/**
* Load your stuffs here.
*/
}
}

Categories

Resources