Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pass data from Activity to Fragment via FragmentStateAdapter

I am currently working on a personal project application for a scoring helper app. The aim is to use the app to keep track of points for each player and have high scores and other fancy features.

Right now, I have a Tabbed Activity which uses ViewPager2. For those familiar with it, you'll know a lot more than I do but what I've noticed is that I never instantiate my Fragment classes in the activity, only in the FragmentStateAdapter. In my activity, I only use two tabs, one for the game aspect and one for the points display. With this in mind, my activity class looks like this:

EightHoleActivity.java

package com.example.game;

import android.content.Intent;
import android.os.Bundle;
import com.google.android.material.tabs.TabLayout;
import com.google.android.material.tabs.TabLayoutMediator;
import androidx.appcompat.app.AppCompatActivity;
import androidx.viewpager2.widget.ViewPager2;

public class EightHoleActivity extends AppCompatActivity {

    private long gameID;
    private RoomDB database;

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

        Intent intent = getIntent();
        gameID = intent.getLongExtra("GameID", 0);
        database = RoomDB.getInstance(EightHoleActivity.this);

        ViewPager2 viewPager2 = findViewById(R.id.eight_hole_view_pager);
        viewPager2.setAdapter(new EightHolePagerAdapter(this));
        TabLayout tabLayout = findViewById(R.id.eight_hole_tabs);
        TabLayoutMediator tabLayoutMediator = new TabLayoutMediator(
                tabLayout, viewPager2, (tab, position) -> {
                    switch (position){
                        case 0: {
                            tab.setText("Current game");
                            tab.setIcon(R.drawable.ic_twotone_games_24);
                            break;
                        }
                        default: {
                            tab.setText("Scores");
                            tab.setIcon(R.drawable.ic_baseline_list_24);
                            break;
                        }
                    }
                }
        );
        tabLayoutMediator.attach();
    }
}

As you can see, I have a RoomDatabase which I use to store the data within the application. When I start this activity by intent, I pass the ID of the game that is being played. This is where I am having issue.

What I want to do:

I want to pass the variable gameID that I get from intent.getLongExtra("GameID", 0) all the way down to the EightHoleScoresFragment class.

I've had no luck with the use of Bundle and putParcelableArrayList()

Here are the other classes that I use. Note that the class Participants.java is what I want to extract with the gameID variable.

Participants.java

package com.example.game;

import android.os.Parcel;
import android.os.Parcelable;

import androidx.annotation.Nullable;
import androidx.room.ColumnInfo;
import androidx.room.Entity;
import androidx.room.ForeignKey;
import androidx.room.PrimaryKey;

import java.util.Objects;

@Entity(tableName = "participants", foreignKeys = {
        @ForeignKey(entity = Player.class, parentColumns = "id", childColumns = "player_id", onDelete = ForeignKey.CASCADE),
        @ForeignKey(entity = Game.class, parentColumns = "id", childColumns = "game_id", onDelete = ForeignKey.CASCADE)})
public class Participant implements Parcelable {
    @PrimaryKey(autoGenerate = true)
    private long id;

    @ColumnInfo(name = "player_id", index = true)
    private long playerID;

    @ColumnInfo(name = "game_id", index = true)
    private long gameID;

    @ColumnInfo(name = "score")
    private int score;

    protected Participant(Parcel in) {
        id = in.readLong();
        playerID = in.readLong();
        gameID = in.readLong();
        score = in.readInt();
    }

    public static final Creator<Participant> CREATOR = new Creator<Participant>() {
        @Override
        public Participant createFromParcel(Parcel in) {
            return new Participant(in);
        }

        @Override
        public Participant[] newArray(int size) {
            return new Participant[size];
        }
    };

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public long getPlayerID() {
        return playerID;
    }

    public void setPlayerID(long playerID) {
        this.playerID = playerID;
    }

    public long getGameID() {
        return gameID;
    }

    public void setGameID(long gameID) {
        this.gameID = gameID;
    }

    public int getScore() {
        return score;
    }

    public void setScore(int score) {
        this.score = score;
    }

    public Participant(long playerID, long gameID){
        this.playerID = playerID;
        this.gameID = gameID;
    }

    @Override
    public boolean equals(@Nullable Object obj) {
        if (this == obj) return true;
        if (!(obj instanceof Participant)) return false;

        Participant participant = (Participant) obj;
        if (id != participant.id) return false;
        return Objects.equals(score, participant.score) || Objects.equals(playerID, participant.playerID) || Objects.equals(gameID, participant.gameID);
    }

    @Override
    public int hashCode() {
        int result = (int) id;
        result = (int) (31 * result + playerID + gameID + score);
        return result;
    }

    @Override
    public String toString() {
        return "Participant{" +
                "ID=" + id +
                ", PlayerID='" + playerID + '\'' +
                ", GameID='" + gameID + '\'' +
                ", Score='" + score + '\'' +
                '}';
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeLong(id);
        dest.writeLong(playerID);
        dest.writeLong(gameID);
        dest.writeInt(score);
    }
}

EightHolePagerAdapter.java

