Difference between these 2 swiping tabs implementations? - android

I have a working code for having 3 tabs, with 3 fragments that you can either horizontally swipe between to change, or you can click on the tab to go to the requested fragment.
However, I wanted to do some tweaks to my design, and I went over to the Android Developer's website: Creating swipe views with tabs
, and it looks completely different than mine. Now I wonder if I should use the code in the developer's website, or keep my own.
Right now I am using ViewPager + TabLayout.
So simple, yet working.
This also makes me wonder, how come this simple code works, but in the official documentation there's a long story. It just makes me think that if I keep my code in the current design, I might encounter some big problems in the future.
Or - if it works - then it works?
MainActivity.java:
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Find the view pager that will allow the user to swipe between fragments
ViewPager viewPager = (ViewPager) findViewById(R.id.viewpager);
// Create an adapter that knows which fragment should be shown on each page
TabsAdapter adapter = new TabsAdapter(this, getSupportFragmentManager());
// Set the adapter onto the view pager
viewPager.setAdapter(adapter);
// Find the tab layout that shows the tabs
TabLayout tabLayout = (TabLayout) findViewById(R.id.tabs);
// Connect the tab layout with the view pager. This will
// 1. Update the tab layout when the view pager is swiped
// 2. Update the view pager when a tab is selected
// 3. Set the tab layout's tab names with the view pager's adapter's titles
// by calling onPageTitle()
tabLayout.setupWithViewPager(viewPager);
}
}
TabsAdapter.java:
public class TabsAdapter extends FragmentPagerAdapter {
/** Context of the app */
private Context mContext;
public TabsAdapter(Context context, FragmentManager fm) {
super(fm);
mContext = context;
}
/**
* Return the {#link Fragment} that should be displayed for the given page number.
*/
#Override
public Fragment getItem(int position) {
if (position == 0) {
return new NbFragment();
} else if (position == 1) {
return new LksFragment();
} else {
return new ChtFragment();
}
}
/**
* Return the total number of pages.
*/
#Override
public int getCount() {
return 3;
}
#Override
public CharSequence getPageTitle(int position) {
if (position == 0) {
return mContext.getString(R.string.category_nb);
} else if (position == 1) {
return mContext.getString(R.string.category_lks);
} else {
return mContext.getString(R.string.category_cht);
}
}
activity_main.xml:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/primary_color"
android:orientation="vertical"
tools:context="com.example.barebones.barebones.MainActivity">
<android.support.design.widget.TabLayout
android:id="#+id/tabs"
style="#style/CategoryTab"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<android.support.v4.view.ViewPager
android:id="#+id/viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
And you can see it is completely different than the one on their website. How could it be? I would love to get some clarification, because as a beginner, I feel lost in this sea of information. Which method should I use in order to have swiping tabs that represent different fragments?

I'm lost what you're trying to solve here. Are you perhaps trying to make it so that when you swipe horizontally, respectively changing fragments, your tab layout is updated as well? If so. I have a snippet down below, which you may have to adjust based on your variable names.
protected void initializeNavigationTransition() {
viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
#Override
public void onPageScrolled(int i, float v, int i1) {
}
#Override
public void onPageSelected(int i) {
tabs_bottom_nav.getTabAt(i).select();
}
#Override
public void onPageScrollStateChanged(int i) {
}
});
tabs_bottom_nav.addOnTabSelectedListener(new TabLayout.BaseOnTabSelectedListener() {
#Override
public void onTabSelected(TabLayout.Tab tab) {
viewPager.setCurrentItem(tab.getPosition());
updateTabIconOnSelect(current_tab, tab.getPosition());
current_tab = tab.getPosition();
}
#Override
public void onTabUnselected(TabLayout.Tab tab) {
}
#Override
public void onTabReselected(TabLayout.Tab tab) {
}
});
}
I'll have an additional note that it is possible for fragments to refresh, meaning it will be reinitialized, which might be something you do not want, especially if you're displaying data unto these fragments, which you are fetching from your API or over the network, after swiping a couple of times to different fragments if you have probably at least 4 fragments within the view pager. Hence you would need to prevent the view pager from refreshing the fragments by using the code below
viewPager.setOffscreenPageLimit(4); // 4 is the number of fragments within your view pager
viewPager.setAdapter(sectionsPagerAdapter);
I believe the cause of fragments refreshing is caused by the viewpager trying to save memory

