【问题标题】:How can I observe LiveData and then pass this data to my adapter?如何观察 LiveData,然后将此数据传递给我的适配器?
【发布时间】:2020-05-14 12:07:43
【问题描述】:

我有一个Calendar。 要创建它,我有一个 CalendarFragment,它会打开 CustomCalendarView

(扩展 LinearLayout 的类)。

然后使用 MyGridAdapter(扩展 ArrayAdapter 的类)来构造日历。

当您单击日历上的单元格时,您将进入一个新活动,您可以在其中保存包含一些信息的日志(以及单击日历中单元格的日期)。

我的 Dao 类中有一个查询:Log_Entries_Dao 传递给 LogEntriesRepository,然后是 LogEntriesViewModel

查询:

@Query("SELECT date FROM log_entries_table WHERE log_entries_table.date = :date " ) LiveData<List<LogDates>> getAllDatesWithLogs(List<Date> date);

查询接收日历月所有日期的列表,然后返回存在 logEntry 的所有日期的列表。

现在我想在我的 setupCalendar 方法中观察这个 LiveData 并将 logEntry 存在的日期列表传递给我的 gridAdater 类,这样我就可以在 logEntry 所在的所有单元格中添加圆圈现在。

问题是,我认为在CustomCalendarView(LinearLayout 类)中使用我的LogViewModel 类是不好的做法。 如何正确查询我的数据库、观察 liveData 并将其从我的 setUpCalendar 方法中发送到我的适配器?

日历片段

public class CalendarFragment extends Fragment {

    CustomCalendarView customCalendarView;
    List<Date> dates = new ArrayList<>();
    LogEntriesViewModel logEntriesViewModel;

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        return inflater.inflate(R.layout.calendar_activity_main, container,false);
    }

    @Override
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        customCalendarView =(CustomCalendarView) getView().findViewById(R.id.custom_calendar_view);
        ((AppCompatActivity) getActivity()).getSupportActionBar().hide();
    }
}

自定义日历视图


public class CustomCalendarView extends LinearLayout {
    ImageButton NextButton, PreviousButton;
    TextView CurrentDate;
    GridView gridView;
    public static final int MAX_CALENDAR_DAYS = 42;
    Calendar calendar = Calendar.getInstance(Locale.ENGLISH);
    Context context;
    MyGridAdapter myGridAdapter;
    SimpleDateFormat dateFormat = new SimpleDateFormat("MMMM yyyy", Locale.ENGLISH);
    SimpleDateFormat monthFormat = new SimpleDateFormat("MMMM", Locale.ENGLISH);
    SimpleDateFormat yearFormat = new SimpleDateFormat("yyyy", Locale.ENGLISH);
    SimpleDateFormat eventDateFormat = new SimpleDateFormat(("dd-MM-yyyy"), Locale.ENGLISH);

    public static final String MY_PREFS_NAME = "MyPrefsFile";

    List<Date> dates = new ArrayList<>();
    List<Log_Entries> eventsList = new ArrayList<>();

    public CustomCalendarView(Context context) {
        super(context);
    }

    public CustomCalendarView(final Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        this.context = context;
        InitializeLayout();
        SetUpCalendar();

        PreviousButton.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                calendar.add(Calendar.MONTH, -1);
                SetUpCalendar();
            }
        });

        NextButton.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                calendar.add(Calendar.MONTH, 1);
                SetUpCalendar();
            }
        });


        gridView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                AlertDialog.Builder builder = new AlertDialog.Builder(context);
                builder.setCancelable(true);

                final String date = eventDateFormat.format(dates.get(position));


                Intent i = new Intent(getContext(), WorkoutButtonsActivity.class);
                i.putExtra(WorkoutButtonsActivity.EXTRA_DATE, date);
                getContext().startActivity(i);
            }
        });
    }

    public CustomCalendarView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    private void InitializeLayout() {
        LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        View view = inflater.inflate(R.layout.calendar_layout, this);
        NextButton = view.findViewById(R.id.nextBtn);
        PreviousButton = view.findViewById(R.id.previousBtn);
        CurrentDate = view.findViewById(R.id.current_Date);
        gridView = view.findViewById(R.id.gridview);
    }

    private void SetUpCalendar() {

        String currentDate = dateFormat.format(calendar.getTime());
        CurrentDate.setText(currentDate);
        dates.clear();
        Calendar monthCalendar = (Calendar) calendar.clone();
        monthCalendar.set(Calendar.DAY_OF_MONTH, 1);
        int FirstDayofMonth = monthCalendar.get(Calendar.DAY_OF_WEEK) - 1;
        monthCalendar.add(Calendar.DAY_OF_MONTH, -FirstDayofMonth);

        while (dates.size() < MAX_CALENDAR_DAYS) {
            dates.add(monthCalendar.getTime());
            monthCalendar.add(Calendar.DAY_OF_MONTH, 1);


        }
        myGridAdapter = new MyGridAdapter(context, dates, calendar, eventsList);
        gridView.setAdapter(myGridAdapter);

    }
}

