Using two way data binding I'm trying to fit a double(Double) inside a EditText type field.
I have tried with converter functions (with #InverseMethod) and also tried to write a #BindingAdapter with #InverseBindingAdapter.
I think I may be missing something crucial cause:
"#={`` + muObject.myDecimal}"
reveals 'null' in the EditText field.
The #InverseFunction method crashes, and the binding adapter way did not work either...
Could someone please point me in the right direction?
Thanks
ViewModel code:
Note that the BaseObservableViewModel extends ViewModel from architecture components and contains the contents of the BaseObservable class (tip from Yigit Boyar).
Also note that none of the fields in the QualityControl class are observable.
At last: Note that the getter/setter for measurementKm are a test. measurementKm is Double field in the QualityControl class and I would prefer to bind directly to that field.
public final class QualityControlViewModel extends BaseObservableViewModel {
#SuppressWarnings("unused")
private static final String TAG = "QualityControlVM";
private QualityControl qualityControl;
private int position;
public String measurementKm = "";
QualityControlViewModel(Application application) {
super(application);
}
public QualityControl getQualityControl() {
return qualityControl;
}
public void setQualityControl(QualityControl qualityControl) {
this.qualityControl = qualityControl;
// Initialize massMeasurementPlaceSelected
if (isValidMassMeasurementPlace()) massMeasurementPlaceSelected = true;
setMeasurementKm(qualityControl.getMeasurementKm());
}
public int getPosition() {
return position;
}
public void setPosition(int position) {
this.position = position;
}
private Double getMeasurementKm() {
if (this.measurementKm.length() > 0) {
return Double.parseDouble(this.measurementKm);
} else {
return 0.0;
}
}
private void setMeasurementKm(Double measurementKm) {
if (qualityControl.getMeasurementKm() != null) {
this.measurementKm = String.valueOf(qualityControl.getMeasurementKm());
}
notifyChange();
}
}
Related
I'm new to programming and have a problem.
I'm trying to get some value from room database.
The problem is when I try to get the method from viewModel to my fragment, android studio cannot resolve symbol.
my fragment:
public class Tab1Frag extends Fragment {
private Tab1FragModelView tab1ViewModel;
private RoomDatabase db;
public Tab1Frag() {
// Required empty public constructor
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
db = TransactionRoomDatabase.getDatabase(this);
//problem here
tab1ViewModel = ViewModelProviders.of(this).get(Tab1FragModelView.class);
tab1ViewModel.todaySum();
//and here
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_tab1, container, false);
}
The android studio underlined getApplicationContext on db as cannot resolve method and transactionEntity on tab1ViewModel.todaySum as cannot resolve symbol.
I don't know what the problem is because there's no problem in my another activity
here is a section of that activity
db = Room.databaseBuilder(getApplicationContext(), TransactionRoomDatabase.class,
"transactionEntity").build();
addSpendingDetailText = findViewById(R.id.addSpendingDetailText);
inputViewModel = ViewModelProviders.of(this).get(InputViewModel.class); //important
Button addSpending = findViewById(R.id.addSpendingBtn);
addSpending.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
String transactionDetail = addSpendingDetailText.getText().toString();
String transactionAmount = hiddenAmount.getText().toString();
String transactionCategory = chooseCategory.getText().toString();
String transactionDate = hiddenDateText.getText().toString();
//Handler if field empty
if (transactionDetail.trim().isEmpty() || transactionAmount.trim().isEmpty() ||
transactionCategory.trim().isEmpty() || transactionDate.trim().isEmpty()) {
Toast toast1 = Toast.makeText(InputSpending.this,
"Please insert all required data", Toast.LENGTH_SHORT);
toast1.setGravity(Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM,
0, 275);
toast1.show();
return;
}
else {TransactionEntity transactionEntity = new TransactionEntity(transactionDate,
transactionCategory, transactionDetail, Double.parseDouble(transactionAmount)) {
};
inputViewModel.insert(transactionEntity);
I don't know if this gonna be needed or not, but i'll just post it just in case
my DAO
#Insert
void insertTransaction(TransactionEntity transactionEntity);
class SumToday { public double total;
}
#Query("SELECT SUM(transactionAmount) as total FROM `transaction` where date(transactionDate = 'now')")
double getTodayDateValue();
my fragment viewmodel
public class Tab1FragModelView extends AndroidViewModel {
private TransactionRepository repository;
public Tab1FragModelView(#NonNull Application application) {
super(application);
repository = new TransactionRepository(application);
}
public void todaySum(){
repository.todaySum();
}
}
my Repository
public class TransactionRepository {
private TransactionDao transactionDao;
public TransactionRepository(Application application) {
TransactionRoomDatabase db;
db = TransactionRoomDatabase.getDatabase(application);
transactionDao = db.transactionDao();
}
public void insert(TransactionEntity transactionEntity) {
new InsertTransactionAsyncTask(transactionDao).execute(transactionEntity);
}
private static class InsertTransactionAsyncTask extends AsyncTask<TransactionEntity, Void, Void> {
private TransactionDao transactionDao;
private InsertTransactionAsyncTask(TransactionDao transactionDao) {
this.transactionDao = transactionDao;
}
#Override
protected Void doInBackground(TransactionEntity... transactions) {
transactionDao.insertTransaction(transactions[0]);
return null;
}
}
public void todaySum() {
new TodaySumAsyncTask(transactionDao).execute();
}
private static class TodaySumAsyncTask extends AsyncTask<TransactionEntity, Void, Double> {
private TransactionDao transactionDao;
private TodaySumAsyncTask(TransactionDao transactionDao) {
this.transactionDao = transactionDao;
}
#Override
protected Double doInBackground(TransactionEntity... transactions) {
Double data = transactionDao.getTodayDateValue();
Log.d("TAG", "doInBackground: data :"+data);
// use livedata to notify viewmodel and then ui
return data;
}
}
EDIT:
+adding information:
My goal is to show the sum of value/amount from the database to my fragment's textView, no less no more.
To do that I'm trying to access it with viewmodel, which connect to repository, which connect to database. Then, I get this "cannot resolve symbol" error which is not happen when I'm inserting data to database. I've tried copying the working code of insert method to this fragment and it showing same "cannot resolve symbol" error. The only differences is I use that insert method in an activity, not fragment. I don't know if that the problem or not, but it maybe worth mentioning.
So far the apps is working until inserting data to database. When I check the database, the inputted data is there.
I don't mind changing my code as long as it can get my goal, but please don't put too advance code (because I'm still new to programming).
In my Activity, I have a Training object member initialized during onCreate(). All the members of this object are set.
private Training mTraining; is a class member
public class Training extends BaseModel {
...
#SerializedName("state")
public TrainingState state;
....
public TrainingPreview() {
}
This object is got from server (JSON), and I had a converter on this state to ensure this enum can't be null (I use GSON engine):
public class TrainingStateConverter extends EnumConverter<TrainingState> {
public static final Type TYPE = new TypeToken<TrainingState>() {}.getType();
#Override
protected TrainingState deserialize(String value) {
return TrainingState.fromString(value);
}
#Override
protected TrainingState getUnknownValue() {
return TrainingState.UNKNOWN;
}
}
During the setup, I've created the exercise list with the listener to show a specific exercise:
private void refreshExercisesList() {
final Runnable showTrainingParts = new Runnable() {
public void run() {
int nbItems = mCardExercises.setExercises(mTraining.training, mTraining.state,
new FlatCardTrainingProfilePartExercisesView.OnClickExerciseListener() {
#Override
public void showPart(String trainingPartId, int index) {
onClickOnExercisesList(trainingPartId, index);
}
});
}
};
}
...
}
My onClickOnExercisesList() method:
private void onClickOnExercisesList(String trainingPartId, int index) {
...
switch (mTraining.state) {
...
This Activity code works perfectly since couple of months, but yesterday there was a NullPointerException on switch (mTraining.state) :
int com.xxx.model.training.TrainingState.ordinal()' on a null object reference
com.xxx.ui.training.TrainingActivity.onClickOnExercisesList
How is possible guys?
Thank you very much for your help!
This would occur if state did not appear in the JSON.
The TypeConverter is only used if there is a value in the JSON to convert. If the value isn't present, then there's nothing to convert, so the value is whatever the default is, which is null, because you didn't set it:
#SerializedName("state")
public TrainingState state;
To fix the issue, initialize the variable to a default value:
#SerializedName("state")
public TrainingState state = TrainingState.UNKNOWN;
I'm using a custom parcelable object called GameSettings to pass a number of settings between Activites within an Android app (developed using MonoDroid). The settings are stored as properties on this GameSettings class, and up until now they've all been simple integers which I've been able to parcel just fine using Parcel.WriteInt() and Parcel.ReadInt().
I've just added a new property to GameSettings called CelebrityNames which is of type List<string>, and I'm trying to pass this in the same way but when ReadStringList() is called the property gets populated with an empty list (despite a non-empty list being written to the parcel prior to this using WriteStringList()). The parcel is being passed from NameEntryActivity to GameRoundActivity.
GameSettings.cs
using System;
using System.Collections.Generic;
using Android.OS;
using Java.Interop;
using Object = Java.Lang.Object;
namespace Celebrities
{
public class GameSettings : Object, IParcelable
{
private static readonly GenericParcelableCreator<GameSettings> _creator
= new GenericParcelableCreator<GameSettings>((parcel) => new GameSettings(parcel));
[ExportField("CREATOR")]
public static GenericParcelableCreator<GameSettings> InitializeCreator()
{
return _creator;
}
public int NumberOfPlayers { get; set; }
public int NumberOfTeams { get; set; }
public int CelebritiesPerPlayer { get; set; }
public int SecondsPerRound { get; set; }
private List<string> _celebrityNames;
public List<string> CelebrityNames {
get
{
_celebrityNames.Shuffle ();
return _celebrityNames;
}
set
{
_celebrityNames = value;
}
}
public GameSettings (int players, int teams, int celebrities, int secondsPerRound)
{
NumberOfPlayers = players;
NumberOfTeams = teams;
CelebritiesPerPlayer = celebrities;
SecondsPerRound = secondsPerRound;
}
private GameSettings(Parcel parcel) : this(parcel.ReadInt (), parcel.ReadInt (), parcel.ReadInt (), parcel.ReadInt ())
{
if (_celebrityNames == null)
{
_celebrityNames = new List<string>();
}
parcel.ReadStringList (_celebrityNames);
}
public void WriteToParcel(Parcel dest, ParcelableWriteFlags flags)
{
dest.WriteInt (NumberOfPlayers);
dest.WriteInt (NumberOfTeams);
dest.WriteInt (CelebritiesPerPlayer);
dest.WriteInt (SecondsPerRound);
dest.WriteStringList (_celebrityNames);
}
public int DescribeContents()
{
return 0;
}
}
}
Note: I'm using the backing variable _celebrityNames for parcelling as I have a custom getter that shuffles the list, which isn't necessary at this point. The problem is the same whether using the property or the variable.
GenericParcelableCreator.cs
using System;
using Android.OS;
using Object = Java.Lang.Object;
namespace Celebrities
{
public sealed class GenericParcelableCreator<T> : Object, IParcelableCreator
where T : Object, new()
{
private readonly Func<Parcel, T> _createFunc;
public GenericParcelableCreator(Func<Parcel, T> createFromParcelFunc)
{
_createFunc = createFromParcelFunc;
}
public Object CreateFromParcel(Parcel source)
{
return _createFunc(source);
}
public Object[] NewArray(int size)
{
return new T[size];
}
}
}
I'm including the relevant code from the Activity classes below (these are not the complete files for brevity, please ask if you think it would be helpful to see the rest too).
NameEntryActivity.cs (where I'm passing the parcel from)
public class NameEntryActivity : Activity
{
...
private GameSettings _gameSettings;
private List<string> _celebrityNames;
protected override void OnCreate (Bundle savedInstanceState)
{
...
_gameSettings = (Intent.Extras.GetParcelable ("GameSettings") as GameSettings);
_celebrityNames = new List<string> ();
...
}
...
private void MoveToNextCelebrity()
{
...
_gameSettings.CelebrityNames = _celebrityNames;
var intent = new Intent (this, typeof(GameRoundActivity));
intent.PutExtra("GameSettings", _gameSettings);
StartActivity (intent);
...
}
}
GameRoundActivity.cs (where I'm passing the parcel to)
public class GameRoundActivity : Activity
{
private GameSettings _gameSettings;
protected override void OnCreate (Bundle savedInstanceState)
{
base.OnCreate (savedInstanceState);
SetContentView (Resource.Layout.GameRound);
_gameSettings = (Intent.Extras.GetParcelable ("GameSettings") as GameSettings);
}
}
This is my first time developing an Android app, so it may well be that I've made a mistake somewhere in implementing the parcelling framework or have misunderstood it. Equally I've been looking at this code for so long that maybe I'm just missing a more general silly mistake :)
Thanks in advance!
I switched to using a string array instead of a list and it's now working using Parcel.WriteStringArray() and Parcel.CreateStringArray().
Obviously this wouldn't be applicable in every situation though so I'm still interested in why this was happening!
I just noticed the Property class http://developer.android.com/reference/android/util/Property.html . I can see some explanation of it here http://developer.android.com/about/versions/android-4.0.html#api but dont really understand the use cases of it. Would be great if someone can point me to some code snippets where I can understand this more.
Property is a wrapper to a Reflection.
For example, you have an object
public class A {
private int fieldOfA;
private int fieldTwo;
private int fieldThree;
public void setFieldOfA(int a) {
fieldOfA = a;
}
public int getFieldOfA() {
return fieldOfA;
}
public void setFieldTwo(int a) {
fieldTwo = a;
}
public int getFieldTwo() {
return fieldTwo;
}
public void setFieldThree(int a) {
fieldThree = a;
}
public int getFieldThree() {
return fieldThree;
}
}
If you need to update phew fields, you have to know all their names in the update method without Properties
private void updateValues(final A a, final int value) {
a.setFieldOfA(value);
a.setFieldTwo(value);
a.setFieldThree(value);
}
With Properties you can update only the properties.
Property aProperty = Property.of(A.class, int.class, "fieldOfA");
Property bProperty = Property.of(A.class, int.class, "fieldTwo");
Property cProperty = Property.of(A.class, int.class, "fieldThree");
Collection<Property<A, Integer>> properties = new HashSet<>();
properties.add(aProperty);
properties.add(bProperty);
properties.add(cProperty);
updateValues(a, 10, properties);
And the method would be
private void updateValues(final A a, final int value, final Collection<Property<A, Integer>> properties) {
for (final Property<A, Integer> property : properties) {
property.set(a, value);
}
}
As laalto memtioned, property animations use a similar mechanism.
One example would be property animations. The Property class provides an abstraction for attributes that can be changed over time to perform an animation.
I've just started using greenDAO.
How do I add an Enum property?
What I've Thought of: using the addIndex property of an entity.
private static void main() {
// TODO Auto-generated method stub
static Schema blah;
Entity unicorn = blah.addEntity("Weather");
unicorn.addIdProperty();
unicorn.addIntProperty("currentAirTemp");
unicorn.addIndex("shirtSize");
}
Is this the right way to do it?
Aim: I want to have a reference to shirtSize being from the set: {XS, S, M, L, XL, XXL}
Using GreenDAO 3 we now have the option to use #convert annotation with PropertyConverter
#Entity
public class User {
#Id
private Long id;
#Convert(converter = RoleConverter.class, columnType = String.class)
private Role role;
enum Role {
DEFAULT, AUTHOR, ADMIN
}
static class RoleConverter implements PropertyConverter<Role, String> {
#Override
public Role convertToEntityProperty(String databaseValue) {
return Role.valueOf(databaseValue);
}
#Override
public String convertToDatabaseValue(Role entityProperty) {
return entityProperty.name();
}
}
}
Read more at http://greenrobot.org/objectbox/documentation/custom-types/
Latest version of GreenDao (2.x) contains functionality which ideally suits your needs. There are a Custom Types which can serve enums very easily.
Enum
public enum ShirtSize {
XS(1),
S(2),
M(3),
L(4),
XL(5),
XXL(6);
private final int value;
ShirtSize(int value) {
this.value = value;
}
public int value() {
return value;
}
}
Converter
public class ShirtSizeConverter implements PropertyConverter<ShirtSize, Integer> {
#Override
public ShirtSize convertToEntityProperty(Integer databaseValue) {
if(databaseValue == null) {
return null;
} else {
for(ShirtSize value : ShirtSize.values()) {
if(value.value() == databaseValue) {
return value;
}
}
throw new DaoException("Can't convert ShirtSize from database value: " + databaseValue.toString());
}
}
#Override
public Integer convertToDatabaseValue(ShirtSize entityProperty) {
if(entityProperty == null) {
return null;
} else {
return entityProperty.value();
}
}
}
Entity field declaration (in generator)
entity.addIntProperty("ShirtSize").customType(
"com.your_package.ShirtSize",
"com.your_package.ShirtSizeConverter"
);
As far as I know, Enums are not supported by greenDAO due to their unstable nature.
Also they are an error-prone component to add to your database logic, since the values of the enum elements can change.
One option to get around this would be to add an Int property to the database and then map Enum ordinal values to that field, like so:
// add the int property to the entity
unicorn.addIntProperty("shirtSize");
// create the enum with static values
public enum ShirtSize {
XS(1), S(2), M(3), L(4), XL(5), XXL(6);
private final int value;
private ShirtSize(int value) {
this.value = value;
}
public int value() {
return value;
}
}
// set the ordinal value of the enum
weather.setShirtSize(ShirtSize.XL.value());