Related

Take focus away from tabs in ViewPager/TabLayout

I am using ViewPager/TabLayout on Android TV, and I have 2 questions.
1) Currently, if I press DPAD_UP, the focus goes to the tabs, but the page does not actually change to the one corresponding to the focused tab until DPAD_CENTER is pressed. So, there is a difference between focused (shown as a grey box around the tab text) and selected (shown as an underline). I am wondering if it is possible to have whatever focused to be selected?
2) I have 3 tabs in my ViewPager where the fragment in tab1 needs to have focus in order to display things properly. When I switch from tab3 to tab1, the focus is not given to the fragment but stays with the tab. This causes my fragment to not display properly. I am wondering if there is a way to take focus away from the tab? I dug into the source code of ViewPager and TabLayout, and I know the focus is with TabView of TabLayout, but no matter what I try, I cannot take away the focus state of TabView (i.e. the grey box surrounding the tab text does not go away until I use DPAD_DOWN to "go down" to the fragment).
Tried
((ViewGroup) tabLayout.getChildAt(0)).getChildAt(desiredPosition).clearFocus() and it doesn't help.
I don't want to use viewPager.setOffscreenPageLimit(2);
// This is my viewpager activity class
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_view_pager);
ViewPager viewPager = (ViewPager) findViewById(R.id.viewPager);
adapterViewPager = new MyPagerAdapter(getFragmentManager());
viewPager.setAdapter(adapterViewPager);
// Give the TabLayout the ViewPager
TabLayout tabLayout = (TabLayout) findViewById(R.id.sliding_tabs);
tabLayout.setupWithViewPager(viewPager);
tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
#Override
public void onTabSelected(TabLayout.Tab tab) {
// Tried to clear focus of tab here, didn't help
}
#Override
public void onTabUnselected(TabLayout.Tab tab) {
}
#Override
public void onTabReselected(TabLayout.Tab tab) {
}
});
}
public static class MyPagerAdapter extends FragmentPagerAdapter {
private static int NUM_ITEMS = 3;
public MyPagerAdapter(FragmentManager fragmentManager) {
super(fragmentManager);
}
// Returns total number of pages.
#Override
public int getCount() {
return NUM_ITEMS;
}
// Returns the fragment to display for a particular page.
#Override
public Fragment getItem(int position) {
switch (position) {
case 0:
return new OneFragment; // This is the fragment that needs focus
case 1:
return new TwoFragment();
case 2:
return new ThreeFragment();
default:
return null;
}
}
// Returns the page title for the top indicator
#Override
public CharSequence getPageTitle(int position) {
if (position == 0) {
return "Fragment 1";
} else if (position == 1) {
return "Fragment 2";
} else {
return "Fragment 3";
}
}
}
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<android.support.design.widget.TabLayout
android:id="#+id/sliding_tabs"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:tabMode="fixed"
android:theme="#style/AppThemeTab" />
<android.support.v4.view.ViewPager
android:id="#+id/viewPager"
android:layout_width="match_parent"
android:layout_height="match_parent">
</android.support.v4.view.ViewPager>
</RelativeLayout>
Any help is appreciated, thank you!
One liner solution :) . To suppress focus on Tablayout children views use the code fragment below assuming the tab is scrollable - i.e. tabs selection is scrolling by digipad:
tabLayout.setTabMode(TabLayout.MODE_SCROLLABLE);
tabLayout.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);

Viewpager dot indicator doesn't display