MyGridAdapter


public class MyGridAdapter extends ArrayAdapter {
    List<Date> dates;
    Calendar currentDate;

    List<Log_Entries> logs;

    LayoutInflater inflater;

    public MyGridAdapter(@NonNull Context context, List<Date> dates, Calendar currentDate, List<Log_Entries> logs){
        super(context, R.layout.single_cell_layout);

        this.dates = dates;
        this.currentDate = currentDate;
        this.logs = logs;
        inflater = LayoutInflater.from(context);

        Log.i("dates", String.valueOf(dates));

    }
    @NonNull
    @Override
    public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
        Date monthDate = dates.get(position);
        Calendar dateCalendar = Calendar.getInstance();
        dateCalendar.setTime(monthDate);
        int DayNo = dateCalendar.get(Calendar.DAY_OF_MONTH);
        int displayMonth = dateCalendar.get(Calendar.MONTH) +1;
        int displayYear = dateCalendar.get(Calendar.YEAR);
        int currentMonth = currentDate.get(Calendar.MONTH) + 1;
        int currentYear = currentDate.get(Calendar.YEAR);
        int currentDay = currentDate.get(Calendar.DAY_OF_MONTH);



        View view = convertView;
        if (view == null){
            view = inflater.inflate(R.layout.single_cell_layout, parent,false);
        }

        if (displayMonth == currentMonth && displayYear == currentYear){
            view.setBackgroundColor(getContext().getResources().getColor(R.color.green));
        }
        else{
            view.setBackgroundColor(Color.parseColor("#cccccc"));
        }

        TextView Day_Number = view.findViewById(R.id.calendar_day);
        Day_Number.setText(String.valueOf(DayNo));

        Calendar eventCalendar = Calendar.getInstance();

        if(DayNo == currentDay && displayMonth == eventCalendar.get(Calendar.MONTH) + 1 && displayYear == eventCalendar.get(Calendar.YEAR)){
            Day_Number.setTextColor(Color.parseColor("#FFFF33"));

        }


        ArrayList<String> arrayList = new ArrayList<>();
        for (int i = 0; i < logs.size(); i++){
        }
        return view;
    }

    @Override
    public int getCount() {
        return dates.size();
    }

    @Override
    public int getPosition(@Nullable Object item) {
        return dates.indexOf(item);
    }

    @Nullable
    @Override
    public Object getItem(int position) {
        return dates.get(position);
    }
}

LogEntriesRepository


public class LogEntriesRepository {

    private Log_Entries_Dao log_entries_dao;
    private LiveData<List<Log_Entries>> allLogEntries;
    private LiveData<List<Log_Entries>> allWorkoutLogEntries;

    private LiveData<List<LogDates>> allDatesWithLog;

    public LogEntriesRepository(Application application) {
        ExerciseDatabase database = ExerciseDatabase.getInstance(application);
        log_entries_dao = database.log_entries_dao();
        allLogEntries = log_entries_dao.getAllFromLogEntries();
    }

    public void insert(Log_Entries log_entries) {
        new InsertLogEntryAsyncTask(log_entries_dao).execute(log_entries);
    }

    public void update(Log_Entries log_entries) {
        new UpdateLogEntryAsyncTask(log_entries_dao).execute(log_entries);
    }

