Search code examples
androidandroid-layoutandroid-fragmentsnullpointerexceptionfindviewbyid

Change TextView values with method inside Fragment (NullPointerException)


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): enter image description here enter image description here enter image description here


Solution

  • 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.