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!
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);
}
}
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With