    public void delete(Log_Entries log_entries) {
        new DeleteLogEntryAsyncTask(log_entries_dao).execute(log_entries);
    }


    public LiveData<List<Log_Entries>> getAllLogEntries() {
        return allLogEntries;
    }

    public LiveData<List<Log_Entries>> getAllWorkoutLogEntries(int junctionID, String date) {
        allWorkoutLogEntries = log_entries_dao.getAllFromWorkoutLogEntries(junctionID, date);
        return allWorkoutLogEntries;
    }

    public LiveData<List<LogDates>> getAllDateLogEntries(List<String> date) {
        allDatesWithLog = log_entries_dao.getAllDatesWithLogs(date);
        return allDatesWithLog;
    }

    private static class InsertLogEntryAsyncTask extends AsyncTask<Log_Entries, Void, Void> {
        private Log_Entries_Dao log_entries_dao;

        private InsertLogEntryAsyncTask(Log_Entries_Dao log_entries_dao) {
            this.log_entries_dao = log_entries_dao;
        }

        @Override
        protected Void doInBackground(Log_Entries... log_entries) {
            log_entries_dao.insert(log_entries[0]);
            return null;
        }
    }

    private static class UpdateLogEntryAsyncTask extends AsyncTask<Log_Entries, Void, Void> {
        private Log_Entries_Dao log_entries_dao;

        private UpdateLogEntryAsyncTask(Log_Entries_Dao log_entries_dao) {
            this.log_entries_dao = log_entries_dao;
        }

        @Override
        protected Void doInBackground(Log_Entries... log_entries) {

            log_entries_dao.update(log_entries[0]);
            return null;
        }
    }

    private static class DeleteLogEntryAsyncTask extends AsyncTask<Log_Entries, Void, Void> {
        private Log_Entries_Dao log_entries_dao;

        private DeleteLogEntryAsyncTask(Log_Entries_Dao log_entries_dao) {
            this.log_entries_dao = log_entries_dao;
        }

        @Override
        protected Void doInBackground(Log_Entries... log_entries) {
            log_entries_dao.delete(log_entries[0]);
            return null;
        }
    }

}

LogEntriesViewModel

public class LogEntriesViewModel extends AndroidViewModel {

    private LogEntriesRepository repository;
    private LiveData<List<Log_Entries>> allLogEntries;
    private LiveData<List<Log_Entries>> allWorkoutLogEntries;

    private LiveData<List<LogDates>> allDateLogEntries;


    public LogEntriesViewModel(@NonNull Application application) {
        super(application);
        repository = new LogEntriesRepository(application);
        allLogEntries = repository.getAllLogEntries();
    }


    public void insert(Log_Entries log_entries){
        repository.insert(log_entries);
    }
    public void update(Log_Entries log_entries){
        repository.update(log_entries);
    }
    public void delete(Log_Entries log_entries ) {
        repository.delete(log_entries);
    }
   // public LiveData<List<Log_Entries>> getAllLogs(){
   //     return allLogEntries;
   // }

    public LiveData<List<Log_Entries>> getAllWorkoutLogEntries(int junctionID, String date){
        allWorkoutLogEntries = repository.getAllWorkoutLogEntries(junctionID, date);
        return allWorkoutLogEntries;
    }

    public LiveData<List<LogDates>> getAllDateLogEntries(List<String> date){
        allDateLogEntries = repository.getAllDateLogEntries(date);
        return allDateLogEntries;
    }

}

