I'm trying to make an app (for learning purposes) where I have a few tabs with scrollable content. I have a collapsing app bar with a ViewPager for the main layout and three identical layouts with a TextView inside a NestedScrollView where the only difference is the image on top of the text. I also have three fragment classes with the same code inside. Everything worked fine.
But since I'm using the same layout in each tab, I thought that instead of a xml file and a java class for each tab, I could just have one of each and then somehow change the content for each tab inside my Adapter.
So I tried to just create a method to do that inside my tabFragment class. But I can't seem to find a way to modify the TextView. I keep getting a NullPointerException every time.
What am I missing? Is my approach wrong? If so, what would be a better one? How do I solve this?
Thank you in advance :)
Here's my fragment class right now (the only one remaining ):
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.fragment.app.Fragment;
import com.example.tablayoutwithscrollview.R;
public class TabFragment extends Fragment {
private TextView textView;
private int text, image;
public TabFragment(){}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
View view = inflater.inflate(R.layout.tab1, container, false);
//textView = view.findViewById(R.id.textView);
return view;
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState)
{
super.onViewCreated(view, savedInstanceState);
textView = getView().findViewById(R.id.textView);
}
public void setTabContent(int text, int image)
{
textView.setText(text);
textView.setCompoundDrawablesWithIntrinsicBounds(0,image,0,0);
}
}
My adapter class:
import android.content.Context;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentPagerAdapter;
import com.example.tablayoutwithscrollview.Tabs.TabFragment;
public class MyAdapter extends FragmentPagerAdapter {
private static int tabCount;
Context context;
public MyAdapter(FragmentManager fm, int tabCount, Context context){
super(fm);
this.tabCount = tabCount;
this.context = context;
}
@Override
public Fragment getItem(int position) {
TabFragment fragment;
switch (position)
{
case 0:
fragment = new TabFragment();
fragment.setTabContent(R.string.lorem_ipsum, R.drawable.jackfruit);
return fragment;
case 1:
fragment = new TabFragment();
fragment.setTabContent(R.string.lorem_ipsum, R.drawable.guava);
return fragment;
case 2:
fragment = new TabFragment();
fragment.setTabContent(R.string.lorem_ipsum, R.drawable.pomegranate);
return fragment;
default:
return null;
}
}
@Override
public CharSequence getPageTitle(int position)
{
switch (position)
{
case 0:
return context.getString(R.string.tab1_title);
case 1:
return context.getString(R.string.tab2_title);
case 2:
return context.getString(R.string.tab3_title);
default:
return null;
}
}
@Override
public int getCount() {
return tabCount;
}
}
MainActivity:
package com.example.tablayoutwithscrollview;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.widget.NestedScrollView;
import androidx.viewpager.widget.ViewPager;
import android.os.Bundle;
import com.google.android.material.tabs.TabLayout;
public class MainActivity extends AppCompatActivity {
private TabLayout tabLayout;
private ViewPager viewPager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tabLayout = findViewById(R.id.tabLayout);
viewPager = findViewById(R.id.viewPager);
populatesTabLayout(tabLayout);
MyAdapter myAdapter = new MyAdapter(getSupportFragmentManager(), tabLayout.getTabCount(), this);
viewPager.setAdapter(myAdapter);
tabLayout.setupWithViewPager(viewPager);
}
private void populatesTabLayout(TabLayout tabLayout)
{
for (int i = 0; i<3; i++)
{
tabLayout.addTab(tabLayout.newTab());
}
}
}
activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/coordLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appBarLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.google.android.material.appbar.CollapsingToolbarLayout
android:id="@+id/collapseToolBar"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
app:contentScrim="?attr/colorPrimary"
app:layout_scrollFlags="scroll|exitUntilCollapsed"
app:toolbarId="@+id/toolbar">
<ImageView
android:id="@+id/imageView7"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:srcCompat="@drawable/strawberry" />
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"></androidx.appcompat.widget.Toolbar>
</com.google.android.material.appbar.CollapsingToolbarLayout>
<com.google.android.material.tabs.TabLayout
android:id="@+id/tabLayout"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:visibility="visible" />
</com.google.android.material.appbar.AppBarLayout>
<androidx.viewpager.widget.ViewPager
android:id="@+id/viewPager"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
And my tab layout:
<?xml version="1.0" encoding="utf-8"?>
<androidx.core.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/tabNestedScrollView"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/textView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:drawableTop="@drawable/jackfruit"
android:text="@string/lorem_ipsum" />
</androidx.core.widget.NestedScrollView>
Here's how it's supposed to work (from before when each tab had a fragment class and a layout file):
The problem is that the created TabFragment instance has not been attached to the activity yet, therefore the textView does not inflated at that point.
You should pass the text to the TabFragment instance as an argument in the MyAdapter#getItem(..) method.
...
case 0:
fragment = new TabFragment();
Bundle args = new Bundle();
args.putString("key", textString);
fragment.setArguments(args);
return fragment
...
After that set the text for the TextView instance in the onViewCreated(...) method:
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
textView = getView().findViewById(R.id.textView);
textView.setText(getArguments().getString("key"));
}
Or you can see a nice example at the Android Reference.