I am trying to add dot indicators to my view pager, I tried different types and none work, for some reason it doesn't appear on the fragment. It doesn't crash... Just doesn't appear.
I am trying to use this library
View pager XML file:
<LinearLayout android:layout_height="match_parent"
android:layout_width="match_parent"
android:orientation="vertical"
xmlns:android="http://schemas.android.com/apk/res/android">
<android.support.v4.view.ViewPager
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/pager"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.pixelcan.inkpageindicator.InkPageIndicator
android:id="#+id/indicator"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:dotDiameter="8dp"
app:dotGap="8dp"
app:animationDuration="320"
app:pageIndicatorColor="#a3a0a0"
app:currentPageIndicatorColor="#000000" />
</android.support.v4.view.ViewPager>
</LinearLayout>
the fragment activity file:
public class HighScoreScreenSlide extends FragmentActivity {
/**
* The number of pages (wizard steps) to show in this demo.
*/
private static final int NUM_PAGES = 3;
/**
* The pager widget, which handles animation and allows swiping horizontally to access previous
* and next wizard steps.
*/
private ViewPager mPager;
private int countDownInd;
Bundle bundle;
/**
* The pager adapter, which provides the pages to the view pager widget.
*/
private PagerAdapter mPagerAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.high_score_view_pager);
countDownInd = getIntent().getIntExtra("gameType", 0);
// Instantiate a ViewPager and a PagerAdapter.
mPager = (ViewPager) findViewById(R.id.pager);
mPagerAdapter = new ScreenSlidePagerAdapter(getSupportFragmentManager());
mPager.setAdapter(mPagerAdapter);
mPager.setCurrentItem(countDownInd);
InkPageIndicator inkPageIndicator = (InkPageIndicator) findViewById(R.id.indicator);
inkPageIndicator.setViewPager(mPager);
}
#Override
public void onBackPressed() {
if (mPager.getCurrentItem() == 0) {
// If the user is currently looking at the first step, allow the system to handle the
// Back button. This calls finish() on this activity and pops the back stack.
super.onBackPressed();
} else {
// Otherwise, select the previous step.
mPager.setCurrentItem(mPager.getCurrentItem() - 1);
}
}
/**
* A simple pager adapter that represents 5 ScreenSlidePageFragment objects, in
* sequence.
*/
private class ScreenSlidePagerAdapter extends FragmentStatePagerAdapter {
public ScreenSlidePagerAdapter( FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) { //use position
HighScoreFragment fragment = new HighScoreFragment();
bundle=new Bundle();
bundle.putInt("gameType",position);
fragment.setArguments(bundle);
return fragment;
}
#Override
public int getCount() {
return NUM_PAGES;
}
}
}
Also this library didn't work.
If there is more code needed to understand I'll be happy to provide it
Thanks!
Try to put InkPageIndicator view not inside ViewPager but on the same level with it, like shown in the sample. In this particular case, InkPageIndicator and ViewPager should be the children of LinearLayout. If you want indicator dots to be on top of the view pager, consider replacing LinearLayout with FrameLayout.

For Multiple Tabs Single Fragment not working getItem called twice

