FET
FET

Reputation: 942

Have a single instance of a Class

Overview

I have a game which uses a ViewPager to display many mines which the player can purchase.


Problem

I have created a User Class in order to help me store all user's info in there.

One of these values is gold, which is used to purchase mines. Fact is, I don't know where to create the user (suspect on the MainActivity) and how to access this new user's info from the MineFragment, which is the code of each page in the ViewPager.

Note: I don't want to pass a User object to a fragment or any other classes. I want to be able to make an instance of a single User and then be able to access all of this user's data from anywhere in my code, but in this case, I care about accessing them from the Fragment (Minefragment).


User

public class User {

private String mName;
private int mGold;
private int mExperience;
private int mExperienceLevel;

public User(String name){
    mName = name;
    mGold = 0;
    mExperience = 0;
    mExperienceLevel = 1;
}

// Getters & Setters
[...]

}


MineFragment

public class MineFragment extends Fragment {
// Store instance variables
[...]

private User mUser;

private Button mineUnlockButton;
private View overlay;

[...]

// Store instance variables based on arguments passed
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    [...]

    // HARD CODED VALUES
    mUser = new User("Leonardo");
    mUser.setGold(2000);
}

// Inflate the view for the fragment based on layout XML
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    View view = inflater.inflate(mLayout, container, false);

    [...]

    // Unlock Button
    mineUnlockButton.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {

            if(mUser.getGold() >= mUnlockCost) {
                overlay.setVisibility(View.GONE); // Remove overlay and button
                mineUnlockButton.setVisibility(View.GONE);
                System.out.println("Gold: " + mUser.getGold() + " | Cost: " + mUnlockCost);
                mUser.setGold(mUser.getGold() - mUnlockCost); // Update user's gold
                System.out.println("Gold: " + mUser.getGold());
                System.out.println("### Mine Purchased ###");

            } else { // Not enough money
                Toast.makeText(getContext(), "Not enough money to purchase", Toast.LENGTH_SHORT).show();
            }
        }
    });

    return view;
}

}


MainFragment

public class MainActivity extends AppCompatActivity {

FragmentPagerAdapter adapterViewPager;
ViewPager viewPager;

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

    viewPager = (ViewPager) findViewById(R.id.vpPager);
    adapterViewPager = new MineAdapter(getSupportFragmentManager());
    viewPager.setAdapter(adapterViewPager);

}

}

Here's the project on GitHub, where you can find the MainActivity, the User class, the MineFragment and some extra stuff if you need.

Upvotes: 0

Views: 674

Answers (3)

Hitesh Sahu
Hitesh Sahu

Reputation: 45072

You need to use Singleton pattern to store your Data in one Single place.

What I got from your code is there are number of levels in your game and each level requires some minimum number of golds to unlock them.

This is how it look like for test data with 200 gold:-

enter image description here enter image description here

Model classes

Lets start with Level class -lavel have name and minimum gold threashold

    public class LevelModel {

        private String levelName;
        private int unlockCost;

        public String getLevelName() {
            return levelName;
        }

        public int getUnlockCost() {
            return unlockCost;
        }

        public LevelModel(String levelName, int unlockCost) {
            this.levelName = levelName;
            this.unlockCost = unlockCost;
        }
    }

Next will be User class all fields are self explanatory

public class User {
    private String userName;
    private int gold;
    private int experience;
    private int experienceLevel = 1;

    /**
     * @param userName
     * @param gold
     * @param experience
     * @param experienceLevel
     */
    public User(String userName, int gold, int experience, int experienceLevel) {
        this.userName = userName;
        this.gold = gold;
        this.experience = experience;
        this.experienceLevel = experienceLevel;
    }

    //Setters

    public void setGold(int gold) {
        this.gold = gold;
    }

    public void setExperience(int experience) {
        this.experience = experience;
    }

    public void setExperienceLevel(int experienceLevel) {
        this.experienceLevel = experienceLevel;
    }

    //Getters

    public String getUserName() {
        return userName;
    }

    public int getGold() {
        return gold;
    }

    public int getExperience() {
        return experience;
    }

