Android Room DB Android中的实时数据观察者问题

问题描述 投票:0回答:1

我在使用实时数据观察器观察3个不同的列表以在一个回收者视图中查看它们时遇到问题。这是场景:在ListsFragment中,我具有带有3个选项卡(WANT,PLAYING,PLAYED)和1个回收站视图的TabLayout。在不同的选项卡上,单击我从数据库加载不同的游戏列表,并在“回收者”视图中以“实时数据”观察者模式显示它们。在此之前,一切正常。当我按下一个游戏的“删除”按钮时,会发生问题,数据库已更新,但在回收站视图中加载的列表错误,而不是所选选项卡的列表(可能因为有3个活动的观察者),因此屏幕上的数据已更新游戏错误。

行为的Gif:https://gfycat.com/showyembarrassedbarasingha

[谁能帮我,我尝试了许多解决方案,但没有任何帮助,我被困了超过1个月。

这是代码。片段:

public class ListsFragment extends Fragment implements VisitedGameInterface {
    public static final String TAG = ListsFragment.class.getSimpleName();

    private static final int WANT = 0;
    private static final int PLAYING = 1;
    private static final int PLAYED = 2;

    private int selectedTab = WANT;

    private GameViewModel gameViewModel;
    private GameVisitedAdapter gameAdapter;
    private TextView noGamesTV;

    public ListsFragment() {
        // Required empty public constructor
    }

    public static ListsFragment newInstance(Bundle bundle) {
        ListsFragment fragment = new ListsFragment();
        fragment.setArguments(bundle);
        return fragment;
    }

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

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        View view = inflater.inflate(R.layout.fragment_lists, container, false);

        TabLayout listsTabLayout = view.findViewById(R.id.lists_tab_layout);
        RecyclerView listsRecyclerView = view.findViewById(R.id.lists_recycler_view);
        noGamesTV = view.findViewById(R.id.no_games_tv);

        listsTabLayout.addTab(listsTabLayout.newTab().setText(getString(R.string.want)), true);
        listsTabLayout.addTab(listsTabLayout.newTab().setText(getString(R.string.playing)));
        listsTabLayout.addTab(listsTabLayout.newTab().setText(getString(R.string.played)));
        listsTabLayout.setTabGravity(TabLayout.GRAVITY_FILL);

        listsRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity(), RecyclerView.VERTICAL, false));
        listsRecyclerView.addItemDecoration(new DividerItemDecoration(getActivity(), DividerItemDecoration.VERTICAL));
        gameAdapter = new GameVisitedAdapter(getActivity(), this);
        listsRecyclerView.setAdapter(gameAdapter);

        gameViewModel = ViewModelProviders.of(this).get(GameViewModel.class);
        gameViewModel.getWantedGames().observe(this, new Observer<List<Game>>() {
            @Override
            public void onChanged(List<Game> wantedGames) {
                if (wantedGames.size() != 0) {
                    noGamesTV.setVisibility(View.GONE);
                }else {
                    noGamesTV.setText(R.string.no_wanted_games);
                    noGamesTV.setVisibility(View.VISIBLE);
                }
                gameAdapter.setGames(wantedGames);
                gameAdapter.notifyDataSetChanged();
            }
        });

        listsTabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
            @Override
            public void onTabSelected(TabLayout.Tab tab) {
                if (getActivity() != null && tab.getText() != null) {
                    if (tab.getText().equals("WANT")) {
                        selectedTab = tab.getPosition();
                        gameViewModel.getWantedGames().observe(getActivity(), new Observer<List<Game>>() {
                            @Override
                            public void onChanged(List<Game> wantedGames) {
                                if (wantedGames.size() != 0) {
                                    noGamesTV.setVisibility(View.GONE);
                                } else {
                                    noGamesTV.setText(R.string.no_wanted_games);
                                    noGamesTV.setVisibility(View.VISIBLE);
                                }
                                gameAdapter.setGames(wantedGames);
                                gameAdapter.notifyDataSetChanged();
                            }
                        });
                    } else if (tab.getText().equals("PLAYING")) {
                        selectedTab = tab.getPosition();
                        gameViewModel.getPlayingGames().observe(getActivity(), new Observer<List<Game>>() {
                            @Override
                            public void onChanged(List<Game> playingGames) {
                                if (playingGames.size() != 0) {
                                    noGamesTV.setVisibility(View.GONE);
                                } else {
                                    noGamesTV.setText(R.string.no_playing_games);
                                    noGamesTV.setVisibility(View.VISIBLE);
                                }
                                gameAdapter.setGames(playingGames);
                                gameAdapter.notifyDataSetChanged();
                            }
                        });
                    } else if (tab.getText().equals("PLAYED")){
                        selectedTab = tab.getPosition();
                        gameViewModel.getPlayedGames().observe(getActivity(), new Observer<List<Game>>() {
                            @Override
                            public void onChanged(List<Game> playedGames) {
                                if (playedGames.size() != 0) {
                                    noGamesTV.setVisibility(View.GONE);
                                } else {
                                    noGamesTV.setText(R.string.no_played_games);
                                    noGamesTV.setVisibility(View.VISIBLE);
                                }
                                gameAdapter.setGames(playedGames);
                                gameAdapter.notifyDataSetChanged();
                            }
                        });
                    }
                }
            }

            @Override
            public void onTabUnselected(TabLayout.Tab tab) {

            }

            @Override
            public void onTabReselected(TabLayout.Tab tab) {

            }
        });
        return view;
    }

    @Override
    public void onGameDelete(final Game game) {
        if (selectedTab == WANT){
            game.setWanted(false);
            showSnackBar(game, WANT);
        } if (selectedTab == PLAYING){
            game.setPlaying(false);
            showSnackBar(game, PLAYING);
        } if (selectedTab == PLAYED){
            game.setPlayed(false);
            showSnackBar(game, PLAYED);
        }

        gameViewModel.update(game);
        gameAdapter.notifyDataSetChanged();
    }

    @Override
    public void onVisitedGameClick(Game game) {
        Intent intent = new Intent(getActivity(), GameActivity.class);
        intent.putExtra("game", game);
        startActivity(intent);
    }

    private void showSnackBar(final Game snackGame, final int selectedTab){
        Snackbar snackbar = Snackbar.make(Objects.requireNonNull(getView()), snackGame.getName() + " deleted", Snackbar.LENGTH_LONG)
                .setAction(R.string.undo, new View.OnClickListener() {
                    @Override
                    public void onClick(View view) {
                        if (selectedTab == WANT) snackGame.setWanted(true);
                        if (selectedTab == PLAYING) snackGame.setPlaying(true);
                        if (selectedTab == PLAYED) snackGame.setPlayed(true);

                        gameViewModel.insert(snackGame);
                    }
                });
        snackbar.show();
    }
}