Sir, I have to use single fragment for multiple tabs. I have implemented it using FragmentStatePagerAdapter but after debugging I have got that some of the tabs displays data but not correct. This is because getItem called twice please help me as early as possible.
I have passed arguments from getItem. it passes arguments two times as getItem calls twice. thus it overrides data of arguments. I have 6 tabs but only 1, 3 and 5 tab displays data but data is of 2, 4 and 6th tab and 2, 4 and 6th tab not showing anything on the screen.
not working any solution from previous questions.
ViewPager Code
public class ViewPagerAdapter extends FragmentStatePagerAdapter {
int TAB_COUNT=5;
Context mContext;
public ViewPagerAdapter(FragmentManager fm,Context context) {
super(fm);
mContext=context;
}
#Override
public Fragment getItem(int position) {
NewsFragment fragment = new NewsFragment();
Bundle args = new Bundle();
args.putString("title", String.valueOf(getPageTitle(position)));
args.putString("data", "data");
fragment.setArguments(args);
return fragment;
}
#Override
public CharSequence getPageTitle(int position) {
return mContext.getResources().getStringArray(R.array.tabs)[position];
}
#Override
public int getCount() {
return TAB_COUNT;
}
}
Listeners to change tab :
#Override
public void onTabSelected(TabLayout.Tab tab) {
viewPager.setCurrentItem(tab.getPosition());
}
#Override
public void onPageSelected(int position) {
TabLayout.Tab tab = tabLayout.getTabAt(position);
if (tab != null) {
tab.select();
}
}
Adapter setting
private void setUpTab() {
viewPagerAdapter = new ViewPagerAdapter(getSupportFragmentManager(), this);
for (int i = 0; i < viewPagerAdapter.getCount(); i++) {
tabLayout.addTab(
tabLayout.newTab()
.setText(viewPagerAdapter.getPageTitle(i)));
}
tabLayout.setTabGravity(TabLayout.GRAVITY_FILL);
viewPager.setAdapter(viewPagerAdapter);
viewPager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(tabLayout));
viewPager.addOnPageChangeListener(this);
tabLayout.addOnTabSelectedListener(this);
}
getItem gets called every time the adapter creates a new page for the first time. The reason you see it being called twice is that the viewPager prepares the next page in advance, so if you enter page 1, page 2 is already being created too for faster paging when you swipe right.
To help you further please show us the code for NewsFragment.
Also, you might consider using FragmentPagerAdapter instead of FragmentStatePagerAdapter because you seem to have a fixed number of tabs (5 in your example) and according to the documentation that is the use case for FragmentPagerAdapter.
Documentation from the page I linked above:
This version of the pager is best for use when there are a handful of typically more static fragments to be paged through, such as a set of tabs.

Changing viewPager swipe direction

