Я бы предложил использовать шаблон Singleton для обработки передачи данных между действиями.
Вы можете передать список по назначению putExtra()
или SharedPreferences
, но с Singleton
класс, это выглядит намного лучше и проще манипулировать вашими данными, потому что они инкапсулированы . Тем более в вашей ситуации, когда вы хотите сохранить состояния ваших уровней (например, когда они уже завершены).
Однако, если вы действительно настаиваете на использовании SharedPreferences
для сохранения списка, я предлагаю преобразовать его Json
с помощью Gson
. (Смотрите ниже мой ответ о том, как это реализовать.)
Как я уже сказал, я бы использовал шаблон Singleton
, чтобы избежать создания ненужного стандартного кода и инкапсулировать состояния уровней.
LevelManager class (синглтон)
final class LevelManager {
// constants
private static final String LEVELS_SHARED_PREFERENCES_NAME = "app_name.LEVELS";
// variables
private static LevelManager instance;
private List<Class> levels;
private SharedPreferences sharedPreferences;
private LevelManager(Context context) {
sharedPreferences =
context.getSharedPreferences(LEVELS_SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE);
levels = new ArrayList<>();
initializeList();
}
private void initializeList() {
// Initialize levels, ie. add levels that are not yet completed/passed
// Check in SharedPreferences if level has already been completed
boolean alreadyPassed;
alreadyPassed = sharedPreferences.getBoolean(Level1Activity.class.getSimpleName(), false);
if (!alreadyPassed) levels.add(Level1Activity.class);
alreadyPassed = sharedPreferences.getBoolean(Level2Activity.class.getSimpleName(), false);
if (!alreadyPassed) levels.add(Level2Activity.class);
alreadyPassed = sharedPreferences.getBoolean(Level3Activity.class.getSimpleName(), false);
if (!alreadyPassed) levels.add(Level3Activity.class);
alreadyPassed = sharedPreferences.getBoolean(Level4Activity.class.getSimpleName(), false);
if (!alreadyPassed) levels.add(Level4Activity.class);
alreadyPassed = sharedPreferences.getBoolean(Level5Activity.class.getSimpleName(), false);
if (!alreadyPassed) levels.add(Level5Activity.class);
}
static LevelManager getInstance(Context context) {
if (instance == null) {
instance = new LevelManager(context);
}
return instance;
}
Class getRandomLevel() {
if (levels.isEmpty()) {
return null; // Return null if all levels are already completed
}
Collections.shuffle(levels);
return levels.get(0);
}
void saveLevelState(Class levelClass, boolean passed) {
sharedPreferences.edit().putBoolean(levelClass.getSimpleName(), passed).apply();
if (passed) {
// Remove level from list if user passed it so that it won't
// be included in next levels
levels.remove(levelClass);
}
}
void reset() {
// Clears all entries in SharedPreferences and re-initialize list
sharedPreferences.edit().clear().apply();
initializeList();
}
}
Внутри onCreate в MainActivity
// Get LevelManager singleton instance
final LevelManager levelManager = LevelManager.getInstance(this);
Button startButton = findViewById(R.id.startButton);
startButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
// Get next random level
Class levelToStart = levelManager.getRandomLevel();
// If all levels are already completed
if (levelToStart == null) {
Toast.makeText(MainActivity.this, "All levels are completed!",
Toast.LENGTH_LONG).show();
return;
}
Intent intent = new Intent(MainActivity.this, levelToStart);
startActivity(intent);
}
});
// I added a new button to reset all levels
Button resetButton = findViewById(R.id.resetButton);
resetButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
// Use the method reset() from LevelManager to restart everything
levelManager.reset();
Toast.makeText(MainActivity.this, "All levels have been reset!",
Toast.LENGTH_LONG).show();
}
});
Внутри onCreate на каждом уровне деятельности
// Get LevelManager
final LevelManager levelManager = LevelManager.getInstance(this);
// I created two buttons to simulate pass and fail
Button pass = findViewById(R.id.passButton);
Button fail = findViewById(R.id.failButton);
pass.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
// Save state (Don't forget to change 'N' below)
levelManager.saveLevelState(LevelNActivity.class, true);
// Get next level
Class levelToStart = levelManager.getRandomLevel();
// Check if all are levels already completed
if (levelToStart == null) {
Toast.makeText(LevelNActivity.this, "Completed all levels",
Toast.LENGTH_LONG).show();
finish(); // Must implement to avoid going back to previous level (ie. Activity)
return;
}
Intent intent = new Intent(LevelNActivity.this, levelToStart);
startActivity(intent);
finish();
}
});
fail.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
finish();
}
});
Как вы можете видеть , вы можете просто использовать метод finish()
, если пользователь не прошел уровень, тогда как вы используете код ниже, чтобы перейти к следующему уровню:
// Get LevelManager
LevelManager levelManager = LevelManager.getInstance(this);
// Set that the user passed this level (Change 'N' to the current level we are in)
levelManager.saveLevelState(LevelNActivity.class, true);
// Get next level
Class nextLevel = levelManager.getRandomLevel();
// If all levels are completed then 'nextLevel' will be null
if (nextLevel == null) {
// ...
}
// Start next level and finish current
Intent intent = new Intent(this, nextLevel);
startActivity(intent);
finish();
Примечание: чтобы не вызывать finish()
явно при запуске На следующем уровне вы можете поместить android:noHistory="true"
в тег активности ваших уровней в файле манифеста.
Как сохранить список в SharedPreferences, преобразовав его Json с использованием Gson
Чтобы на самом деле использовать Gson
, вам нужно будет добавить implementation 'com.google.code.gson:gson:2.8.6'
в зависимости грейда вашего приложения .
Также существует проблема на Gson
при разборе Class
объектов на Json
: вам необходимо создать свой собственный сериализатор и десериализатор для этих объекты и зарегистрируйте его в GsonBuilder
.
ClassAdapter class (здесь мы создаем собственный настраиваемый сериализатор и десериализатор для объектов Class)
public class ClassAdapter implements JsonSerializer<Class>, JsonDeserializer<Class> {
@Override
public JsonElement serialize(Class src, Type typeOfSrc, JsonSerializationContext context) {
// Get our class 'src' name
return new JsonPrimitive(src.getName());
}
@Override
public Class deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
try {
// Get class
return Class.forName(json.getAsString());
} catch (ClassNotFoundException e) {
// If class could not be found or did not exists, handle error here...
e.printStackTrace();
}
return null;
}
}
Вот пример использования сохранения списка в SharedPreferences
с помощью Json
с использованием Gson
:
// Create new GsonBuilder and register our adapter for Class objects
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapter(Class.class, new ClassAdapter());
// Initialize our list of levels (ie. classes)
List<Class> classes = new ArrayList<>();
classes.add(Level1Activity.class);
classes.add(Level2Activity.class);
classes.add(Level3Activity.class);
classes.add(Level4Activity.class);
classes.add(Level5Activity.class);
// Create Gson from GsonBuilder and convert list to json
Gson gson = gsonBuilder.create();
String json = gson.toJson(classes);
// Save json to SharedPreferences
SharedPreferences sharedPreferences = getSharedPreferences("app_name", MODE_PRIVATE);
sharedPreferences.edit().putString("levels", json).apply();
и получения списка обратно:
// Retrieve json from SharedPreferences
SharedPreferences sharedPreferences = getSharedPreferences("app_name", MODE_PRIVATE);
String json = sharedPreferences.getString("levels", null);
// Handle here if json doesn't exist yet
if (json == null) {
// ...
}
// Create new GsonBuilder and register our adapter for Class objects
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapter(Class.class, new ClassAdapter());
// Create Gson from GsonBuilder and specify type of list
Gson gson = gsonBuilder.create();
Type type = new TypeToken<ArrayList<Class>>(){}.getType();
// Convert json to list
List<Class> classes = gson.fromJson(json, type);
Я надеюсь, что вы получили ценные советы для решения этой проблемы! И как всегда, удачного кодирования!