package com.example.game;

import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.viewpager2.adapter.FragmentStateAdapter;

public class EightHolePagerAdapter extends FragmentStateAdapter {

    public EightHolePagerAdapter(@NonNull FragmentActivity fragmentActivity) {
        super(fragmentActivity);
    }

    @NonNull
    @Override
    public Fragment createFragment(int position) {
        switch (position){
            case 0:
                return new EightHoleGameFragment();
            default:
                return new EightHoleScoresFragment();
        }
    }

    @Override
    public int getItemCount() {
        return 2;
    }
}

EightHoleScoresFragment.java

package com.example.game;

import android.os.Bundle;
import androidx.fragment.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ListView;
import java.util.ArrayList;

public class EightHoleScoresFragment extends Fragment {

    private ArrayList<Participant> participants = new ArrayList<Participant>();

    public EightHoleScoresFragment() {
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_eight_hole_scores, container, false);
        ListView listView = (ListView) view.findViewById(R.id.eight_hole_current_scores_list);

        ScoresAdapter adapter = new ScoresAdapter(getActivity(), R.layout.scores_adapter_layout, participants);
        listView.setAdapter(adapter);
        return view;
    }
}

I apologize for the digest amount of code. I feel it's all necessary to be able to understand what is going on. I've tried to use Bundle as I said, following various answers on the site but none have worked. I wonder if it would be at all possible to directly pass the contents of the intent to the EightHoleScoresFragment with a method that links back to the parents EightHoleActivity but I'm not sure...

Thanks in advance!

like image 378
Maxime Michel Avatar asked Oct 17 '25 14:10

Maxime Michel


2 Answers

Please try this approach

In Activity:

Pass gameId as extra argument and receive in Adapter

viewPager2.setAdapter(new EightHolePagerAdapter(this, gameID));

In ViewPagerAdapter:

EightHoleScoresFragment takes gameId and calls fragment newInstance

public class EightHolePagerAdapter extends FragmentStateAdapter {

    private long gameId;

    public EightHolePagerAdapter(@NonNull FragmentActivity fragmentActivity, long gameId) {
        super(fragmentActivity);
        this.gameId = gameId;
    }

    @NonNull
    @Override
    public Fragment createFragment(int position) {
        switch (position){
            case 0:
                return new EightHoleGameFragment();
            default:
                return EightHoleScoresFragment.newInstance(gameId);
        }
    }

    @Override
    public int getItemCount() {
        return 2;
    }
}

In your Fragment :

Receive gameId arguments in fragment this way.

public class EightHoleScoresFragment extends Fragment {

    private static final String ARG_GAME_ID = "game_id";
    private long mGameId;

    public static EightHoleScoresFragment newInstance(long gameId) {
        EightHoleScoresFragment fragment = new EightHoleScoresFragment();
        Bundle args = new Bundle();
        args.putLong(ARG_GAME_ID, gameId);
        fragment.setArguments(args);
        return fragment;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (getArguments() != null) {
            mGameId = getArguments().getLong(ARG_GAME_ID);
        }
    }

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

        Log.d("EightHoleScoresFragment", "Game Id = " + mGameId);
    }
}
like image 74
Rajasekhar Avatar answered Oct 20 '25 03:10

Rajasekhar


There's a far less complicated way to do this. Rajasekhar's answer is certainly the "correct" way to do it... but it's also possible without getting into bundles and arguments.

EightHoleActivity.java This gets the variable into your adapter.

viewPager2.setAdapter(new EightHolePagerAdapter(this, gameID));

EightHolePagerAdapter Once it's in your adapter... the variable is available to all your fragments.

public class EightHolePagerAdapter extends FragmentStateAdapter {

    private long gameId;

    public EightHolePagerAdapter(@NonNull FragmentActivity fragmentActivity, long gameId) {
        super(fragmentActivity);
        this.gameId = gameId;
    }

    @NonNull
    @Override
    public Fragment createFragment(int position) {
        switch (position){
            case 0:
                return new EightHoleGameFragment();
            default:
                return EightHoleScoresFragment.sendVariable(gameId);
        }
    }

    @Override
    public int getItemCount() {
        return 2;
    }
}

EightHoleScoresFragment at this point all you have to do is set a static variable and you're DONE. You don't need to create a bundle or retrieve from that bundle because the data is already there.

public class EightHoleScoresFragment extends Fragment {

    private static final String ARG_GAME_ID = "game_id";
    static long gameId2;

    public static EightHoleScoresFragment sendVariable(long gameId) {
        EightHoleScoresFragment fragment = new EightHoleScoresFragment();
        gameId2 = gameId;
        return fragment;
    }

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

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

        Log.d("EightHoleScoresFragment", "Game Id = " + gameId2);
    }
}

And there you have it. No Bundles or Arguments required. A few less lines of code. I would say that having a bundle would be useful for moving the score elsewhere... but as long as you just need to move data from an activity to a fragment then... once it's in the adapter... you're done.

like image 23
mystic cola Avatar answered Oct 20 '25 04:10

mystic cola



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!