0

I have a use case in which I use a ViewPager with TabLayout. I have used the Android tutorial linked here.

My Layout

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">
     <androidx.viewpager.widget.ViewPager
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/notification_view_pager">
        <com.google.android.material.tabs.TabLayout
            android:id="@+id/notification_tabs"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />
    </androidx.viewpager.widget.ViewPager>
</LinearLayout>

My activity

public class MyActivity extends AppCompatActivity 
{

    int currentFocussedTab = -1;

    private TabLayout notificationsTabLayout;

    private ViewPager notificationsViewPager;

    private FragmentStatePagerAdapter notificationsStatePagerAdapter;

    private Fragment fragmentOne;

    private Fragment fragmentTwo;

    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.layout_activity);
        // Setting up tabbed layouts.
        notificationsTabLayout = findViewById(R.id.notification_tabs);

        notificationsTabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
            @Override
            public void onTabSelected(TabLayout.Tab tab)
            {
                int position = tab.getPosition();
                currentFocussedTab = position;
                // Custom function to log click.
                if (position == 0) {
                  fragmentOne.logClick();
                } else {
                  fragmentTwo.logClick();
                }
            }

            @Override
            public void onTabUnselected(TabLayout.Tab tab)
            {
                int position = tab.getPosition();

                if (position == 0) {
                   fragmentOne.logUnselect();
                } else
                   fragmentTwo.logUnselect();
                }
            }

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

        // Set up view pager.
        notificationsViewPager = findViewById(R.id.notification_view_pager);
        notificationsViewPager.setCurrentItem(0);
        notificationsTabLayout.setupWithViewPager(notificationsViewPager);
        FragmentManager fragmentManager = getSupportFragmentManager();
        fragmentOne = new FragmentOne();
        fragmentTwo = new FragmentTwo();
        notificationsStatePagerAdapter = new FragmentStatePagerAdapter(fragmentManager,
            FragmentStatePagerAdapter.BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
            @NonNull
            @Override
            public Fragment getItem(int position)
            {
                return position == 0 ? fragmentOne : fragmentTwo;
            }

            @Override
            public int getCount()
            {
                // Two fixed tabs.
                return 2;
            }

            @Nullable
            @Override
            public CharSequence getPageTitle(int position)
            {
                return position == 0 ? getText(R.string.act_now_tab) : getText(R.string.others_tab);
            }


        };
        notificationsViewPager.setAdapter(notificationsStatePagerAdapter);
    }
}

Both FragmentOne and Fragment two are similarly set up. So I am using FragmentOne as an example

class FragmentOne extends Fragment {


. . . . Define all UI elements . . . .

    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, 
          Bundle savedInstanceState)
   {
       final View view = inflater.inflate(R.layout.fragment_layout, container, false);
        ... Initialize all the layout UI elements . . . 

       return view;
   }
}

UPDATE

I have a button defined in the fragment layout. The button is initialised in onCreateView method. When the button is clicked, then I want to launch a new activity.

ISSUE

I have to load another activity from inside the fragment. This is how I am doing it

Intent intent = new Intent(getActivity(), MyNewActivity.class);
getActivity().startActivity(intent);

However, the application terminates because the getActivity() always returns null. If I don't have the fragment in the view pager, then the function does not return null.

DESIRED BEHAVIOUR

I want to load the new application activity from within a fragment that has been loaded inside the view pager. How do I achieve that?

Kartik
  • 2,541
  • 2
  • 37
  • 59
  • Have you tried passing Activity context from the activity to the Fragment class? Save it in a global variable, and then you can try using it to start another activity – Atish Agrawal Jan 01 '20 at 14:40
  • You should store the activity variable as member in the fragment in the onAttach of the fragment and use it later with Intent to start new activities. The context (activity) in onAttach is guaranteed to be @NonNull – Daniel Jan 01 '20 at 15:31
  • Where are you putting the code for starting new Activity? – Mohammed Alaa Jan 01 '20 at 16:41
  • @MohammedAlaa I am putting it in the fragment. There is a button defined as one of a UI elements in the fragment layout. I have an onclick listener callback that creates the intent. – Kartik Jan 02 '20 at 06:18
  • @AtishAgrawal Shouldn't fragment have access to the activity? – Kartik Jan 02 '20 at 06:19
  • @Daniel I am afraid I don't understand. – Kartik Jan 02 '20 at 07:01
  • 1
    Check #2 and #3 in https://proandroiddev.com/the-seven-actually-10-cardinal-sins-of-android-development-491d2f64c8e0 – EpicPandaForce Jan 02 '20 at 08:58
  • @EpicPandaForce Will this solve my problem? – Kartik Jan 02 '20 at 11:14
  • There is a good chance that it might – EpicPandaForce Jan 02 '20 at 11:30
  • as @EpicPandaForce mentioned in his article this is not recommended , as I have understood if you want to launch activity with button clicked or some action in your fragment, you can do this normally in onCreateView or if I understand this wrong , please edit your question with putting your starting activity code in it's place in fragment which case the problem – Mohammed Alaa Jan 02 '20 at 14:52
  • @MohammedAlaa I have a button in the fragment layout xml. I want to launch an activity in the onclick event listener callback function. – Kartik Jan 02 '20 at 19:09
  • @EpicPandaForce But i need access to the fragment instance in the Tablayout callback listener. – Kartik Jan 02 '20 at 19:10
  • https://stackoverflow.com/questions/54279509/how-to-get-elements-of-fragments-created-by-viewpager-in-mainactivity/54280113#54280113 – EpicPandaForce Jan 02 '20 at 19:50