ListsFragment的XML

<RelativeLayout
    android:id="@+id/lists_layout" xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:orientation="vertical"
    tools:context=".fragment.ListsFragment">

    <com.google.android.material.tabs.TabLayout
        android:id="@+id/lists_tab_layout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:tabSelectedTextColor="@color/button_blue"/>

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/lists_recycler_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_below="@id/lists_tab_layout"/>

    <TextView
        android:id="@+id/no_games_tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        tools:text="No games at this moment"
        android:textColor="@color/text_black"
        android:visibility="invisible"/>

</RelativeLayout>

适配器:

public class GameVisitedAdapter extends RecyclerView.Adapter<GameVisitedAdapter.GameVisitedViewHolder> {

    private Context context;
    private LayoutInflater inflater;
    private List<Game> games = new ArrayList<>();

    private VisitedGameInterface visitedGameInterface;

    public GameVisitedAdapter(Context context, VisitedGameInterface visitedGameInterface){
        this.context = context;
        inflater = LayoutInflater.from(context);
        this.visitedGameInterface = visitedGameInterface;
    }

    public void setGames(List<Game> games){
        this.games = games;
    }

    @NonNull
    @Override
    public GameVisitedViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = inflater.inflate(R.layout.item_game_visited, parent, false);
        return new GameVisitedViewHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull GameVisitedViewHolder holder, int position) {
        Game game = games.get(position);

        if (game.getCover() != null) {
            Glide
                    .with(context)
                    .load(context.getString(R.string.cover_url) + game.getCover().getImage_id() + ".jpg")
                    .centerCrop()
                    .placeholder(context.getResources().getDrawable(R.drawable.placeholder))
                    .into(holder.gameCover);
        }

        holder.gameTitle.setText(game.getName());

        SimpleDateFormat formatter = new SimpleDateFormat("dd MMM yyyy");
        String dateString = formatter.format((new Date((long) game.getFirst_release_date() * 1000)));
        holder.gameYear.setText(dateString);
    }

    @Override
    public int getItemCount() {
        return games.size();
    }

    class GameVisitedViewHolder extends RecyclerView.ViewHolder {

        private ImageView gameCover, gameDeleteButton;
        private TextView gameTitle, gameYear;

        GameVisitedViewHolder(@NonNull View itemView) {
            super(itemView);

            gameCover = itemView.findViewById(R.id.game_cover);
            gameTitle = itemView.findViewById(R.id.game_name);
            gameYear = itemView.findViewById(R.id.game_release_year);
            gameDeleteButton = itemView.findViewById(R.id.game_delete_button);

            gameDeleteButton.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    visitedGameInterface.onGameDelete(games.get(getAdapterPosition()));
                }
            });

            itemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    visitedGameInterface.onVisitedGameClick(games.get(getAdapterPosition()));
                }
            });
        }
    }
}

