I started using ViewPager2 and I don't know how to use custom tablayout to customize the page title as normal viewpager.
I found a temporary and good solution.
1-Use RecyclerView for the tabs and handle it manually.
2-Use ViewPager2 for fragments.
public class ViewPagerHelper extends ViewPager2.OnPageChangeCallback implements TabSelectInterface {
private Activity activity;
private byte from;
private RecyclerView recyclerViewTabs;
private ViewPager2 viewPager2;
private List<Fragment> fragmentList;
private List<String> tabList;
private ViewPagerTabsAdapter viewPagerTabsAdapter;
private FragmentManager fragmentManager;
private boolean isLive;
private LinearLayoutManager linearLayoutManagerTabs;
public ViewPagerHelper(FragmentManager fragmentManager, Activity activity, byte from, List<Fragment> fragmentList, List<String> tabList, boolean isLive) {
this.fragmentManager = fragmentManager;
this.activity = activity;
this.from = from;
this.fragmentList = fragmentList;
this.tabList = tabList;
this.isLive = isLive;
setupViewPager();
}
private void setupViewPager() {
recyclerViewTabs = activity.findViewById(R.id.recycle_tabs);
viewPager2 = activity.findViewById(R.id.viewpager);
linearLayoutManagerTabs = new LinearLayoutManager(activity, RecyclerView.HORIZONTAL, false);
recyclerViewTabs.setLayoutManager(linearLayoutManagerTabs);
setTabsAdapter();
setFragmentsAdapter();
viewPager2.registerOnPageChangeCallback(this);
// viewPager2.setUserInputEnabled(false);
}
private void setTabsAdapter() {
viewPagerTabsAdapter = new ViewPagerTabsAdapter(activity, tabList, isLive, this);
recyclerViewTabs.setAdapter(viewPagerTabsAdapter);
}
private void setFragmentsAdapter() {
ViewPagerFragmentAdapter viewPagerFragmentAdapter = new ViewPagerFragmentAdapter(fragmentManager, fragmentList);
viewPager2.setAdapter(viewPagerFragmentAdapter);
}
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
System.out.println("onPageScrolled1: " + position);
System.out.println("onPageScrolled2: " + positionOffset);
System.out.println("onPageScrolled3: " + positionOffsetPixels);
super.onPageScrolled(position, positionOffset, positionOffsetPixels);
}
@Override
public void onPageSelected(int position) {
super.onPageSelected(position);
Log.e("Selected_Page", String.valueOf(position));
viewPagerTabsAdapter.setSelectedTab(position);
linearLayoutManagerTabs.scrollToPositionWithOffset(position, 0);
}
@Override
public void onPageScrollStateChanged(int state) {
super.onPageScrollStateChanged(state);
}
@Override
public void onTabSelected(int position) {
viewPager2.setCurrentItem(position, true);
}
public void animateFirstTab(){
viewPagerTabsAdapter.animateFirstTab();
}
And the viewpager2 adapter class
public class ViewPagerFragmentAdapter extends FragmentStateAdapter {
private List<Fragment> arrayList;
ViewPagerFragmentAdapter(@NonNull FragmentManager fragmentManager, List<Fragment> arrayList) {
super(fragmentManager);
this.arrayList = arrayList;
}
@NonNull
@Override
public Fragment getItem(int position) {
return arrayList.get(position);
}
@Override
public int getItemCount() {
return arrayList.size();
}
}
The recyclerview tab adapter
public class ViewPagerTabsAdapter extends RecyclerView.Adapter<ViewPagerTabsAdapter.OrderHolder> {
private Context context;
private List<String> list;
private int selectedTab = 0;
private TabSelectInterface tabSelectInterface;
private boolean isLive = false;
private List<Integer> tabsWidth = new ArrayList<>();
int numberToSpliteWidth = 0;
private boolean animateFirstTab=false;
ViewPagerTabsAdapter(Context context, List<String> list, boolean isLive, TabSelectInterface tabSelectInterface) {
this.context = context;
this.list = list;
this.isLive = isLive;
this.tabSelectInterface = tabSelectInterface;
}
@NonNull
@Override
public OrderHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View rowView = LayoutInflater.from(context).inflate(R.layout.custom_tab, parent, false);
// final RecyclerView.ViewHolder holder = new RecyclerView.ViewHolder(rowView);
// if (!tabsWidth.isEmpty()) {
// int sumWidths = 0;
// for (Integer w : tabsWidth) {
// sumWidths += w;
// }
//
// //no scroll needed, split the width
// if (sumWidths < parent.getWidth()) {
//
// System.out.println("THEWIDTHIS: " + viewType);
// int newWidth = parent.getWidth() / list.size();
// if (newWidth > viewType) {
// rowView.getLayoutParams().width = newWidth;
// }
//
//
// }
//
// }
if (!tabsWidth.isEmpty()) {
DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
int newTabMinWidth = displayMetrics.widthPixels / list.size();
System.out.println("THEtabMinWidth: " + newTabMinWidth);
if (list.size() <= 4) {
// rowView.setMinimumWidth(tabMinWidth - 25);
if (newTabMinWidth > viewType) {
rowView.getLayoutParams().width = newTabMinWidth;
}
} else {
// rowView.setMinimumWidth(tabMinWidth + 32);
if (newTabMinWidth > viewType) {
rowView.getLayoutParams().width = (int) (newTabMinWidth + context.getResources().getDimension(R.dimen._9sdp));
}
}
}
return new OrderHolder(rowView);
}
@Override
public void onBindViewHolder(@NonNull OrderHolder holder, int position) {
holder.name.setText(list.get(position));
if (selectedTab == position) {
holder.name.setTextColor(ContextCompat.getColor(context, R.color.yellow));
holder.selectedView.setVisibility(View.VISIBLE);
} else {
holder.name.setTextColor(ContextCompat.getColor(context, R.color.font2));
holder.selectedView.setVisibility(View.INVISIBLE);
}
if (holder.name.getText().toString().equals("الفيديو")) {
//
if (isLive) {
Glide.with(context).asGif().load(R.drawable.live).into(holder.imageView);
holder.imgframe.setVisibility(View.VISIBLE);
} else {
holder.imgframe.setVisibility(View.GONE);
}
} else {
holder.imgframe.setVisibility(View.GONE);
}
if (animateFirstTab){
Animation a = AnimationUtils.loadAnimation(context, R.anim.scale);
a.reset();
holder.name.clearAnimation();
holder.name.startAnimation(a);
animateFirstTab=false;
}
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
tabSelectInterface.onTabSelected(holder.getAdapterPosition());
}
});
if (tabsWidth.isEmpty()) {
final ViewTreeObserver observer = holder.itemView.getViewTreeObserver();
observer.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
holder.itemView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
// int containerWidth = holder.itemView.getWidth();
// int containerHeight = holder.itemView.getHeight();
// float x = containerWidth - Float.parseFloat(model.getXpoints(o)) / 100 * containerWidth;
// float y = containerHeight - Float.parseFloat(model.getYpoints(o)) / 100 * containerHeight;
// Log.e("measuredWidth", "" + containerHeight + "" + containerWidth);
// Log.e("getPointX", "" + model.getXpoints(o) + "" + model.getYpoints(o));
// Log.e("x", "x" + x + "y" + y);
//
// h.tag.setX(x);
// h.tag.setY(y);
// h.tag.requestLayout();
System.out.println("THEWIDTHIS2: " + holder.itemView.getWidth());
tabsWidth.add(holder.itemView.getWidth());
if (position == list.size() - 1) {
System.out.println("notifiNOW");
notifyDataSetChanged();
}
}
});
}
}
@Override
public int getItemViewType(int position) {
try {
return tabsWidth.get(position);
} catch (Exception e) {
// return super.getItemViewType(position);
return 0;
}
}
@Override
public int getItemCount() {
return list.size();
}
void setSelectedTab(int selectedTab) {
int currentSelectedTab = this.selectedTab;
if (currentSelectedTab != selectedTab) {
this.selectedTab = selectedTab;
notifyItemChanged(currentSelectedTab);
notifyItemChanged(selectedTab);
}
}
void animateFirstTab() {
animateFirstTab=true;
notifyItemChanged(0);
}
public class OrderHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
private TextView name;
private View selectedView;
private FrameLayout imgframe;
private ImageView imageView;
OrderHolder(View itemView) {
super(itemView);
name = itemView.findViewById(R.id.textview);
selectedView = itemView.findViewById(R.id.selected_view);
imgframe = (FrameLayout) itemView.findViewById(R.id.imgframe);
imageView = (ImageView) itemView.findViewById(R.id.imageView);
}
@Override
public void onClick(View view) {
}
}
Use this in XML
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycle_tabs"
android:layout_width="match_parent"
android:layout_height="52dp"
android:layout_gravity="center_horizontal"
ads:layout_constraintBottom_toBottomOf="parent"
ads:layout_constraintEnd_toEndOf="parent"
ads:layout_constraintStart_toStartOf="parent" />
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@+id/container"
android:layout_marginBottom="50dp" />
And to use it. you just need to use this code
List<Fragment> fragmentList = new ArrayList<>();
List<String> tabtList = new ArrayList<>();
tabtList.add(tab_index,"tab_name" );
fragmentList.add(tab_index, new TabFragment());
new ViewPagerHelper(getSupportFragmentManager(), MatchProfileActivity.this, VIEWPAGER_TYPE_FROM_MATCHES, fragmentList, tabtList,
false);
Hint: not all code needed I put all my code.
Today, I was looking for this thing. After some studying, and some trials and errors, I came up with a solution:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto">
<com.google.android.material.tabs.TabLayout
android:id="@+id/storiesTabLayout"
app:layout_constraintTop_toTopOf="parent"
android:clipToPadding="false"
android:layout_width="match_parent"
android:layout_height="150dp"
/>
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/reactionsViewPager2"
app:layout_constraintTop_toBottomOf="@id/storiesTabLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</androidx.constraintlayout.widget.ConstraintLayout>
Now create an adapter for your viewPager2 (I'll be using Recyclerview.Adapter
as I don't need fragments).
Now you need a
public fun getCustomTabAt(position: Int): View {
val simpleStoryView: View = LayoutInflater.from(activity).inflate(R.layout.fragment_viewstoryreacts_simple_story_view, null);
val roundedImageView: RoundedImageView = simpleStoryView.findViewById(R.id.storyImageView);
var url = "";
if(stories[position].getMedia().type == "photos") {
url = stories[position].getMedia().data[0].url;
}else{
url = stories[position].getMedia().data[0].thumbnail?: "";
}
Picasso.get().load(url).into(roundedImageView);
return simpleStoryView;
}
Place it in your fragment / activity / adapter (I put it in the adapter for convenience such as easy integration with data). Basically, this method creates and returns a custom view that will be displayed in your tab.
Finally, in your Fragment/activity you need something like this:
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
fragmentRootView = inflater.inflate(R.layout.fragment_viewstoryreacts_bottomsheet, container, false);
this.activity = requireActivity() as ConversationsActivity;
initGui();
return fragmentRootView;
}
/** Private methods */
private fun initGui() {
this.storiesTabLayout = fragmentRootView.findViewById(R.id.storiesTabLayout);
// <--- Step 1: link the tab layout
this.reactionsViewPager2 = fragmentRootView.findViewById(R.id.reactionsViewPager2);
// step 2: link the viewPager2, and init adapters, data etc
this.reactionsAdapter = ReactionsAdapter(activity = requireActivity(),stories = stories);
reactionsViewPager2.adapter = reactionsAdapter;
reactionsViewPager2.orientation = ViewPager2.ORIENTATION_HORIZONTAL;
storiesTabLayout.setTabMode(TabLayout.MODE_SCROLLABLE);
storiesTabLayout.setTabGravity(TabLayout.GRAVITY_CENTER);
// step3: init tabLayoutMediator to bind tabs with the viewPager2
val tabLayoutMediator: TabLayoutMediator = TabLayoutMediator(storiesTabLayout, reactionsViewPager2, object : TabLayoutMediator.TabConfigurationStrategy{
override fun onConfigureTab(tab: TabLayout.Tab, position: Int) {
val finalPosition = position;
val simpleStoryView: View = reactionsAdapter.getCustomTabAt(finalPosition);
tab.customView = simpleStoryView;
}
});
tabLayoutMediator.attach();
// <-- step 4: call the attach()
}
This is for fragments. If you are using activity, just call the initGui()
inside onCreate()
method.
Here we are creating a
Finally, run the code. This is my output:
As you can see, the tabs now have imageviews. (The viewpager2 needs some work). I hope this is helpful.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With