I'm using TabLayout and viewPager with an option to swipe the viewPager to the left or right in order to navigate between pages.
My problem is, that my application is RTL based (right-to-left) and the swipe direction is reversed.
I'm trying to change the swipe direction from the default to the reversed version.
I've been searching alot on the web and couldn't find a way how to do it.
I'm using android.support.v4.view.ViewPager;
and this is how I initialize my TabLayout with the viewPager:
// View Page Adapter
final ViewPager viewPager = (ViewPager) findViewById(R.id.pager);
final PagerAdapter adapter = new PagerAdapter
(getSupportFragmentManager(), tabLayout.getTabCount());
//View Page Adapter Configuration
viewPager.setAdapter(adapter);
viewPager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(tabLayout));
viewPager.setOffscreenPageLimit(3);
tabLayout.setOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
#Override
public void onTabSelected(TabLayout.Tab tab) {
viewPager.setCurrentItem(tab.getPosition());
}
Summary: Currently, When I'm swiping the viewPager to the left, it shows the next page.
in RTL, when you swipe the viewPager to the right, it shows the next page.
It might be hard to understand, but here it is.
Swiping right shows the next page
while I need swiping right to show the previous page.
It is the very simple techniqe to change the swipe direction of the Viewpager.
There are two simple step which have to follow you,
1. Change the rotation of the View pager,
viewpager.setRotationY(180);
2. Then again change the direction of the each item view in adapter,
itemView.setRotationY(180);
This is 100% worked for me.
I faced the same problem month ago but I think I found a great solution.
Change your TabLayout direction to ltr in xml:
<android.support.design.widget.TabLayout
android:id="#+id/tl_activity_main"
android:layout_below="#id/toolbar_activity_main"
android:layoutDirection="ltr"
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="#style/DisionTabLayout" />
Create custom adapter for ViewPager and override methods getItem(int position) and getPageTitle(int position):
#Override
public Fragment getItem(int position) {
if (mIsRtlOrientation && mTabs != null && mTabs.length > 0) {
return mTabs[mTabs.length - position - 1].getFragment();
} else {
return mTabs[position].getFragment();
}
}
#Override
public int getCount() {
return mTabs.length;
}
#Override
public CharSequence getPageTitle(int position) {
if (mIsRtlOrientation && mTabs != null && mTabs.length > 0) {
return mTabs[mTabs.length - position - 1].getTitle();
} else {
return mTabs[position].getTitle();
}
}
Set the adapter to ViewPager and then apply this ViewPager to TabLayout.
private void setAdapters() {
// initialize adapter
mTabsAdapter = new TabsAdapter(getSupportFragmentManager(), mTabs, true);
// set adapter to ViewPager
vp.setAdapter(mTabsAdapter);
// set ViewPager to TabLayout
tl.setupWithViewPager(vp);
// if RTL orientation, go to last item
vp.setCurrentItem(vp.getAdapter().getCount() - 1, false);
}
i know it's late but this is a smart question. as #CommonsWare said it's the matter of reversing the order of items in getItem() method in page adapter. but this isn't enought. as you reverse the order in getitem() method you should also NOT reverse the order in getPageTitle() method. something like this:
public static class AppSectionsPagerAdapter extends FragmentStatePagerAdapter {
public AppSectionsPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
switch (position) {
case 0:
return new page1();
case 1:
return new page2();
case 2:
return new page3();
}
}
#Override
public CharSequence getPageTitle(int position) {
switch (position) {
case 0:
return "page 3 title";
case 1:
return "page 2 title";
case 2:
return "page 1 title";
}
}
#Override
public int getCount() {
return 3;
}
}
now comes the tricky part. you should use this simple method which converts zero based index order reversed using max index and current index.
public int calculateReverse(int maxIndex,int currentIndex){
return maxIndex-currentIndex;
}
than you should use this method in onTabSelected() like this: be aware you should use your own max index:
#Override
public void onTabSelected(ActionBar.Tab tab, FragmentTransaction ft) {
mViewPager.setCurrentItem(calculateReverse(2,tab.getPosition()));
}
the second part is to change page listener on page adapter. once again you should use your own max index number:
mViewPager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
#Override
public void onPageSelected(int position) {
actionBar.setSelectedNavigationItem(calculateReverse(2,position)); // reversing the tab selection
}
});
i tried to be as descriptive as i can. hope this helps :)
If you are using viewpager2 with fragments. you can use.
<androidx.viewpager2.widget.ViewPager2
android:id="#+id/pager"
android:layout_width="0dp"
android:layout_height="0dp"
**android:orientation="vertical"**
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
I hope this helps somebody.
Yout can use ViewPager2, It support right to left direction. to make it possible:
Add android:supportsRtl="true" in app Manifest
Make viewpager2's parent viewgroup layout direction ltr (android:layoutDirection="ltr")
Now set viewpager2's layoutDirection ltr (android:layoutDirection="rtl")
also set adapter's layout to layoutDirection ltr (android:layoutDirection="ltr")
So I found that the easiest way for this to work for ViewPager (1)
is just to invert the array of items, and then before displaying set the current item of the pager at [array size] position.

How to scroll tablayout programmatically - Android

