Select third page as default in TabLayout + ViewPager2 - java

I'm trying to make third tab as default, but below piece of code does not work:
public class MessagesFragment extends Fragment {
private FragmentsAdapter fragmentsAdapter;
private ViewPager2 viewPager;
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_messages, container, false);
}
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
fragmentsAdapter = new FragmentsAdapter(this);
viewPager = view.findViewById(R.id.pager);
viewPager.setAdapter(fragmentsAdapter);
viewPager.setCurrentItem(2);
TabLayout tabLayout = view.findViewById(R.id.tab_layout);
TabLayoutMediator.TabConfigurationStrategy tabConfigurationStrategy = new TabLayoutMediator.TabConfigurationStrategy() {
#Override
public void onConfigureTab(#NonNull TabLayout.Tab tab, int position) {
switch (position) {
case FragmentsAdapter.MESSAGES_NEW:
tab.setText(R.string.navMessagesNew);
break;
case FragmentsAdapter.MESSAGES_READ:
tab.setText(R.string.navMessagesRead);
break;
case FragmentsAdapter.SETTINGS:
tab.setText(R.string.navSettings);
break;
default:
throw new IllegalArgumentException("There is no fragment for that position = " + position);
}
}
};
new TabLayoutMediator(tabLayout, viewPager, tabConfigurationStrategy).attach();
}
}
I'm using TabLayout and ViewPager2. All the time 1st tab is default.
Can anyone advise how to make a 3rd of my tab as default?
Thanks

I had the same problem, it looks like there is a problem with viewPager2, if you set the tab as soon as you add attach the TabLayoutMediator (using setCurrentItem) it has a weird behavior, select the tab but doesn't update the page of the viewPager.
But if you do it later, in a button or after a couple of seconds calling the method viewPager.setCurrentItem(2) works fine, in may case since I have to know to which tab I have to go I asked to my viewModel to tell me with tab I have to display and when the observer is nofity the tab is changing correctly.
At least for me this works but I can see the first tab load (the deault tab 0) and then the tab change for the one I selected, since this is not viable for me, I change to use viewPager (instead of viewPager2) and set it to the tabLayout as usual, tab_layout.setupWithViewPager(view_pager).
With the regular viewPager works fine.

Your code is correct:
viewPager.setCurrentItem(2);
viewPager.setCurrentItem(2, false);
tabLayout.getTabAt(2).select();
tabLayout.selectTab(tabLayout.getTabAt(2));
these are the same things

viewPager.setCurrentItem(2)
Modify to
viewPager.t.post {
viewPager.setCurrentItem(2, true)
}

Related

Is there a way on the new navigation component on back pressed to restore the state on recyclerview

What I'm using:
MVP
Navigation Component
Single Activity
Fragments
RecyclerView
Realm
Problem:
I have a MainActivity that hosts all the fragments for the application flow.
There's one fragment that has a recyclerview, the more I scroll the more API requests are made. Whenever I press one item to navigate to a different fragment and press back to return to the list, the list only has a small piece of the previous loaded recyclerview, I want everything.
Is it possible to do this with the navigation component?
Can I prevent onCreateView being called whenever I press back?
I've tried to save the View in a static variable, have a onSaveInstance (but it never gets called)
I'm using navigation this way:
#Override
public void onClick(View view) {
Navigation.findNavController(view)
.navigate(R.id.action_tripsFragment_to_tripDetailsFragment, bundle);
}
MainFragment:
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater,
#Nullable ViewGroup container,
#Nullable Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.trips_fragment, container, false);
mPresenter = new TripsFragmentPresenterImpl(this);
ButterKnife.bind(this, v);
navController = Navigation.findNavController(getActivity(), R.id.nav_host_fragment);
mLinearLayoutManager = new LinearLayoutManager(getContext(), RecyclerView.VERTICAL, false);
mAdapter = new PaginationAdapter(getContext());
mAdapter.setOnItemClickListener(onItemClickListener);
mPresenter.setLoadingTripsProgressBar(true);
mRecyclerView.setLayoutManager(mLinearLayoutManager);
mRecyclerView.setItemAnimator(new DefaultItemAnimator());
mRecyclerView.setAdapter(mAdapter);
mRecyclerView.addOnScrollListener(new PaginationScrollListener(mLinearLayoutManager) {
#Override
protected void loadMoreItems() {
isLoading = true;
currentPage += 1;
loadNextPage();
}
#Override
public int getTotalPageCount() {
return currentPage;
}
#Override
public boolean isLoading() {
return isLoading;
}
});
loadFirstPage();
return v;
}
What I expect:
Whenever I press back from TripDetails to Trips I want to get my recyclerview as it was, with the previous loaded items at the correct position.
Current results:
Whenever I press back from TripDetails to Trips the recyclerview cuts the items to only the items that are visible, meaning that we'll lose items previous loaded in previous pages.
For future reference I've managed to solve the issue by creating a ModelView to keep the data independent of the lifecycle of the fragment