【问题讨论】:

    标签: java android android-fragments android-room android-adapter


    【解决方案1】:

    试试这个通用架构:

    1. 由于 Room-method 具有可在 Fragment 生命周期期间更改的参数的功能 - 在您的 ViewModel 中应该是两个实时数据字段而不是一个。 首先,不可变 (LiveData) - 您将从您的存储库中获得结果。 第二,可变的 (MutableLiveData) 应该包含您的日期列表(在您的日历中可见)。然后你应该使用 Transfromation.switchMap 使 MutableLiveData 成为不可变数据的来源。这个方法的解释你可以阅读in this answer例如。
    2. 您应该从 Fragment 设置 MutableLiveData 的值(您甚至可以在 CustomCalendarView 内部进行设置,因为它包含对 Fragment 上下文的引用。这意味着它也可以访问 ViewModel)。因此,当您日历中的当前月份设置或更改时(也许您可以在那里添加一些方法 changeMonth()?),您应该设置 MutableLiveData 的新值并...等待 Room 的 LiveData回应。
    3. 在您的 Fragment onViewCreated() 中,您应该观察您的 不可变 LiveData 值 并获取它 - 调用 CustomCalendarView 的 setupCalendar() 方法(因为您可以访问它)并把你房间的给定日期列表放在那里。在开始观察 LiveData 值之前设置 MutableLiveData 的值很重要。需要明确的是 - 我建议在从 Room 获得价值后更改(设置)您的日历视图。但可能还有其他方法,这取决于个人喜好。
    4. 后续步骤(在您的 CustomCalendarView 中设置适配器列表并在您的视图中绘制圆圈)我想您知道如何实现。

    希望对你有帮助

    【讨论】:

      【解决方案2】:

      我希望如果您理解这一点并将其应用于您的代码,如果我没记错的话,那将解决您的查询。我没有查看您的所有代码,但我得到了您的查询,即“如何将 Livedata 设置为适配器”,这是解决方案。

      您可以在此处与您的参考代码进行比较。

         @Dao
       interface UserDao {
       @Query("SELECT * FROM user ORDER BY lastName ASC")
       public abstract LiveData<List<User>> usersByLastName();
       }
      
       class MyViewModel extends ViewModel {
       public final LiveData<List<User>> usersList;
       public MyViewModel(UserDao userDao) {
           usersList = userDao.usersByLastName();
       }
       }
      
       class MyActivity extends AppCompatActivity {
       @Override
       public void onCreate(Bundle savedState) {
           super.onCreate(savedState);
           MyViewModel viewModel = new ViewModelProvider(this).get(MyViewModel.class);
           RecyclerView recyclerView = findViewById(R.id.user_list);
           UserAdapter<User> adapter = new UserAdapter();
           viewModel.usersList.observe(this, list -> adapter.submitList(list));
           recyclerView.setAdapter(adapter);
       }
       }
      
       class UserAdapter extends ListAdapter<User, UserViewHolder> {
       public UserAdapter() {
           super(User.DIFF_CALLBACK);
       }
       @Override
       public void onBindViewHolder(UserViewHolder holder, int position) {
           holder.bindTo(getItem(position));
       }
       public static final DiffUtil.ItemCallback<User> DIFF_CALLBACK =
               new DiffUtil.ItemCallback<User>() {
           @Override
           public boolean areItemsTheSame(
                   @NonNull User oldUser, @NonNull User newUser) {
               // User properties may have changed if reloaded from the DB, but ID is fixed
               return oldUser.getId() == newUser.getId();
           }
           @Override
           public boolean areContentsTheSame(
                   @NonNull User oldUser, @NonNull User newUser) {
               // NOTE: if you use equals, your object must properly override 
      Object#equals()
               // Incorrectly returning false here will result in too many animations.
               return oldUser.equals(newUser);
           }
        }
      }
      

      总之,

      道中的第 1 步:

       public abstract LiveData<List<User>> usersByLastName();
      

      第 2 步在您的 ViewModel 中:

      public final LiveData<List<User>> usersList;
      usersList = userDao.usersByLastName();
      

      最后在你的 Activity/Fragment 中:

       UserAdapter<User> adapter = new UserAdapter();
       viewModel.usersList.observe(this, list -> adapter.submitList(list));
       recyclerView.setAdapter(adapter);
      

      【讨论】:

      • 谢谢,但这在我的 customView 中不起作用。问题是我需要访问自定义视图中的 liveData。
      猜你喜欢
      • 2018-12-23
      • 2023-03-26
      • 1970-01-01
      • 1970-01-01
      • 2021-06-01
      • 2019-06-04
      • 2019-08-30
      • 2019-01-24
      • 1970-01-01
      相关资源
      最近更新 更多