I have created 30 scrollable tabs using tablayout.
So first three tabs are visible on screen and rest of them are invisible which can be scroll using swipe gesture.
The problem is when I am selecting last tab programmatically but it is not get visible (tab layout not get scrolled to last tab).
How can I make tablayout to scroll to last tab?
I found the solution.
First I had found the width of tablayout and scroll it's x position to width and than called the select() method of last tab.
And it works fine.
Below is the code.
mTabLayout.setScrollX(mTabLayout.getWidth());
mTabLayout.getTabAt(lastTabIndex).select();
Updated:
If above is not working you can use the below code as well, it is also working fine.
new Handler().postDelayed(
new Runnable() {
#Override public void run() {
mTabLayout.getTabAt(TAB_NUMBER).select();
}
}, 100);
write this method in your custom tablayout (Your own layout which extends tablayout). So, in future you can use this method whenever you need instad of code duplication
public void selectTabAt(int tabIndex) {
if (tabIndex >= 0 && tabIndex < getTabCount() && getSelectedTabPosition() != tabIndex) {
final Tab currentTab = getTabAt(tabIndex);
if (currentTab != null) {
this.post(new Runnable() {
#Override
public void run() {
currentTab.select();
}
});
}
}
}
If you don't want yo use CustomLayout. you can just do this
final Tab currentTab = mTabLayout.getTabAt(tabIndex);
if(currentTab != null){
mTabLayout.post(new Runnable() {
#Override
public void run() {
currentTab.select();
}
});
}
I found this solution for me:
TabLayout tabLayout = activity.getTabLayout();
tabLayout.setSmoothScrollingEnabled(true);
tabLayout.setScrollPosition(targetChannelPosition, 0f, true);
Also, if you receive this error: "Only the original thread that created a view hierarchy can touch its views.", you can use this code, in order to run on Ui thread:
// find a way to get the activity containing the tab layout
TabLayout tabLayout = activity.getTabLayout();
activity.runOnUiThread(new Runnable()
{
#Override
public void run()
{
TabLayout.Tab tab = tabLayout.getTabAt(targetChannelPosition);
tab.select();
}
});
Are you calling tab.select() before the TabLayout and its children have actually been measured and drawn? If so, your TabLayout won't animate to the selection with tab.select() (or Kayvan N's suggestion of scrollTo()). Using a Handler will probably work, but that's not an ideal solution.
Provided the layout hasn't been laid out yet, a ViewTreeObserver will allow you to move to your selected tab after the layout process is finished.
private void scrollToTabAfterLayout(final int tabIndex) {
if (getView() != null) {
final ViewTreeObserver observer = mTabLayout.getViewTreeObserver();
if (observer.isAlive()) {
observer.dispatchOnGlobalLayout(); // In case a previous call is waiting when this call is made
observer.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
observer.removeOnGlobalLayoutListener(this);
} else {
//noinspection deprecation
observer.removeGlobalOnLayoutListener(this);
}
mTabLayout.getTabAt(tabIndex).select();
}
});
}
}
}
Please comment if you have any suggestions.
The above answer wouldn't work because first As agirardello mentioned you should not use mTabLayout.getWidth() since it doesn't return what we need (which is the position of the child you want to scroll to) and the updated solution doesn't always work because of a bug in TabLayout (reported here) but a work around is simple.
The tabs on the tabLayout are not direct children of the TabLayout so we need to go one level deeper using
((ViewGroup) mTabLayout.getChildAt(0)).getChildAt(YOUR_DESIRED_TAB_INDEX).getRight()
the only child of tabLayout is a TabLayout.SlidingTabStrip which is also a ViewGroup and getRight() will give us the right most position of our desired tab view. Thus scrolling to that position will give us what we desire. Here is a complete code:
int right = ((ViewGroup) mTabLayout.getChildAt(0)).getChildAt(4).getRight();
mTabLayout.scrollTo(right,0);
mTabLayout.getTabAt(4).select();
NOTE: Make sure you are calling these methods after the layout has been drown (like onResume and not onCreate)
Hope this helps.
new Handler().postDelayed(
new Runnable() {
#Override public void run() {
mTabLayout.getTabAt(TAB_NUMBER).select();
}
}, 100);
The code snippet below works for me
class TriggerOnceListener(private val v: View, private val block: () -> Unit) : ViewTreeObserver.OnPreDrawListener {
override fun onPreDraw(): Boolean {
block()
v.viewTreeObserver.removeOnPreDrawListener(this)
return true
}
}
fun onCreate() {
val position = ***The tab position you want to scroll to, 29 for your case here***
tabLayout.let { it.viewTreeObserver.addOnPreDrawListener(TriggerOnceListener(it)
{ it.setScrollPosition(position, 0f, true) } ) }
}
I dived into Tab.select(), and found Android uses Tablayout.setScrollPosition() to do this scrolling. And in onCreate() the widgets have not been measured, you need to postpone the call until layout is complete.
To select the last tab, use
tabLayout.getTabAt(X).select(); where X is the last tab index
If your TabLayout is used in conjunction with a ViewPager, which is common, simply add the following in the onCreate() method in your Activity:
tabLayout.addOnTabSelectedListener(new TabLayout.ViewPagerOnTabSelectedListener(viewPager);
viewPager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(tabLayout);
That some of your tabs are not being shown indicates the tabMode attribute is set to app:tabMode="scrollable".
viewpager.setItem(position) should also set the position of the tab
This solution worked for me. My situation is a little bit different though; in my case, I am using the TabLayout with a ViewPager and adding more views and calling notifyDataSetChange().
The solution is to set a callback on the observer of TabLayout and scroll when the children are actually added to the TabLayout. Here is my example:
/**
Keep in mind this is how I set my TabLayout up...
PagerAdapter pagerAdapter = new PagerAdapter(...);
ViewPager pager = (ViewPager)findViewById(...);
pager.setAdapter(pagerAdapter);
TabLayout tabLayout = (TabLayout)findViewById(...);
tabLayout.setupWithViewPager(pager);
*/
public void loadTabs(String[] topics) {
animateTabsOpen(); // Irrelevant to solution
// Removes fragments from ViewPager
pagerAdapter.clear();
// Adds new fragments to ViewPager
for (String t : topics)
pagerAdapter.append(t, new TestFragment());
// Since we need observer callback to still animate tabs when we
// scroll, it is essential to keep track of the state. Declare this
// as a global variable
scrollToFirst = true;
// Alerts ViewPager data has been changed
pagerAdapter.notifyOnDataSetChanged();
// Scroll to the beginning (or any position you need) in TabLayout
// using its observer callbacks
tabs.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
/**
We use onGlobalLayout() callback because anytime a tab
is added or removed the TabLayout triggers this; therefore,
we use it to scroll to the desired position we want. In my
case I wanted to scroll to the beginning position, but this
can easily be modified to scroll to any position.
*/
if (scrollToFirst) {
tabs.getTabAt(0).select();
tabs.scrollTo(0, 0);
scrollToFirst = false;
}
}
});
}
Here is my code for the PagerAdapter if you need it too lol:
public class PagerAdapter extends FragmentStatePagerAdapter {
private List<Fragment> fragments;
private List<String> titles;
public PagerAdapter(FragmentManager fm) {
super(fm);
this.fragments = new ArrayList<>();
this.titles = new ArrayList<>();
}
/**
* Adds an adapter item (title and fragment) and
* doesn't notify that data has changed.
*
* NOTE: Remember to call notifyDataSetChanged()!
* #param title Fragment title
* #param frag Fragment
* #return This
*/
public PagerAdapter append(String title, Fragment frag) {
this.titles.add(title);
this.fragments.add(frag);
return this;
}
/**
* Clears all adapter items and doesn't notify that data
* has changed.
*
* NOTE: Rememeber to call notifyDataSetChanged()!
* #return This
*/
public PagerAdapter clear() {
this.titles.clear();
this.fragments.clear();
return this;
}
#Override
public Fragment getItem(int position) {
return fragments.get(position);
}
#Override
public CharSequence getPageTitle(int position) {
return titles.get(position);
}
#Override
public int getCount() {
return fragments.size();
}
#Override
public int getItemPosition(Object object) {
int position = fragments.indexOf(object);
return (position >= 0) ? position : POSITION_NONE;
}
}
I wonder if this is answer will be relevant since its coming very late. i actually achieved it in C# using Xamarin.
tabs.GetChildAt(0).Selected = true;
viewPager.SetCurrentItem(0, true);
tab = tabLayout.getSelectedTabPosition();
tab++;
TabLayout.Tab tabs = tabLayout.getTabAt(tab);
if (tabs != null) {
tabs.select();
}
else {
tabLayout.getTabAt(0).select();
}
if you want next tab on click event then use this code its work perfactly

Categories

Resources