Actionbar.removealltabs() leaving behind the tabs space

Actionbar.removealltabs removes all my tabs. However, the remaining space used to hold the tabs are still there. As a result my actionbar looks like a very tall actionbar with a empty bottom half.
Using support actionbar with appcompatactivity.
I've tried calling Actionbar.removealltabs in both my fragment and Mainactivity... same result.
Tabs pre-destruction
Tabs Destroyed, leaving behind it's empty space
How it originally looked before adding tabs. This is my desired result
Here's my Fragment that created the tabs.
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mViewPager = view.findViewById(R.id.reddit_search_pager);
mActionBar = ((AppCompatActivity) getActivity()).getSupportActionBar();
setViewPager();
setTabs();
}
public void setTabs() {
mActionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
ActionBar.TabListener tabListener = new ActionBar.TabListener() {
#Override
public void onTabSelected(ActionBar.Tab tab, android.support.v4.app.FragmentTransaction ft) {
mViewPager.setCurrentItem(tab.getPosition());
}
#Override
public void onTabUnselected(ActionBar.Tab tab, android.support.v4.app.FragmentTransaction ft) {
}
#Override
public void onTabReselected(ActionBar.Tab tab, android.support.v4.app.FragmentTransaction ft) {
}
};
for (int i = 0; i < 3; i++) {
String tabTitle = "";
switch (i) {
case 0:
tabTitle = TAB_TITLE_SUBREDDITS;
break;
case 1:
tabTitle = TAB_TITLE_POSTS;
break;
case 2:
tabTitle = TAB_TITLE_USERS;
break;
}
mActionBar.addTab(
mActionBar.newTab()
.setText(tabTitle)
.setTabListener(tabListener));
}
}
Here's my primary MainActivity pre-existing modification's to the toolbar.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
NetworkingUtils myClass = new NetworkingUtils(this);
ActionBar actionbar = getSupportActionBar();
actionbar.setDisplayHomeAsUpEnabled(true);
actionbar.setHomeAsUpIndicator(R.drawable.ic_action_menu);
When I am working with ListViews I usually use adapters. With adapters you have to call yourAdapter.notifyDataSetChaged() every time you make a new change on your list so the list can update normally. Maybe that is what is happening to you so try to call some method like the one I showed above and it will maybe update and clear the empty space.
I fixed it by using another way to use tabs, the Tablayout as described in this video. https://www.youtube.com/watch?v=zQekzaAgIlQ. I had to make my own Toolbar instead of the default theme one.
I have no idea why Android is suggesting a deprecated and buggy way to implement tabs on their official website instead of this. In fact, the TabLayout examples provided in the Youtube video are all 404 not found.

onCreateView for tab being called whenever selecting adjacent tab?

