1

I have a ViewPager and TabLayout setup with 4 fragments, like any modern social app. I've tried, tried, tried, but I couldn't find an answer to my problem. Within one of the tabs, I want to navigate from the fragment to another fragment but instead of navigating, it just puts it on top and I can still interact with the previous fragment. It's not replacing, it's simply just layering it on top.

Code:

// Chat fragment : Inside the onCreateView fun
this.loadConvos({
            chats ->
            this.chatsArray = chats
            this.chatsArray.sortBy { it.timestamp}
            this.chatsArray.reverse()

            listView.adapter = ChatBaseAdapter(this.chatsArray, context)
            listView.setOnItemClickListener {
                parent, view, position, id ->
                this.chatID = this.chatsArray[position].chatID!!
                Toast.makeText(context, "Position Clicked: " + position, Toast.LENGTH_SHORT).show()

                childFragmentManager
                        .beginTransaction()
                        .replace(R.id.chatFragmentLayout, MessagesFragment())
                        .addToBackStack(null)
                        .commit()
            }
        }, {
            error ->
            print(error)
        })

The function simply just loads the listView with chat details the user has which works and I can tap and the Toast will give me the cell position but when it commit()s it just layers the MessagesFragment() on top of the ChatsFragment(). I'd also like to know how to pass information to the next fragment. This method I don't see a way to pass data, unlike the regular Bundle/Intent way I know of.

XML Chat on the viewpager:

<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"

android:id="@+id/chatFragmentLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"

tools:context="io.jedi.jedi.fragments.ChatFragment">

<ListView
    android:id="@+id/listView"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

</FrameLayout>

XML Messages

<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"

android:id="@+id/messagesFragmentLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"

tools:context="io.jedi.jedi.fragments.MessagesFragment">

<TextView
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:text="Messages" />

</FrameLayout>
Mohamed Mohamed
  • 3,965
  • 3
  • 23
  • 40
  • I think what you doing is correct, you may just need to set the Parent Layout of your MessagesFragment to be clickable "android:clickable="true"". That will prevent you to interact with the previous fragment. If you want to pass params you can use Fragment.setArguments with a Bundle with the params. – velval Feb 05 '18 at 22:53
  • @velval But there's no slide transition. It just shows on top like a fade-in and I can see both fragments at the same time. For example the user taps on the chat cell and I want it to go to the messages view like the text app – Mohamed Mohamed Feb 05 '18 at 23:06
  • If you want to add a "slide transition" you will need to add the custom animation yourself using FragmentTransaction.setCustomAnimations(int enter, int exit, int popEnter, int popExit) where the enter, exit, popEnter and popExit int params are the animation resource IDs from R.anim.ID that you need to create so the previous fragment slides to the left when existing and the new one slides from the right to left when entering. check this: https://stackoverflow.com/a/5151774/3792636 – velval Feb 06 '18 at 05:40

2 Answers2

0

If the Fragment you are trying to switch to is one within your ViewPager, you shouldn't manually change it via FragmentTransaction. You should instead call setCurrentItem() on the ViewPager (see this link in the online documentation). If this call is via one of those Fragments, you will also need to have some form of interface to communicate between the parent Activity and that Fragment(see here).

As for your second question, you will have to think through this some more if it is your intention to have a Fragment in your ViewPager that is constantly changing. If otherwise, you should take a look at a New Instance type pattern such as is discussed in this post.

Bryan Dormaier
  • 800
  • 6
  • 10
  • It's not one of the tabs, it like a user taps on the chat row then it should transition to the messages fragment. – Mohamed Mohamed Feb 05 '18 at 23:07
  • Can you post the layout for your chat fragment? I need to know how chatFragmentLayout relates to the rest of your Fragment elements. – Bryan Dormaier Feb 05 '18 at 23:09
  • By default, your new `Fragment` is not going to replace, because it is being committed to the `ChildFragmentManager` and not the parent. If you are seeing through below and are dedicated to this design, you'll need to set the parent layout of your child fragment to clickable as mentioned above, and you'll need to set a background. – Bryan Dormaier Feb 05 '18 at 23:12
  • No I don't want to see through the design, I want it to behave like any text app. You press on the chat -> see messages and reply – Mohamed Mohamed Feb 05 '18 at 23:16
  • I understand, I'm telling you given your current pattern you will have to set a background and make it clickable to block the layout below. I would launch a separate activity for Chat instead of making it a child fragment, but if you're going to go with your design, you have to take these steps, because you are replacing on the CHILD fragment manager and not the parent activity (in effect, you are replacing nothing). – Bryan Dormaier Feb 05 '18 at 23:19
  • How do I change the way to how you're saying it. I come from an iOS background so I was looking for something like `performSegue()` in swift. And the closest thing in android is `startActivity()` – Mohamed Mohamed Feb 06 '18 at 01:12
  • Yeah, you can create a new `Activity` via `Intent` which will load and leave the previous `Activity` as resumable on a back click. Not using `Fragments` except when necessary will also reduce complications having to do with Lifecycle interactions between activities and fragments. – Bryan Dormaier Feb 06 '18 at 17:54
0

Fragments have void setArguments(Bundle) and Bundle getArguments() methods that you can use the same way you use an intent for an Activity.

For example, from the official documentation.

public class CountingFragment extends Fragment {

    private int mNum;

    public static CountingFragment newInstance(int num) {
        CountingFragment f = new CountingFragment();

        Bundle args = new Bundle();
        args.putInt("num", num);
        f.setArguments(args);

        return f;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mNum = getArguments() != null ? getArguments().getInt("num") : 1;
    }
}

Or in Kotlin:

class CountingFragment : Fragment() {

    companion object {
        fun newInstance(number: Int): CountingFragment {
            return CountingFragment().apply {
                arguments = Bundle().apply {
                    putInt("number", number)
                }
            }
        }
    }

    private var number: Int = 0

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        number = arguments?.getInt("number") ?: 0
    }

}
James McCracken
  • 15,488
  • 5
  • 54
  • 62