Android ViewModel LiveData наблюдать - PullRequest
0 голосов
/ 16 марта 2020

Я следую рекомендациям по реализации ViewModel согласно Android, предоставленным командой разработчиков . Однако я получаю неожиданные результаты. Я копал inte rnet, но не повезло.

BookMarketPojo. java

class BookMarketPojo {
private String bookTitle;
private String bookAuthor;
private String bookDescription;
private String bookPictureUrl;
private String ownerId;
private boolean sold;

public BookMarketPojo() {
}

public BookMarketPojo(String bookTitle, String bookAuthor, String bookDescription, String bookPictureUrl, String ownerId, boolean sold) {
    this.bookTitle = bookTitle;
    this.bookAuthor = bookAuthor;
    this.bookDescription = bookDescription;
    this.bookPictureUrl = bookPictureUrl;
    this.ownerId = ownerId;
    this.sold = sold;
}

// getters and setters
}

BookViewModel. java

public class BookViewModel extends ViewModel {
private List<BookMarketPojo> booksList = new ArrayList<>();

private final String NODE_NAME = "_books_for_sale_";

private FirebaseDatabase db = FirebaseDatabase.getInstance();

public MutableLiveData<List<BookMarketPojo>> getData() {
    fetchData();
    MutableLiveData<List<BookMarketPojo>> data = new MutableLiveData<>();
    data.setValue(booksList);
    return data;
}

private void fetchData() {
    db.getReference(NODE_NAME).addChildEventListener(new ChildEventListener() {
        @Override
        public void onChildAdded(@NonNull DataSnapshot dataSnapshot, @Nullable String s) {
            booksList.add(dataSnapshot.getValue(BookMarketPojo.class));
        }

        @Override
        public void onChildChanged(@NonNull DataSnapshot dataSnapshot, @Nullable String s) {

        }

        @Override
        public void onChildRemoved(@NonNull DataSnapshot dataSnapshot) {

        }

        @Override
        public void onChildMoved(@NonNull DataSnapshot dataSnapshot, @Nullable String s) {

        }

        @Override
        public void onCancelled(@NonNull DatabaseError databaseError) {

        }
    });
}
}

BookMarketAdapter. java

public class BookMarketAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {

private Context context;
private List<BookMarketPojo> bookMarketPojos;

public BookMarketAdapter(Context context, List<BookMarketPojo> bookMarketPojos) {
    this.context = context;
    this.bookMarketPojos = bookMarketPojos;
}

@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
    View itemView = LayoutInflater.from(context).inflate(R.layout.book_market_row, parent, false);
    return new ViewHolder(itemView);
}

@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int position) {
    final BookMarketPojo book = bookMarketPojos.get(position);

    ((ViewHolder) viewHolder).title.setText(book.getBookTitle());
    ((ViewHolder) viewHolder).author.setText(book.getBookAuthor());
    ((ViewHolder) viewHolder).description.setText(book.getBookDescription());
    Picasso.get().load(book.getBookPictureUrl()).into(((ViewHolder) viewHolder).bookImg);
}

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

private class ViewHolder extends RecyclerView.ViewHolder {
    TextView title, author, description;
    ImageView bookImg;

    public ViewHolder(View convertView) {
        super(convertView);
        title = convertView.findViewById(R.id.tv_title);
        author = convertView.findViewById(R.id.tv_author);
        description = convertView.findViewById(R.id.tv_description);
        bookImg = convertView.findViewById(R.id.iv_bookImg);
    }
}
}

MainActivity. java

public class MainActivity extends AppCompatActivity {

private static final String TAG = "MarketActivity";

private BookMarketAdapter myAdapter;
private RecyclerView recyclerView;

private BookViewModel bookViewModel;

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    recyclerView = findViewById(R.id.rv_bookMarket);

    bookViewModel = new ViewModelProvider(this).get(BookViewModel.class);

    bookViewModel.getData().observe(this, books -> {
        myAdapter.notifyDataSetChanged();
    });

    initRecyclerView();

}

public void initRecyclerView(){
    myAdapter = new BookMarketAdapter(this, bookViewModel.getData().getValue());
    recyclerView.setLayoutManager(new LinearLayoutManager(this));
    recyclerView.setAdapter(myAdapter);
}
}

Ожидается результат: приложение при запуске открывает MainActivity и отображает книги в представлении утилизатора.

Фактический результат: приложение при запуске открывает MainActivity не отображает содержимое. При нажатии возврата приложение закрывается. При нажатии на значок приложения, чтобы открыть его, запускается отображение MainActivity, показывающее дублированный набор данных книг.

Ссылка на gif с указанием результата.

Может кто-нибудь определить, в чем проблема ? Почему onCreate не инициализирует адаптер на первом go? И почему я получаю дублированный список книг?

Ссылка на Git хранилище

1 Ответ

1 голос
/ 16 марта 2020

Давайте разберем, что вы написали, и как LiveData работает с вашими вопросами, не так ли?

Во-первых, ваш адаптер выглядит хорошо (в основном), и здесь проблема не в этом.

Давайте посмотрим на ViewModel и что здесь не так, прочитайте мои комментарии для решений:

public class BookViewModel extends ViewModel {

    private List<BookMarketPojo> booksList = new ArrayList<>();

    private final String NODE_NAME = "_books_for_sale_";

    private FirebaseDatabase db = FirebaseDatabase.getInstance();

    /** 
    * The purpose of livedata is to simply emit events. 
    * Therefore this can be simply written as [note the general convention used in naming]
    */
    public MutableLiveData<List<BookMarketPojo>> onBookListUpdated = MutableLiveData<List<BookMarketPojo>>();

    /** 
    * The problem with this code, is that when you set the value initially, you only set an empty arraylist. 
    * Remember, your LiveData is only a way to emit changes to a data (the data here being booksList) with the safetynet of LifeCycle Awareness
    * therefore, to fetch the data, we only need to do this. 
    */
    public void getData() {
        fetchData()
    }

    private void fetchData() {
        db.getReference(NODE_NAME).addChildEventListener(new ChildEventListener() {
            @Override
            public void onChildAdded(@NonNull DataSnapshot dataSnapshot, @Nullable String s) {

                //To remove duplicates, make sure the book isn't already present in the list. Exercise is left to you
                booksList.add(dataSnapshot.getValue(BookMarketPojo.class));

                //This will now trigger the livedata, with the list of the books.
                onBookListUpdated.setValue(booksList)

            }

            @Override
            public void onChildChanged(@NonNull DataSnapshot dataSnapshot, @Nullable String s) {

            }

            @Override
            public void onChildRemoved(@NonNull DataSnapshot dataSnapshot) {

            }

            @Override
            public void onChildMoved(@NonNull DataSnapshot dataSnapshot, @Nullable String s) {

            }

            @Override
            public void onCancelled(@NonNull DatabaseError databaseError) {

            }
        });
    }
}

и, наконец, в OnCreate ()

 bookViewModel.onBookListUpdated.observe(this, books -> {
        myAdapter.notifyDataSetChanged();
   });

вашей деятельности. Кроме того, взгляните на документацию по базе данных Firebase Realtime для Чтение данных один раз , так как я считаю, что он лучше подходит для вашего сценария использования, чем просто выборка книг один раз!

...