Reputation: 22958
In a dictionary app using Room to store words I have a Fragment with a TextField.
When the user enters a word into the TextField, I am extracting the text and send it to a JobIntentService, which then calls the DAO method synchronously on its own thread:
@Dao
public interface MyDao {
@Query("SELECT 1 FROM dictionary WHERE word = :word")
int findWord(String word);
}
Finally, the JobIntentService sends the result via LocalBroadcastManager to the hosting Activity and the activity calls a public method on the Fragment.
This path works, but you can see that it is very long and I had to write lot of boilerplate code to implement it.
Recently I've got a hint on Reddit, that LiveData is better for such cases and I have been reading on it and already switched some of my fragments to using it.
So I am trying to switch this Fragment to using LiveData too, by changing the signature of the method to:
@Dao
public interface MyDao {
@Query("SELECT 1 FROM dictionary WHERE word = :word")
LiveData<Integer> findWord(String word);
}
and invoking it from my Fragment:
public class MyFragment extends Fragment {
private static final Pattern PATTERN = Pattern.compile("([A-Z]{2,})");
private LiveData<Integer> mLiveData;
private EditText mInputText;
private CheckedTextView mResultText;
@Override
public View onCreateView(@NonNull LayoutInflater inflater,
ViewGroup container,
Bundle savedInstanceState) {
...
mInputText.addTextChangedListener(new TextWatcher() {
...
@Override
public void afterTextChanged(Editable s) {
String word = mInputText.getText().toString().trim().toUpperCase();
if (PATTERN.matcher(word).matches()) {
mLiveData = MyDatabase.getInstance(getContext()).myDao().findWord(word);
} else {
mResultText.setChecked(false);
}
}
});
mLiveData.observe(this, found -> {
if (found != null && found == 1) {
mResultText.setChecked(true);
} else {
mResultText.setChecked(false);
}
});
return view;
}
And the above method of course fails with NPE because mLiveData
is null.
But how can I initiate it, when the word parameter to findWord(word)
is changing?
Upvotes: 0
Views: 161
Reputation: 22958
I have followed pskink's hint (thank you!) and the following seems to work for me:
public class MyFragment extends Fragment {
private static final Pattern PATTERN = Pattern.compile("([A-Z]{2,})");
private MutableLiveData<String> mTrigger = new MutableLiveData<>();
private LiveData<Integer> mResult = Transformations.switchMap(mTrigger,
word -> MyDatabase.getInstance(getContext()).myDao().findWord(word));
private EditText mInputText;
private CheckedTextView mResultText;
@Override
public View onCreateView(@NonNull LayoutInflater inflater,
ViewGroup container,
Bundle savedInstanceState) {
...
mInputText.addTextChangedListener(new TextWatcher() {
...
@Override
public void afterTextChanged(Editable s) {
String word = mInputText.getText().toString().trim().toUpperCase();
if (PATTERN.matcher(word).matches()) {
mTrigger.setValue(word);
} else {
mResultText.setChecked(false);
}
}
});
mLiveData.observe(this, found -> {
if (found != null && found == 1) {
mResultText.setChecked(true);
} else {
mResultText.setChecked(false);
}
});
return view;
}
Upvotes: 1