    public int getExperienceLevel() {
        return experienceLevel;
    }
}

Now You need a single place to store, update and access your game data here this class will act as Singleton class to hold all your game data.

public class CenterRepository {

    public void setCurrentUser(User currentUser) {
        this.currentUser = currentUser;
    }

    private User currentUser;
    ArrayList<LevelModel> listOfLavels = new ArrayList<>();
    private static CenterRepository singletonInstance;

    private CenterRepository() {
    }

    public static CenterRepository getSingletonInstance() {
        if (null == singletonInstance) {
            singletonInstance = new CenterRepository();
        }
        return singletonInstance;
    }

    public User getCurrentUser() {
        return currentUser;
    }


    public ArrayList<LevelModel> getListOfLavels() {
        return listOfLavels;
    }
}

Now For the second Part how to access and update data from ViewPager . I have enhanced your view pager to use view instead of fragment

MineAdapter Updated**

package com.fet.minebeta.ui;

import android.content.Context;
import android.support.v4.view.PagerAdapter;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.TextView;
import android.widget.Toast;

import com.fet.minebeta.R;
import com.fet.minebeta.data.CenterRepository;

/**
 * Created by FET on 08/09/2016.
 * All rights reserved.
 * Please contact @[email protected]
 */
public class MineAdapter extends PagerAdapter {
    private Context mContext;
    private LayoutInflater mLayoutInflater;

    public MineAdapter(Context context) {
        mContext = context;
        mLayoutInflater = (LayoutInflater) mContext
                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    }

    @Override
    public int getCount() {

        //Fill Data directly from Repository
        return CenterRepository.getSingletonInstance().getListOfLavels().size();
    }

    @Override
    public boolean isViewFromObject(View view, Object object) {
        return view == ((FrameLayout) object);
    }

    @Override
    public Object instantiateItem(ViewGroup container, final int position) {

        final View itemView = mLayoutInflater.inflate(R.layout.carousal_page, container,
                false);

        switch (position) {
            case 0:
                itemView.setBackgroundResource(R.color.iron);
                break;
            case 1:
                itemView.setBackgroundResource(R.color.coal);
                break;
            case 2:
                itemView.setBackgroundResource(R.color.gold);
                break;
        }

        //Mine Name
        ((TextView) itemView.findViewById(R.id.mineName)).setText(
                CenterRepository.getSingletonInstance().getListOfLavels().get(position).getmName());

        //Mine Cost
        ((TextView) itemView.findViewById(R.id.mineCost)).setText("" +
                CenterRepository.getSingletonInstance().getListOfLavels().get(position).getUnlockCost());

        //Mine Cost
        ((TextView) itemView.findViewById(R.id.mineDropRate)).setText("" +
                CenterRepository.getSingletonInstance().getListOfLavels().get(position).getDropRate());

        //Mineral Name
        ((TextView) itemView.findViewById(R.id.mineMineral)).setText(
                CenterRepository.getSingletonInstance().getListOfLavels().get(position).getMineral().getName());

        //Mineral Drop Rate
        ((TextView) itemView.findViewById(R.id.mineDropRate)).setText("" +
                CenterRepository.getSingletonInstance().getListOfLavels().get(position).getMineral().getValue());

        // Unlock Button
        itemView.findViewById(R.id.unlockButton).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                if (CenterRepository.getSingletonInstance().getCurrentUser().getGold() >=
                        CenterRepository.getSingletonInstance().getListOfLavels().get(position).getUnlockCost()) {

                    //If User has more gold than cost to unlock hide lock image and buy it

                    CenterRepository.getSingletonInstance().getCurrentUser().setGold(
                            CenterRepository.getSingletonInstance().getCurrentUser().getGold()
                                    - CenterRepository.getSingletonInstance().getListOfLavels().get(position).getUnlockCost()); // Update user's gold

                    Toast.makeText(mContext,
                            "Reduced " + CenterRepository.getSingletonInstance().getListOfLavels().get(position).getUnlockCost() +
                                    "\n Updated Gold " + CenterRepository.getSingletonInstance()
                                    .getCurrentUser().getGold(), Toast.LENGTH_LONG).show();

                } else {

                    // Not enough money
                    Toast.makeText(mContext, "Not enough money to purchase You need " +
                            (CenterRepository.getSingletonInstance().getListOfLavels().get(position).getUnlockCost()
                                    - CenterRepository.getSingletonInstance().getCurrentUser().getGold()) + "More", Toast.LENGTH_SHORT).show();
                }

            }
        });

        container.addView(itemView);

        return itemView;
    }

    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        container.removeView((FrameLayout) object);
    }
}

