私は頭を壁にぶつけていましたが、なぜこれが起こっているのか理解できません。 Android=の新しいArchitectural Componentsを使用していますが、LiveDataをオブジェクトのリストで更新するのに問題があります。2つのスピナーがあります。最初のオプションを変更すると、内容を変更する必要がありますが、この最後の部分は発生していません。
State.Java
@Entity(tableName = "states")
public class State{
@PrimaryKey(autoGenerate = false)
private int id;
private String name;
@ColumnInfo(name = "countryId")
private String CountryId;
@Ignore
private Object geoCenter, geoLimit;
public State(){
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getCountryId() {
return CountryId;
}
public void setCountryId(String countryId) {
CountryId = countryId;
}
}
StateDAO
@Dao
public interface StateDao {
@Query("SELECT * FROM states")
LiveData<List<State>> getAllStates();
@Query("SELECT * FROM states WHERE countryId = :countryID")
LiveData<List<State>> getStatesFromCountry(String countryID);
@Query("SELECT COUNT(*) FROM states")
int getNrStates();
@Query("SELECT COUNT(*) FROM states WHERE countryId = :countryID")
int getNrStatesByCountry(String countryID);
@Insert(onConflict = IGNORE)
void insertAll(List<State> states);
@Delete
void delete(State state);
}
StateRepository
@Singleton
public class StatesRepository {
private final WebServices services;
private final StateDao stateDao;
private final Executor executor;
@Inject
public StatesRepository(Executor executor, StateDao stateDao, WebServices services) {
this.services = services;
this.stateDao = stateDao;
this.executor = executor;
}
public LiveData<List<State>> getStates(String token){
refreshStates(token);
return stateDao.getAllStates();
}
public LiveData<List<State>> getStatesFromCountry(String countryID){
return stateDao.getStatesFromCountry(countryID);
}
private void refreshStates(final String token){
executor.execute(() -> {
Log.d("oooooo", stateDao.getNrStates() + "");
if(stateDao.getNrStates() == 0){
try {
Response<List<State>> response = services.getStates("Bearer "+token).execute();
stateDao.insertAll(response.body());
} catch (IOException e) {
e.printStackTrace();
}
}
});
}
}
StateViewModel
public class StatesViewModel extends ViewModel {
private LiveData<List<State>> states;
private StatesRepository repo;
@Inject
public StatesViewModel(StatesRepository repository){
this.repo = repository;
}
public void init(String token){
states = repo.getStates(token);
}
public void getStatesFromCountry(String countryID){
states = repo.getStatesFromCountry(countryID);
}
public LiveData<List<State>> getStates(){
return this.states;
}
}
フラグメント
public class EditAddressFragment extends LifecycleFragment implements View.OnClickListener, Injectable{
private Spinner country, city, state, Zip_code;
private String token;
private List<Country> countries;
private List<City> cities;
private List<State> states;
@Inject ViewModelFactory viewModelFactory;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.addresses_edit_layout, container, false);
city = view.findViewById(R.id.city);
state = view.findViewById(R.id.state);
country = view.findViewById(R.id.country);
...
countries = new ArrayList<>();
cities = new ArrayList<>();
states = new ArrayList<>();
return view;
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
CountrySpinnerAdapter adapter = new CountrySpinnerAdapter(getActivity(), Android.R.layout.simple_spinner_item, countries);
country.setAdapter(adapter);
CitySpinnerAdapter cityAdapter = new CitySpinnerAdapter(getActivity(), Android.R.layout.simple_spinner_item, cities);
city.setAdapter(cityAdapter);
StateSpinnerAdapter stateAdapter = new StateSpinnerAdapter(getActivity(), Android.R.layout.simple_spinner_item, states);
state.setAdapter(stateAdapter);
CountriesViewModel countriesViewModel = ViewModelProviders.of(this, viewModelFactory).get(CountriesViewModel.class);
countriesViewModel.init(token);
countriesViewModel.getCountries().observe(this, adapter::setValues);
CityViewModel cityViewModel = ViewModelProviders.of(this, viewModelFactory).get(CityViewModel.class);
cityViewModel.init(token);
cityViewModel.getCities().observe(this, cityAdapter::setValues);
StatesViewModel statesViewModel = ViewModelProviders.of(this, viewModelFactory).get(StatesViewModel.class);
statesViewModel.init(token);
statesViewModel.getStates().observe(this, states -> {
Log.d("called", states.toString());
stateAdapter.setValues(states); } );
country.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
Country c = (Country) adapterView.getItemAtPosition(i);
Log.d("cd", c.getId());
//states = new ArrayList<State>();
statesViewModel.getStatesFromCountry(c.getId());
}
@Override
public void onNothingSelected(AdapterView<?> adapterView) {
}
});
....
アダプター
public void setValues(List<State> states)
{
this.states = states;
Log.d("s", states.isEmpty()+" "+states.toString());
notifyDataSetChanged();
}
さて、私はこの問題の解決策に到達し、このLiveDataがどのように機能するかを見つけました。
@MartinMarconciniのおかげで、彼の助けはすべてデバッグです;)
どうやら、オブザーバーは最初に設定したオブジェクトにリンクされているようです。オブジェクトを(帰属によって)置換することはできません。そうしないと機能しません。また、変数の値が変更される場合は、MutableLiveDataを使用する必要があります
したがって、必要な変更は次のとおりです。
1。LiveDataからMutableLiveDataに変更し、MutableLiveDataを更新する必要があるときにリポジトリに渡します
public class StatesViewModel extends ViewModel {
private MutableLiveData<List<State>> states; ;;CHANGED
private StatesRepository repo;
@Inject
public StatesViewModel(StatesRepository repository){
this.repo = repository;
}
public void init(String token){
states = repo.getStates(token);
}
public void getStatesFromCountry(String countryID){
repo.getStatesFromCountry(this.states, countryID); ;;CHANGED
}
public LiveData<List<State>> getStates(){
return this.states;
}
}
2。リポジトリで、setValueを使用してMutableLiveDataを更新します
@Singleton
public class StatesRepository {
private final WebServices services;
private final StateDao stateDao;
private final Executor executor;
@Inject
public StatesRepository(Executor executor, StateDao stateDao, WebServices services) {
this.services = services;
this.stateDao = stateDao;
this.executor = executor;
}
public MutableLiveData<List<State>> getStates(String token){
refreshStates(token);
final MutableLiveData<List<State>> data = new MutableLiveData<>();
data.setValue(stateDao.getAllStates());
return data;
}
;; CHANGED
public void getStatesFromCountry(MutableLiveData states, final String countryID){
states.setValue(stateDao.getStatesFromCountry(countryID));
}
private void refreshStates(final String token){
executor.execute(() -> {
if(stateDao.getNrStates() == 0){
try {
Response<List<State>> response = services.getStates("Bearer "+token).execute();
stateDao.insertAll(response.body());
} catch (IOException e) {
e.printStackTrace();
}
}
});
}
}
。LiveDataではなくListを返すようにDAOを変更>
@Dao
public interface StateDao {
@Query("SELECT * FROM states")
List<State> getAllStates();
@Query("SELECT * FROM states WHERE ctrId = :countryID")
List<State> getStatesFromCountry(String countryID);
@Query("SELECT COUNT(*) FROM states")
int getNrStates();
@Query("SELECT COUNT(*) FROM states WHERE ctrId = :countryID")
int getNrStatesByCountry(String countryID);
@Insert(onConflict = IGNORE)
void insertAll(List<State> states);
@Delete
void delete(State state);
}
4。最後にメインスレッドでクエリを実行できるようにする
AppModule.Java
@Singleton @Provides
AppDatabase provideDb(Application app) {
return Room.databaseBuilder(app, AppDatabase.class,"unitail.db")
.allowMainThreadQueries()
.fallbackToDestructiveMigration()
.build();
}
ライブデータ参照を設定して監視を開始したら、ライブデータ参照を更新しないでください。リポジトリでライブデータを更新する代わりに、MediatorLiveDataを使用する必要があります。
あなたの場合、次の変更を行ってください。
private MediatorLiveData<List<State>> states; // change
.....
.....
states.addSource(repo.getStatesFromCountry(countryID), newData -> states.setValue(newData)); //change
Daoはすべての操作で同じでなければなりません。挿入と監視に異なるDaoインスタンスを使用します
より良い議論のために答えを書く。
だから私は(コトリンで、sry)ノートのリストであるモデルを持っています(これはすべてこれを再生するための単なるサンドボックスアプリです)そして私のアーキテクチャはここにあります:レポはありませんが、アクティビティ-> ViewModelがあります->ダオ。
だからDaoは_LiveData<MutableList<Note>>
_を公開します
_@Query("SELECT * FROM notes")
fun loadAll(): LiveData<MutableList<Note>>
_
My ViewModel…は以下を介して公開します。
val notesList = database.notesDao().loadAll()
私のアクティビティ(onCreate)は…
_ viewModel.notesList.observe(this,
Observer<MutableList<Note>> { notes ->
if (notes != null) {
progressBar?.hide()
adapter.setNotesList(notes)
}
})
_
これは動作します。アダプタは、文字通り何もしないRecyclerViewアダプタです:
_ fun setNotesList(newList: MutableList<Note>) {
if (notes.isEmpty()) {
notes = newList
notifyItemRangeInserted(0, newList.size)
} else {
val result = DiffUtil.calculateDiff(object : DiffUtil.Callback() {
override fun getOldListSize(): Int {
return notes.size
}
override fun getNewListSize(): Int {
return newList.size
}
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
return notes[oldItemPosition].id == newList[newItemPosition].id
}
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
val (id, title, _, priority) = newList[newItemPosition]
val (id1, title1, _, priority1) = notes[oldItemPosition]
return id == id1
&& priority == priority1
&& title == title1
}
})
notes = newList
result.dispatchUpdatesTo(this)
}
}
_
アプリの他の部分がそのメモのリストを変更すると、アダプターは自動的に更新されます。これにより、単純な(r?)アプローチを試すための遊び場が提供されることを願っています。