krz.
krz.

Reputation: 21

Cannot access Textview inside fragment

I have an activity with a fragment on it that has a TextView inside. In the CourseFragment.java I made a simple method that changes the text of the TextView. However, when I try to call the fragment's method from the Activity, it says that the TextView object is null. Here is some of my code:

CourseFragment.java

public class CourseFragment extends Fragment {

private Subject subject;
View rootView;
private TextView title;

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    // Inflate the layout for this fragment
    rootView = inflater.inflate(R.layout.fragment_course, container,
            false);

    title = (TextView) rootView.findViewById(R.id.course_title);

    return rootView;
}

public void setTitle() {
    title.setText("My Title");
}

}

this is the code in my activity that makes the new fragment and puts in the right location:

CourseFragment fragment = new CourseFragment();
FragmentManager fm = getFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
ft.replace(R.id.course1, fragment);
fragment.setTitle();
ft.addToBackStack(null);
ft.commit();

the error coming from the setTitle() method inside the fragment

Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.TextView.setText(java.lang.CharSequence)' on a null object reference

this seems so straight forward yet I don't understand why I keep getting NPE. I looked at similar questions about this and tried all the accepted solutions but to no avail. any ideas?

thanks in advance

edit: here is some more code as per requested:

the beginning of fragment_course.xml

<LinearLayout 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/course_fragment"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.stagenda.stagenda.CourseFragment"
    android:padding="16dp"
    android:orientation="vertical"
    android:background="@android:color/background_light">

    <RelativeLayout
        android:layout_width="match_parent"
        tools:ignore="UselessParent"
        android:background="@color/colorAccent"
        android:padding="16dp"
        android:layout_height="wrap_content"
        android:id="@+id/course_container">
<TextView
        android:text="Course Given Title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/course_given_title"
        android:textColor="@android:color/background_light"
        android:textSize="18sp" />

    <TextView
    android:text="Course Title"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:id="@+id/course_title"
    android:layout_below="@+id/course_given_title"
    android:textColor="@android:color/background_light" />

    <TextView
        android:text="Course Code"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/course_code"
        android:textColor="@android:color/background_light"
        android:layout_toRightOf="@+id/course_title"
        android:layout_below="@+id/course_given_title"
        android:layout_alignParentEnd="false"
        android:layout_marginStart="10dp" />

full error:

E/AndroidRuntime: FATAL EXCEPTION: main
                  Process: com.stagenda.stagenda, PID: 1654
                  java.lang.IllegalStateException: Could not execute method for android:onClick
                      at android.support.v7.app.AppCompatViewInflater$DeclaredOnClickListener.onClick(AppCompatViewInflater.java:293)
                      at android.view.View.performClick(View.java:5639)
                      at android.view.View$PerformClick.run(View.java:22387)
                      at android.os.Handler.handleCallback(Handler.java:751)
                      at android.os.Handler.dispatchMessage(Handler.java:95)
                      at android.os.Looper.loop(Looper.java:154)
                      at android.app.ActivityThread.main(ActivityThread.java:6088)
                      at java.lang.reflect.Method.invoke(Native Method)
                      at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886)
                      at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776)
                   Caused by: java.lang.reflect.InvocationTargetException
                      at java.lang.reflect.Method.invoke(Native Method)
                      at android.support.v7.app.AppCompatViewInflater$DeclaredOnClickListener.onClick(AppCompatViewInflater.java:288)
                      at android.view.View.performClick(View.java:5639) 
                      at android.view.View$PerformClick.run(View.java:22387) 
                      at android.os.Handler.handleCallback(Handler.java:751) 
                      at android.os.Handler.dispatchMessage(Handler.java:95) 
                      at android.os.Looper.loop(Looper.java:154) 
                      at android.app.ActivityThread.main(ActivityThread.java:6088) 
                      at java.lang.reflect.Method.invoke(Native Method) 
                      at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886) 
                      at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776) 
                   Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.TextView.setText(java.lang.CharSequence)' on a null object reference
                      at com.stagenda.stagenda.CourseFragment.setTitle(CourseFragment.java:34)
                      at com.stagenda.stagenda.MainPage.confirmAddCourse(MainPage.java:435)
                      at java.lang.reflect.Method.invoke(Native Method) 
                      at android.support.v7.app.AppCompatViewInflater$DeclaredOnClickListener.onClick(AppCompatViewInflater.java:288) 
                      at android.view.View.performClick(View.java:5639) 
                      at android.view.View$PerformClick.run(View.java:22387) 
                      at android.os.Handler.handleCallback(Handler.java:751) 
                      at android.os.Handler.dispatchMessage(Handler.java:95) 
                      at android.os.Looper.loop(Looper.java:154) 
                      at android.app.ActivityThread.main(ActivityThread.java:6088) 
                      at java.lang.reflect.Method.invoke(Native Method) 
                      at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886) 
                      at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776) 