One thing to note here carousal page have FrameLayout as rootlayout . if you plan to use any other update addview and remove view function accordingly

carousal_page.xml **updated you dont need separate layouts for each mineral

<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <TextView
        android:id="@+id/mineName"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginStart="@dimen/activity_horizontal_margin"
        android:layout_weight="1"
        android:gravity="center_vertical"
        android:text="COAL MINE"
        android:textColor="@android:color/white"
        android:textSize="25sp" />


    <TextView
        android:id="@+id/mineCost"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_weight="5"
        android:gravity="center"
        android:text="1000"
        android:textColor="@android:color/white"
        android:textSize="50sp" />

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_weight="3"
        android:paddingEnd="100dp"
        android:paddingStart="100dp">

        <TextView
            android:id="@+id/mineMineral"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentStart="true"
            android:layout_alignTop="@+id/mineDropRate"
            android:text="COAL"
            android:textAlignment="center"
            android:textColor="@android:color/white"
            android:textSize="25sp" />

        <TextView
            android:id="@+id/mineDropRate"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentEnd="true"
            android:layout_centerVertical="true"
            android:text="1"
            android:textAlignment="center"
            android:textColor="@android:color/white"
            android:textSize="25sp" />

    </RelativeLayout>
</LinearLayout>

<RelativeLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <Button
        android:id="@+id/unlockButton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_centerVertical="true"
        android:text="Unlock" />

</RelativeLayout>

Model class is so loosely coupled that you can move it anywhere and it work just fine.

Activity Class with test Data Updated**

public class MainActivity extends AppCompatActivity {

    PagerAdapter adapterViewPager;
    ViewPager viewPager;

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

        //  Add Test User from Activity
        CenterRepository.getSingletonInstance().setCurrentUser(new User("FET", 200, 20, 10));

        //Add Test Mines
        CenterRepository.getSingletonInstance().getListOfLavels().add(new Mine("Iron", new Mineral("Iron Mineral", 1), 100, 2));
        CenterRepository.getSingletonInstance().getListOfLavels().add(new Mine("Coal", new Mineral("Coal Mineral", 3), 200, 2));
        CenterRepository.getSingletonInstance().getListOfLavels().add(new Mine("Gold", new Mineral("Gold Mineral", 2), 300, 2));

        viewPager = (ViewPager) findViewById(R.id.vpPager);
        viewPager.setAdapter(new MineAdapter(this));

        Toast.makeText(getApplicationContext(), "Current Credits " + CenterRepository.getSingletonInstance()
                .getCurrentUser().getGold(), Toast.LENGTH_LONG).show();


    }


}

Upvotes: 2

obwan02
obwan02

Reputation: 113

In Your User Class Create A Static User Which Represents The Person Who is Playing The Game:

    public class User {

    public static User user = new User("Name Here");

    private String mName;
    private int mGold;
    private int mExperience;
    private int mExperienceLevel;

    public User(String name){
        mName = name;
        mGold = 0;
        mExperience = 0;
        mExperienceLevel = 1;
    }
    // Getters & Setters
    [...]

Access it with eg:

    User.user.setGold(User.user.getGold() + 1);

Upvotes: 0

Fred Grott
Fred Grott

Reputation: 3476

It is not a bad start...

Since that will probably your only game initialization class, you need to make it singleton. One of the tricks in java is that if you change the class to enum and make a field called INSTANCE

to use in your MainActivity in your onCreate call

User.INSTANCE

That will initialize the game with the default values you have in the User class. Than you have access to all the methods of the User class and fields.

Upvotes: 1

Related Questions