I am having a weird issue where onCreateView is being called every time I navigate to an adjacent tab in my TabLayout. Here is my code:
news_feed.java:
private static TabLayout tabLayout;
#Override
public void onCreate(Bundle savedInstanceState) {
tabLayout = (TabLayout) findViewById(R.id.tab_layout);
tabLayout.addTab(tabLayout.newTab().setText("Public"));
tabLayout.addTab(tabLayout.newTab().setText("Friends"));
tabLayout.addTab(tabLayout.newTab().setText("My Tabs"));
tabLayout.setTabGravity(TabLayout.GRAVITY_FILL);
final ViewPager viewPager = (ViewPager) findViewById(R.id.pager);
final PagerAdapter adapter = new PagerAdapter
(getSupportFragmentManager(), tabLayout.getTabCount());
viewPager.setAdapter(adapter);
viewPager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(tabLayout));
tabLayout.setOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
#Override
public void onTabSelected(TabLayout.Tab tab) {
viewPager.setCurrentItem(tab.getPosition());
}
#Override
public void onTabUnselected(TabLayout.Tab tab) {
}
#Override
public void onTabReselected(TabLayout.Tab tab) {
}
});
}
PublicTab.java:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
System.out.println("Creating a new view");
fragmentView = inflater.inflate(R.layout.public_tab, container, false);
progressOverlay = fragmentView.findViewById(R.id.progress_overlay);
AndroidUtils.animateView(progressOverlay, View.VISIBLE, 0.9f, 200);
getPublicPosts(progressOverlay, fragmentView);
return fragmentView;
}
I have 3 other tabs that look exactly like PublicTab.java but with different layouts. I am not sure why onCreateView is being called so frequently. I thought in the lifecycle, onCreateView is only invoked for 2 reasons:
1. when we first initialize the tabs
2. if we come out from onStop or onPause method.
However, when switching between the tabs that are next to the actual tab, the println message is called like above and this is not what I want because I don't want to update the View of that fragment so frequently: only during the first 2 reasons above. Anyone know why this is happening? Any help would be appreciated! Thanks!
Use setoffScreenPageLimit
Android documentation:
public void setOffscreenPageLimit (int limit)
Set the number of pages that should be retained to either side of the
current page in the view hierarchy in an idle state. Pages beyond this
limit will be recreated from the adapter when needed
The default value is set to 1.
First and foremost of all, Viewpager default working property is this.
if you have around ten tabs and you are choice second tab.
viewpager will create tab2, tab1 and tab 3. This to provide smooth navigation between viewpagers. So whatever you do, your oncreate view will get called, because it will recreate the fragment everytime.
One thing you can do, is if you don't wan't to make a network call/or fetch data from online everytime a tab has been revisited after it's first creation.
you can do this.
save the data in TabLayout activity, use an interface create a setdata and getData method.
so whenever it reaches onCreateview. check if there is any data left in your get method, passing position to the method.
if exist display that data, else make a network call.

android RadioButton text is not updated when RadioButton is selected in RadioGroup, and Fragment is recreated from backstack

I have a radiogroup where I programmatically add radiobuttons. This is all done in onCreateView() method, so should be redone every time fragment is recreated from the backstack. The problem I have is when I programmatically preselect the radiobutton at the start by using RadioGroup.check(id), RadioButton text no longer updates on subsequent calls to onCreateView() method. Any ideas?
Project is using compatibility library (v4) if this makes any difference.
Here is the code:
public static String x; //(x changes every time)
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = nflater.inflate(R.layout.right, container, false);
RadioGroup rg = (RadioGroup) rootView.findViewById(R.id.radioGroup1);
rg.removeAllViews();
Log.d("Cild count", Integer.toString(rg.getChildCount()); // Always 0
RadioButton.rb = new RadioButton(getActivity());
// Always works first time. Does not work with 1 and 2 when fragment is recreated from back stack.
rb.setText(x);
rb.setId(99099); // 1 Atbitrary int for id
rg.addView(rb);
rg.check(99099); // 2 Selection of radiobutton
return rootView;
}
The problem was solved by not using a backstack, but instead manually handling onBackPressed.
Fragment fr = getSupportFragmentManager().getFragmentByTag("Fragment name");
#Override
public void onBackPressed() {
if (fr != null)
{
getSupportFragmentManager().beginTransaction().replace(container, new PreviousFragment, "Tag").commit();
}
else
{
super.onBackPressed();
}
}