I/Process: Sending signal. PID: 1654 SIG: 9
Disconnected from the target VM, address: 'localhost:8612', transport: 'socket'

explanation of layout (image)

Upvotes: 1

Views: 2599

Answers (7)

sats
sats

Reputation: 657

The onCreateView() method is called after fragmentTransaction.commit(). Since, you are calling setTitle() before fragmentTransaction.commit(), the TextView instance title is not instantiated and so is null. Add the line fragment.setTitle() after fragmentTransaction.commit().

Upvotes: 0

krz.
krz.

Reputation: 21

I figured it out myself. The problem was here:

CourseFragment fragment = new CourseFragment();
FragmentManager fm = getFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
ft.replace(R.id.course1, fragment);
fragment.setTitle();
ft.addToBackStack(null);
ft.commit();

The problem was that I was calling setTitle() on fragment which did not have its view created. when instead I did the following the problem was solved:

CourseFragment fragment = new CourseFragment();
FragmentManager fm = getFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
ft.replace(R.id.course1, fragment);
ft.addToBackStack(null);
ft.commit();

CourseFragment frag = fm.findFragmentById(R.id.course1);
frag.setTitle();

I eventually changed a lot of my code but I hope this helps someone. thanks for the answers everyone

Upvotes: 1

Hamza
Hamza

Reputation: 2045

solved this problem by using Framgnet Callbacks as described in here http://developer.android.com/training/basics/fragments/communicating.html I believe that this is the best was to pass fragment TextViews into my activity after they are created.

Upvotes: 0

Pushpendra
Pushpendra

Reputation: 2819

One thing you can do is pass your data as parameters:

CourseFragment fragment = new CourseFragment(Your title);
FragmentManager fm = getFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
ft.replace(R.id.course1, fragment);
ft.addToBackStack(null);
ft.commit();

You can also use Bundle to pass your data.

Upvotes: 0

Sumit Shetty
Sumit Shetty

Reputation: 112

Layout for tabs:

<?xml version="1.0" encoding="utf-8"?>
    <LinearLayout 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="wrap_content"
        android:orientation="vertical"
        android:background="#ffffff">

        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            app:layout_scrollFlags="scroll|enterAlways"
            app:layout_collapseMode="pin"
            android:background="#000000"
            app:titleTextColor="#ffffff"

            app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
            app:popupTheme="@style/PopupMenuStyle">

            <TextView
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize"
                android:id="@+id/textview"
                android:textColor="@color/colorTrueWhite"/>

        </android.support.v7.widget.Toolbar>

        <!-- our tablayout to display tabs  -->
        <android.support.design.widget.TabLayout
            android:id="@+id/tabLayout"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="@color/colorBlack"
            android:minHeight="?attr/actionBarSize"

            app:tabIndicatorColor="@color/colorTrueWhite"
            app:tabIndicatorHeight="5dp"
            android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"/>

        <!-- View pager to swipe views -->


        <android.support.v4.view.ViewPager
            android:id="@+id/pager"
            android:layout_width="match_parent"
            android:layout_height="fill_parent"/>


    </LinearLayout>

Activity using that layout:

public class Main2Activity extends AppCompatActivity implements TabLayout.OnTabSelectedListener {