ViewModel

public class GameViewModel extends AndroidViewModel {

    private GameRepository gameRepository;

    public GameViewModel(@NonNull Application application) {
        super(application);

        gameRepository = new GameRepository(application);
    }

    public void insert(Game game){
        gameRepository.insert(game);
    }

    public void update(Game game){
        gameRepository.update(game);
    }

    public void deleteGame(Game game){
        gameRepository.delete(game);
    }

    public void deleteAll(){
        gameRepository.deleteAll();
    }

    public void DeleteVisitedGames(){ gameRepository.deleteVisitedGames();}


    public LiveData<List<Game>> getWantedGames(){
        return gameRepository.getWantedGames();
    }

    public LiveData<List<Game>> getPlayingGames(){
        return gameRepository.getPlayingGames();
    }

    public LiveData<List<Game>> getPlayedGames(){
        return gameRepository.getPlayedGames();
    }

    public LiveData<List<Game>> getVisitedGames(){
        return gameRepository.getVisitedGames();
    }

    public LiveData<List<Game>> getAllGamesFromDB(){
        return gameRepository.getAllGamesFromDB();
    }
}

存储库:

public class GameRepository {

    private GameDao gameDao;

    private LiveData<List<Game>> wantedGames;
    private LiveData<List<Game>> playingGames;
    private LiveData<List<Game>> playedGames;
    private LiveData<List<Game>> visitedGames;
    private LiveData<List<Game>> allGamesFromDB;

    public GameRepository(Application application){
        GameDatabase gameDatabase = GameDatabase.getInstance(application);
        gameDao = gameDatabase.gameDao();
        wantedGames = gameDao.getWantedGames();
        playingGames = gameDao.getPlayingGames();
        playedGames = gameDao.getPlayedGames();
        visitedGames = gameDao.getVisitedGames();
        allGamesFromDB = gameDao.getAllGamesFromDB();
    }

    public LiveData<List<Game>> getWantedGames(){
        return wantedGames;
    }

    public LiveData<List<Game>> getPlayingGames(){
        return playingGames;
    }

    public LiveData<List<Game>> getPlayedGames(){
        return playedGames;
    }

    public LiveData<List<Game>> getVisitedGames(){
        return visitedGames;
    }

    public LiveData<List<Game>> getAllGamesFromDB(){
        return allGamesFromDB;
    }


    public void insert(Game game){
        new InsertAsyncTask(gameDao).execute(game);
    }

    public void update(Game game){
        new UpdateAsyncTask(gameDao).execute(game);
    }

    public void delete(Game game){
        new DeleteItemAsyncTask(gameDao).execute(game);
    }

    public void deleteAll(){
        new DeleteAllAsyncTask(gameDao).execute();
    }

    public void deleteVisitedGames() {new DeleteVisitedAsyncTask(gameDao).execute();}


    //-------------INSERT GAME ASYNC TASK
    private static class InsertAsyncTask extends AsyncTask<Game, Void, Void> {

        private GameDao asyncDao;

        public InsertAsyncTask(GameDao asyncDao){
            this.asyncDao = asyncDao;
        }

        @Override
        protected Void doInBackground(Game... games) {
            asyncDao.insert(games[0]);
            return null;
        }
    }

    //-------------UPDATE GAME ASYNC TASK
    private static class UpdateAsyncTask extends AsyncTask<Game, Void, Void> {