2 Answers2

1

Try something like this, assuming you are launching a new intent by a button click.

In your fragment class

public class MyFragment extends Fragment implements View.OnclickListener {
    private Activity activity;

    @Override
    public void onAttach(Context context) {
        activity = (Activity) context;
    }

    @Override
    public void onDetach(){
        activity = null;
    }

    @Override
    public void onClick(View v) {
        startIntent();
    }

    private void startIntent(){
        if (activity == null) return;
        Intent intent = new Intent(activity, MyNewActivity.class);
        activity.startActivity(intent);
    }
}

If this does not work, ie., activity is null, then you could be starting the intent in the wrong lifecycle stage of the fragment, when the fragment is not attached to an activity and not through a button's onclicklistener.

Daniel
  • 400
  • 1
  • 2
  • 12
  • This should work, but be careful keeping references to Activities in Fragments, it's not good form as you can get memory leaks. – Cory Roy Jan 03 '20 at 23:47
  • @CoryRoy Yes, in general. But can you elaborate on this specific case? I believe it is fine with reference passed from onAttach and that is removed onDetach. – Daniel Jan 04 '20 at 00:05
0

I would put the method in question in your Activity and call it from your Fragment. I added a method to the bottom of your class.

Activity:

public class MyActivity extends AppCompatActivity 
{

    int currentFocussedTab = -1;

    private TabLayout notificationsTabLayout;

    private ViewPager notificationsViewPager;

    private FragmentStatePagerAdapter notificationsStatePagerAdapter;

    private Fragment fragmentOne;

    private Fragment fragmentTwo;

    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.layout_activity);
        // Setting up tabbed layouts.
        notificationsTabLayout = findViewById(R.id.notification_tabs);

        notificationsTabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
            @Override
            public void onTabSelected(TabLayout.Tab tab)
            {
                int position = tab.getPosition();
                currentFocussedTab = position;
                // Custom function to log click.
                if (position == 0) {
                  fragmentOne.logClick();
                } else {
                  fragmentTwo.logClick();
                }
            }

            @Override
            public void onTabUnselected(TabLayout.Tab tab)
            {
                int position = tab.getPosition();

                if (position == 0) {
                   fragmentOne.logUnselect();
                } else
                   fragmentTwo.logUnselect();
                }
            }

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

        // Set up view pager.
        notificationsViewPager = findViewById(R.id.notification_view_pager);
        notificationsViewPager.setCurrentItem(0);
        notificationsTabLayout.setupWithViewPager(notificationsViewPager);
        FragmentManager fragmentManager = getSupportFragmentManager();
        fragmentOne = new FragmentOne();
        fragmentTwo = new FragmentTwo();
        notificationsStatePagerAdapter = new FragmentStatePagerAdapter(fragmentManager,
            FragmentStatePagerAdapter.BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
            @NonNull
            @Override
            public Fragment getItem(int position)
            {
                return position == 0 ? fragmentOne : fragmentTwo;
            }

            @Override
            public int getCount()
            {
                // Two fixed tabs.
                return 2;
            }

            @Nullable
            @Override
            public CharSequence getPageTitle(int position)
            {
                return position == 0 ? getText(R.string.act_now_tab) : getText(R.string.others_tab);
            }


        };
        notificationsViewPager.setAdapter(notificationsStatePagerAdapter);
    }

    public void startNewActivity() {
        Intent intent = new Intent(this, MyNewActivity.class);
        startActivity(intent);
    }
}

Inside Fragment:

Put this inside your fragment's click function

...
MyActivity myActivity = getActivity()
if (myActivity != null)
  myActivity.startNewActivity()
...
Cory Roy
  • 5,379
  • 2
  • 28
  • 47