    private TabLayout tabLayout;
    public static ViewPager viewPager;
    public static Context ctx;
    Pager adapter;
    public static int expired, paid;
    Boolean isConnection;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main2);

        Toolbar tb = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(tb);

        TextView tv = (TextView) findViewById(R.id.textview);

        //getSupportActionBar().setDisplayHomeAsUpEnabled(true);


        ctx = getApplicationContext();


        StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
        StrictMode.setThreadPolicy(policy);


        tabLayout = (TabLayout) findViewById(R.id.tabLayout);
        viewPager = (ViewPager) findViewById(R.id.pager);


        tabLayout.addTab(tabLayout.newTab().setText("title1"));
        tabLayout.addTab(tabLayout.newTab().setText("title2"));
        tabLayout.addTab(tabLayout.newTab().setText("title3"));
        tabLayout.setTabGravity(TabLayout.GRAVITY_FILL);
        tabLayout.setupWithViewPager(viewPager);

        adapter = new Pager(getSupportFragmentManager(), tabLayout.getTabCount(), ctx);
        viewPager.setAdapter(adapter);


        if(some condition)
        {
            viewPager.setCurrentItem(2);
        }
        else
        {
            viewPager.setCurrentItem(1);
        }
        viewPager.setOffscreenPageLimit(2);

        tabLayout.addOnTabSelectedListener(this);

        ViewGroup vg = (ViewGroup) tabLayout.getChildAt(0);
        int tabsCount = vg.getChildCount();
        for (int j = 0; j < tabsCount; j++) {
            ViewGroup vgTab = (ViewGroup) vg.getChildAt(j);
            int tabChildsCount = vgTab.getChildCount();
            for (int i = 0; i < tabChildsCount; i++) {
                View tabViewChild = vgTab.getChildAt(i);
                if (tabViewChild instanceof TextView) {
                    ((TextView) tabViewChild).setTypeface(Typeface.DEFAULT, Typeface.BOLD);
                }
            }
        }
    }


    @Override
    public void onTabSelected(TabLayout.Tab tab) {
        viewPager.setCurrentItem(tab.getPosition());
    }

    @Override
    public void onTabReselected(TabLayout.Tab tab) {
    }

    @Override
    public void onTabUnselected(TabLayout.Tab tab) {
    }


}

Pager code:

public class Pager extends FragmentStatePagerAdapter
{
    int tabcount;
    Context ctx;
    private String [] Titles = {"title1", "title2", "title3"};

    public Pager(FragmentManager fm, int tabcount, Context ctx)
    {
        super(fm);
        this.tabcount = tabcount;
        this.ctx = ctx;
    }

    @Override
    public int getCount() {
        return tabcount;
    }

    @Override
    public CharSequence getPageTitle(int position) {
        return Titles[position];
    }

    @Override
    public Fragment getItem(int position) {

        switch (position) {

            case 0:
                Tab1 tab1 = new Tab1();
                return tab1;

            case 1:
                Tab2 tab2 = new Tab2();
                return tab2;

            case 2:
                Tab3 tab3 = new Tab3();
                return tab3;

            default:
                return null;
        }
    }


}

Upvotes: 0

frozenkoi
frozenkoi

Reputation: 3248

Fragment transactions are not synchronous. When you add a transaction it can be executed when the main looper has a chance to run it (it gets queued for later).

This means that the fragment gets attached until after the current method ends. Check the order in which CourseFragment.setTitle() and onCreateView() are executed.

See docs for FragmentTransaction.commit(): commit

Consider using commitNow() if it works for you.

Also, consider passing the title as an argument to the fragment so that it can be read inside onCreateView using getArguments().

Upvotes: 0

Kush Patel
Kush Patel

Reputation: 1251

Go with this approch.

 Bundle bundle = new Bundle();
    bundle.putString("title", "whatever you want to pass");

        CourseFragment fragment = new CourseFragment();
        fragment.setArguments(bundle);
        FragmentManager fm = getFragmentManager();
        FragmentTransaction ft = fm.beginTransaction();
        ft.replace(R.id.course1, fragment);
        ft.addToBackStack(null);
        ft.commit();



    public class CourseFragment extends Fragment {

    private Subject subject;
    View rootView;
    private TextView title;
    Bundle bundle;

      @Override
        public void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);

            bundle = getArguments();
         }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        rootView = inflater.inflate(R.layout.fragment_course, container,
                false);

        title = (TextView) rootView.findViewById(R.id.course_title);

        String text = bundle.getString("title");
        title.setText(text);

        return rootView;
    }


    }

Upvotes: 2

Related Questions