Fragment replace doesn't work always

This is my situation:
I have several fragments added dynamicly to an FragmentStatePagerAdapter, this works fine. But now i want to be able to replace an fragment when I push on an button.
public class QuestionFragment extends UpperFragment {
...
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
setRetainInstance(true);
CustomViewPager.enabled = true;
View rootView = inflater.inflate(R.layout.question, container, false);
Button btn = ((Button) rootView.findViewById(R.id.bQuestion));
if (how == true) {
btn.setVisibility(View.VISIBLE);
btn.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// lijst afgaan met alle how en kijken welke id nodig is
for (int i = 0; i < XmlParserSax.howFragments.size(); i++) {
Fragment how = XmlParserSax.howFragments.get(i);
if (howId.equals(((UpperFragment) how).getIdNum())) {
FragmentTransaction transaction = getFragmentManager()
.beginTransaction();
transaction
.replace(R.id.flQuestion, how, "howFragment")
.setTransition(
FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
.addToBackStack(null).commit();
break;
}
}
}
});
} else {
btn.setVisibility(View.GONE);
}
return rootView;
}
So when i press the button the current layout (R.id.flQuestion) is replaced with the new fragment. This works, but the tricky part comes here:
When I slide to the next fragment and the slide to the fragment with the button it keeps working but if i slide 2 times to the next fragment (of the same type QuestionFragment) it does the functionallity of the new fragment but it doesn't show the new fragment.. So it seems that it can't replace the R.id.flQuestion because it is stored in memory maybe?
I need to be sure that the fragment is always replaced even if the next 2 fragments are of the same type and same layout (R.id.flQuestion)..
This is the class layout of the new frag
public class HowFragment extends UpperFragment {
..
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
setRetainInstance(true);
View rootView = inflater.inflate(R.layout.how, container, false);
//if back key pressed return to layout of Question
rootView.setFocusableInTouchMode(true); //this line is important
rootView.requestFocus();
rootView.setOnKeyListener( new View.OnKeyListener()
{
#Override
public boolean onKey( View v, int keyCode, KeyEvent event )
{
if( keyCode == KeyEvent.KEYCODE_BACK )
{
CustomViewPager.enabled = true;
return false;
}
return true;
}
} );
//don't allow pushing button again
rootView.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
// TODO Auto-generated method stub
return true;
}
});
return rootView;
}
Also important to tell: i'm using framelayouts for both the fragments (so no hard coded fragment tag in the xml)
To make it clear:
This happens when the next fragment is from different class, no problem
This happens when next fragments are from the same layout and class:
I'll have to test the code, but from what I see, easiest way to make this work is to grab new Fragment even if it is the same fragment class.
Fragment how = XmlParserSax.howFragments.get(i);
If this function is returning new instance of a fragment, it should work.
Hope that helps
Edit :
I'm pretty sure the activity can access the button after the fragments are created.
Otherwise you need a handler to pass the click to handle it in the adapter. I'm seeing the your list of fragments are static (Not recommended). Since you haven't added any codes for how you setup the pageradapter, I have no idea what list you are using, but you need to change out the item in that list. From the Activity where you initialized the pager, you can call the public function to replace the current pager item pager. (you can use ViewPager.getCurrentItem())
I haven't tested, so you might have to tweak and play around to perfect it.
Hope this helps.
Here is a sample :
public static class MyAdapter extends FragmentStatePagerAdapter {
ArrayList<Fragment> fragmentArray = new ArrayList<Fragment();
public MyAdapter(FragmentManager fm) {
super(fm);
}
//didn't put in the function to populate the list with fragments
public void replaceItem(Fragment newFrag, int pos){
fragmentArray.remove(pos);
fragmentArray.add(pos,newFrag);
notifyDataSetChanged();
}
#Override
public int getCount() {
return fragmentArray.size();
}
#Override
public Fragment getItem(int position) {
return fragmentArray.get(position);
}
}

Categories