Why is accessing TextView of a Fragment inside Act

2019-01-04 15:20发布

MainActivity class:

/* all necessary imports */

public class MainActivity extends AppCompatActivity
        implements NavigationView.OnNavigationItemSelectedListener {

    /* Other variable initialized here... */
    FragOne fo;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        fo.setTextViewText("This is added from Activity");

        DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
        ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
                this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
        drawer.addDrawerListener(toggle);
        toggle.syncState();

        NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view);
        navigationView.setNavigationItemSelectedListener(this);

        viewPager = (ViewPager) findViewById(R.id.viewpager);
        setupViewPager(viewPager);

        tabLayout = (TabLayout) findViewById(R.id.tabs);
        tabLayout.setupWithViewPager(viewPager);
    }


    private void setupViewPager(ViewPager viewPager) {
        ViewPagerAdapter adapter = new ViewPagerAdapter(getSupportFragmentManager());
        adapter.addFragment(new FragOne(), "My Tracker");
        adapter.addFragment(new FragTwo(), "Team Tracker");
        viewPager.setAdapter(adapter);
    }

    class ViewPagerAdapter extends FragmentPagerAdapter {
        private final List<Fragment> mFragmentList = new ArrayList<>();
        private final List<String> mFragmentTitleList = new ArrayList<>();

        public ViewPagerAdapter(FragmentManager manager) {
            super(manager);
        }

        @Override
        public Fragment getItem(int position) {
            return mFragmentList.get(position);
        }

        @Override
        public int getCount() {
            return mFragmentList.size();
        }

        public void addFragment(Fragment fragment, String title) {
            mFragmentList.add(fragment);
            mFragmentTitleList.add(title);
        }

        @Override
        public CharSequence getPageTitle(int position) {
            return mFragmentTitleList.get(position);
        }
    }

    @Override
    public void onBackPressed() {
        DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
        if (drawer.isDrawerOpen(GravityCompat.START)) {
            drawer.closeDrawer(GravityCompat.START);
        } else {
            super.onBackPressed();
        }
    }

    @SuppressWarnings("StatementWithEmptyBody")
    @Override
    public boolean onNavigationItemSelected(MenuItem item) {
        // Handle navigation view item clicks here.
        int id = item.getItemId();

        if (id == R.id.nav_manage) {

        } else if (id == R.id.nav_share) {

        } else if (id == R.id.nav_send) {

        }

        DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
        drawer.closeDrawer(GravityCompat.START);
        return true;
    }
}

Fragment class:

/* all necessary imports */
public class FragOne extends Fragment {

    TextView tvCName;

    public FragOne() {
        // Required empty public constructor
    }


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

        return view;
        //return inflater.inflate(R.layout.fragment_frag_one, container, false);
    }

    @Override
    public void onViewCreated(View view , Bundle savedInstanceState) {
        tvCName = (TextView) view.findViewById(R.id.tvctq);
    }

    public void setTextViewText(String value){
        tvCName.setText(value);
    }

}

Fragment XML Layout:

<FrameLayout 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"
    tools:context="com.mytip.FragOne">

        <TextView
            android:text="TextView"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:id="@+id/tvctq" />
</FrameLayout>

I am trying to access the TextView inside the Fragment from MainActivity like this:

FragOne fo;
fo.setTextViewText("This is added from Activity");

I keep getting a NullPointerExceptionError. I looked at all the articles to see how to access, however none of them helped me.

Can someone please let me know what am I doing wrong and how to fix it?

I also plan on adding other Views inside my Fragment that I would need to access in the future.

4条回答
我命由我不由天
2楼-- · 2019-01-04 15:31

You need to use your Fragment factory method when creating your Fragment in your activity. Please see below:

**

Back Stack

**

The transaction in which fragments are modified can be placed on an internal back-stack of the owning activity. When the user presses back in the activity, any transactions on the back stack are popped off before the activity itself is finished.

For example, consider this simple fragment that is instantiated with an integer argument and displays that in a TextView in its UI:

public static class CountingFragment extends Fragment {
int mNum;

/**
 * Create a new instance of CountingFragment, providing "num"
 * as an argument.
 */
static CountingFragment newInstance(int num) {
    CountingFragment f = new CountingFragment();

    // Supply num input as an argument.
    Bundle args = new Bundle();
    args.putInt("num", num);
    f.setArguments(args);

    return f;
}

/**
 * When creating, retrieve this instance's number from its arguments.
 */
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    mNum = getArguments() != null ? getArguments().getInt("num") : 1;
}

/**
 * The Fragment's UI is just a simple text view showing its
 * instance number.
 */
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
        Bundle savedInstanceState) {
    View v = inflater.inflate(R.layout.hello_world, container, false);
    View tv = v.findViewById(R.id.text);
    ((TextView)tv).setText("Fragment #" + mNum);
    tv.setBackgroundDrawable(getResources().getDrawable(android.R.drawable.gallery_thumb));
    return v;
}
}

A function that creates a new instance of the fragment, replacing whatever current fragment instance is being shown and pushing that change on to the back stack could be written as:

void addFragmentToStack() {
mStackLevel++;

// Instantiate a new fragment.
Fragment newFragment = CountingFragment.newInstance(mStackLevel);

// Add the fragment to the activity, pushing this transaction
// on to the back stack.
FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.replace(R.id.simple_fragment, newFragment);
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
ft.addToBackStack(null);
ft.commit();
}

After each call to this function, a new entry is on the stack, and pressing back will pop it to return the user to whatever previous state the activity UI was in.

Source: https://developer.android.com/reference/android/app/Fragment.html

查看更多
仙女界的扛把子
3楼-- · 2019-01-04 15:49

Because fo hasn't been initialized in the following code snippet:

FragOne fo;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
    setSupportActionBar(toolbar);

    fo.setTextViewText("This is added from Activity");
    ...
}

fo.setTextViewText() reasonably throws NPE.

查看更多
仙女界的扛把子
4楼-- · 2019-01-04 15:52

You have to pay attention to the Activity lifecycle - you seem to be setting everything up correctly, but making a few mistakes accessing the correct instance of the fragment at the time it's actually ready. Things you should do

  1. Get proper instance of the fragment from your ViewPager, like @ginomempin suggested;
  2. Only try to set your text no earlier then your activities onStart method has been called - I usually do it onResume method (you can override it if you haven't already). Doing it in onResume method in the activity makes sure your Fragment has already gone through it's lifecycle up till onResume as well, and data will refresh if it has been brought to the background previously.

Here's a lifecycle diagram for your reference: enter image description here

查看更多
啃猪蹄的小仙女
5楼-- · 2019-01-04 15:53

You need to get the same instance of FragOne from the viewpager.

First, you can only access the FragOne instance after the ViewPager is setup.
Then, try this:

fo = adapter.getItem(0)

Note:
Since you already have fragments, it would be better to let the fragment itself handle the UI-related actions (such as setting the textview) rather than from the Activity.

查看更多
登录 后发表回答