        private GameDao asyncDao;

        public UpdateAsyncTask(GameDao asyncDao){
            this.asyncDao = asyncDao;
        }

        @Override
        protected Void doInBackground(Game... games) {
            asyncDao.updateGame(games[0]);
            return null;
        }
    }

    //----------DELETE GAME ASYNC TASK
    private static class DeleteItemAsyncTask extends AsyncTask <Game, Void, Void> {

        private GameDao asyncDao;

        public DeleteItemAsyncTask(GameDao asyncDao){
            this.asyncDao = asyncDao;
        }

        @Override
        protected Void doInBackground(Game... games) {
            asyncDao.deleteGame(games[0]);
            return null;
        }
    }

    //-------------DELETE ALL ASYNC TASK
    private static class DeleteAllAsyncTask extends AsyncTask <Void, Void, Void>{
        private GameDao asyncDao;

        public DeleteAllAsyncTask(GameDao asyncDao){
            this.asyncDao = asyncDao;
        }

        @Override
        protected Void doInBackground(Void... voids) {
            asyncDao.deleteAllGames();
            return null;
        }
    }

    //-------------DELETE VISITED GAMES ASYNC TASK
    private static class DeleteVisitedAsyncTask extends AsyncTask <Void, Void, Void>{
        private GameDao asyncDao;

        public DeleteVisitedAsyncTask(GameDao asyncDao){
            this.asyncDao = asyncDao;
        }

        @Override
        protected Void doInBackground(Void... voids) {
            asyncDao.deleteVisitedGames();
            return null;
        }
    }
}

DAO:

@Dao
public interface GameDao {

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    void insert(Game game);

    @Delete
    void deleteGame(Game game);

    @Update
    void updateGame(Game game);

    @Query("DELETE FROM game_table")
    void deleteAllGames();

    @Query("SELECT * FROM game_table WHERE isWanted")
    LiveData<List<Game>> getWantedGames();

    @Query("SELECT * FROM game_table WHERE isPlaying")
    LiveData<List<Game>> getPlayingGames();

    @Query("SELECT * FROM game_table WHERE isPlayed")
    LiveData<List<Game>> getPlayedGames();

    @Query("SELECT * FROM game_table WHERE isVisited")
    LiveData<List<Game>> getVisitedGames();

    @Query("SELECT * FROM game_table")
    LiveData<List<Game>> getAllGamesFromDB();

    @Query("DELETE FROM game_table WHERE isVisited")
    void deleteVisitedGames();
}

数据库:

@Database(entities = Game.class, version = 8, exportSchema = false)
@TypeConverters({Converters.class})
public abstract class GameDatabase extends RoomDatabase {

    public abstract GameDao gameDao();

    private static GameDatabase INSTANCE;

    public static GameDatabase getInstance(Context context){
        if (INSTANCE == null){
            synchronized (GameDatabase.class){
                if (INSTANCE == null){
                    INSTANCE = Room.databaseBuilder(context.getApplicationContext(),
                            GameDatabase.class, "game_database")
                            .fallbackToDestructiveMigration()
                            .addCallback(sRoomDatabaseCallback)
                            .build();
                }
            }
        }
        return INSTANCE;
    }

    private static RoomDatabase.Callback sRoomDatabaseCallback = new RoomDatabase.Callback(){
        @Override
        public void onOpen(@NonNull SupportSQLiteDatabase db) {
            super.onOpen(db);
        }
    };
}
android database android-room android-livedata observers
1个回答
0
投票

我的2cents:即使您更改了标签页,您的观察员仍然处于活动状态,因为您对所有内容使用同一张表,并且我相信(只需看一眼代码即可)您正在更新游戏条目(例如:isPlayed设置为false)因此它将更新发送给所有活动的观察者。

考虑到这一点,如果我在这里,您可以:

  1. 制作3个不同的表(可以在同一数据库中)
  2. 使用viewpager将3个片段与3个不同的回收站一起使用
  3. 有3个回收站(您可以看到。每次用户更改标签时都可以看到)

如果我不正确,选项2 e 3仍然可以解决您的问题。希望对您有所帮助!

p.s:您还可以在每次用户更改选项卡时都删除并插入观察者,但是我发现它不太简单。

© www.soinside.com 2019 - 2024. All rights reserved.