Reputation: 332
I'm having trouble making two Edit Text views that update when one is changed. To provide some context, see the following image:
Also the view in action (can't have it embedded apparently.):
https://i.imgur.com/an6Kodx.mp4
Here, we add targets (T1, T2, T3 etc.), then draw an arc and user may set start and finish points of the camera (gray and red icons respectively.) Then, we get the total move value (in degrees). This value will determine amount the motor will rotate (The app is basically a controller for users to have automated photo-shoots).
What I try to achieve is that, when user enters a photo number, right edittext divides total move degrees to that count and show angle per photo and vice-versa.
However, I'm a bit lost among all the online content demonstrating various examples (like password strength etc.)
I've included DataBinding on gradle.
I've created a custom class (RotaryPhotoShoot) to have a model of three main parameters (angle per shoot, number of photos and total move).
I've moved my cosntraint layout to layout root.
I've created data as seen on following code blocks.
RotaryPhotoShoow.java (my model)
package com.example.macrorecapp.models;
import androidx.databinding.BaseObservable;
import androidx.databinding.Bindable;
public class RotaryPhotoShoot extends BaseObservable {
private static final String TAG = "Rotary Photo Shoot";
private float anglePerPhotos;
private int numberOfPhotos;
private int totalMoveDegrees;
public RotaryPhotoShoot(float anglePerPhotos,int numberOfPhotos, int totalMoveDegrees) {
this.anglePerPhotos = anglePerPhotos;
this.numberOfPhotos = numberOfPhotos;
this.totalMoveDegrees = totalMoveDegrees;
}
@Bindable
public float getAnglePerPhotos() {
return anglePerPhotos;
}
@Bindable
public int getNumberOfPhotos() {
return numberOfPhotos;
}
@Bindable
public int getTotalMoveDegrees() {
return totalMoveDegrees;
}
@Bindable
public void setAnglePerPhotos(float anglePerPhotos) {
this.anglePerPhotos = anglePerPhotos;
}
@Bindable
public void setNumberOfPhotos(int numberOfPhotos) {
this.numberOfPhotos = numberOfPhotos;
}
@Bindable
public void setTotalMoveDegrees(int totalMoveDegrees) {
this.totalMoveDegrees = totalMoveDegrees;
}
}
activity_rotary_photo_settings.xml
<?xml version="1.0" encoding="utf-8"?>
<layout>
<data>
<variable
name="photoShoot"
type="com.example.macrorecapp.models.RotaryPhotoShoot" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/appMainBackground"
tools:context=".features.rotary.RotaryPhotoSettings">
...
<com.example.macrorecapp.features.shared.views.RotaryView
android:id="@+id/rotaryPhotoView"
android:layout_width="360dp"
android:layout_height="360dp"
app:isClockwise="true"
app:targetList="@array/targets"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/h_guideline1"
app:layout_constraintBottom_toTopOf="@id/h_guideline2" />
<EditText
android:id="@+id/numberOfPhotosEdittext"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{``+photoShoot.numberOfPhotos}"
android:textAlignment="center"
android:ems="4"
android:textSize="15sp"
android:textColor="#FFFFFFFF"
app:layout_constraintBottom_toTopOf="@+id/numberOfPhotosSubtext"
app:layout_constraintEnd_toStartOf="@id/v_guideline"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/numberOfPhotosBG"
android:importantForAutofill="no"
android:inputType="number" />
...
<EditText
android:id="@+id/anglePerPhotosEdittext"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{``+photoShoot.anglePerPhotos+(char) 0x00B0}"
android:textAlignment="center"
android:ems="4"
android:textSize="15sp"
android:textColor="#FFFFFFFF"
app:layout_constraintBottom_toTopOf="@+id/anglePerPhotosSubtext"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@id/v_guideline"
app:layout_constraintTop_toTopOf="@+id/anglePerPhotosBG"
android:importantForAutofill="no"
android:inputType="numberDecimal" />
...
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
And finally RotaryPhotoSettings.java
package com.example.macrorecapp.features.rotary;
import androidx.appcompat.app.AppCompatActivity;
import androidx.databinding.DataBindingUtil;
import android.os.Bundle;
import android.view.View;
import android.view.animation.AlphaAnimation;
import com.example.macrorecapp.R;
import com.example.macrorecapp.databinding.ActivityRotaryPhotoSettingsBinding;
//import com.example.macrorecapp.features.shared.views.RotaryView;
import com.example.macrorecapp.models.RotaryPhotoShoot;
public class RotaryPhotoSettings extends AppCompatActivity {
private AlphaAnimation buttonClick = new AlphaAnimation(1F, 0.2F);
//RotaryView mPhotoRotaryView;
//private int mTotalMoveInDegrees;
ActivityRotaryPhotoSettingsBinding mBinding;
RotaryPhotoShoot mRotaryPhotoShoot;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mBinding = DataBindingUtil.setContentView(this, R.layout.activity_rotary_photo_settings);
mRotaryPhotoShoot = new RotaryPhotoShoot(6.88f, 25, 178);
mBinding.setPhotoShoot(mRotaryPhotoShoot);
//mPhotoRotaryView = findViewById(R.id.rotaryPhotoView);
//mPhotoRotaryView.addTarget(300);
//mTotalMoveInDegrees = mPhotoRotaryView.getTotalMoveInDegrees();
}
public void goBack(View view) {
view.startAnimation(buttonClick);
finish();
}
public void openThreeSixtyPhotoRotary(View view) {
}
}
Currently I have no errors whatsoever and I'm sure I'll be able change views one way when I programmatically set them in activity. What I feel like I should do is, first use @={} syntax in xmls to begin with. Then I may need to have custom adapters or binders. I've also seen that people use ObservableInt etc. which I got a bit lost. I needed to set my getTotalMove function to set static to get it from RotaryView.java but from then on I couldn't progress.
I'd like to have some pointers what to do onward. I think I can easily handle rounding up numbers where I implement the custom binder/adapter. I know for example the angle may be decimal while the photo count needs to be integer. I will be rounding up photo count and change the angle itself to closest possible value once it is done being edited. I will also need to determine whether start and end points will be included in the interval. Like, for 100 degrees, with 20 degrees per shoot, it'd be like this:
0: S__S__S__S__S__S :100 Thus 6 photos etc.
Before I implement any listeners etc., I figured I could ask here first, because obviously point of using the Data Binding library is to get rid of bunch of listeners and so on. I would appreciate some sort of example where two EditText views change eachother.
Once I figure out how to set non-edited EditText, I'll be dealing with extra considerations I mentioned above, but first I need to get done with two way binding part. I suppose this "two way" is between view and view model, not directly between views, obviously. So I don't know if I can have a trick like @={``+photoShoot.totalMove/photoShoot.anglePerPhoto} etc. in xml.
Anyways, the post is much longer than it is supposed to be, my apologies. This looked pretty straight-forward at first glance, but the more I look into it, the more complicated it gets. Maybe I'm just confusing myself.
Upvotes: 1
Views: 1002
Reputation: 1
This solved my problem:
android:text="@{String.valueOf(product.quantityInventory + product.quantityShop)}"
Upvotes: 0
Reputation: 332
I'd like to add some partial-answer to my own question. I tried to adjust info that I had from following link in my own use case:
https://www.bignerdranch.com/blog/two-way-data-binding-on-android-observing-your-view-with-xml/
I managed to change angle box (one on the right) with following changes:
I deleted some unnecessary variables in my custom view you see above and added a public "Total Move" getter. I use this in my model class RotaryPhotoShoot.
I also added @={} in my xml as you can see updated code below. This combined with notifyPropertyChanged(com.example.macrorecapp.BR.numberOfPhotos);
made it possible to update angle box.
Before adding another wall of text, I'll just add the relevant parts of my code for further reference to other people.
RotaryPhotoSettings.java (The activity class that utilizes binding.)
package com.example.macrorecapp.features.rotary;
import androidx.appcompat.app.AppCompatActivity;
import androidx.databinding.DataBindingUtil;
import android.os.Bundle;
import android.view.View;
import android.view.animation.AlphaAnimation;
import com.example.macrorecapp.R;
import com.example.macrorecapp.databinding.ActivityRotaryPhotoSettingsBinding;
import com.example.macrorecapp.models.RotaryPhotoShoot;
public class RotaryPhotoSettings extends AppCompatActivity {
private AlphaAnimation buttonClick = new AlphaAnimation(1F, 0.2F);
//RotaryView mPhotoRotaryView;
//private int mTotalMoveInDegrees;
ActivityRotaryPhotoSettingsBinding mBinding;
RotaryPhotoShoot mRotaryPhotoShoot;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mBinding = DataBindingUtil.setContentView(this, R.layout.activity_rotary_photo_settings);
mRotaryPhotoShoot = new RotaryPhotoShoot(6.88f, 25);
mBinding.setPhotoShoot(mRotaryPhotoShoot);
//mPhotoRotaryView = findViewById(R.id.rotaryPhotoView);
//mPhotoRotaryView.addTarget(300);
//mTotalMoveInDegrees = mPhotoRotaryView.getTotalMoveInDegrees();
}
public void goBack(View view) {
view.startAnimation(buttonClick);
finish();
}
public void openThreeSixtyPhotoRotary(View view) {
}
}
My model class, RotaryPhotoShoot.java
package com.example.macrorecapp.models;
import androidx.databinding.BaseObservable;
import androidx.databinding.Bindable;
import com.example.macrorecapp.features.shared.views.RotaryView;
public class RotaryPhotoShoot extends BaseObservable {
private static final String TAG = "Rotary Photo Shoot";
private float anglePerPhotos;
private int numberOfPhotos;
public RotaryPhotoShoot(float anglePerPhotos, int numberOfPhotos) {
this.anglePerPhotos = anglePerPhotos;
this.numberOfPhotos = numberOfPhotos;
}
@Bindable
public float getAnglePerPhotos() {
return RotaryView.getTotalMoveInDegrees()/(float) numberOfPhotos;
}
@Bindable
public int getNumberOfPhotos() {
return numberOfPhotos;
}
@Bindable
public void setAnglePerPhotos(float anglePerPhotos) {
this.anglePerPhotos = RotaryView.getTotalMoveInDegrees()/numberOfPhotos;
}
@Bindable
public void setNumberOfPhotos(int numberOfPhotos) {
this.numberOfPhotos = numberOfPhotos;
notifyPropertyChanged(com.example.macrorecapp.BR.numberOfPhotos);
notifyPropertyChanged(com.example.macrorecapp.BR.anglePerPhotos);
}
}
The activity layout file that have views in it, activity_rotary_photo_settings.xml
<?xml version="1.0" encoding="utf-8"?>
<layout>
<data>
<variable
name="photoShoot"
type="com.example.macrorecapp.models.RotaryPhotoShoot" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/appMainBackground"
tools:context=".features.rotary.RotaryPhotoSettings"
android:focusable="true"
android:focusableInTouchMode="true">
<com.example.macrorecapp.features.shared.views.RotaryView
android:id="@+id/rotaryPhotoView"
android:layout_width="360dp"
android:layout_height="360dp"
app:isClockwise="true"
app:targetList="@array/targets"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/h_guideline1"
app:layout_constraintBottom_toTopOf="@id/h_guideline2" />
<EditText
android:id="@+id/numberOfPhotosEdittext"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@={``+photoShoot.numberOfPhotos}"
android:textAlignment="center"
android:ems="4"
android:textSize="15sp"
android:textColor="#FFFFFFFF"
app:layout_constraintBottom_toTopOf="@+id/numberOfPhotosSubtext"
app:layout_constraintEnd_toStartOf="@id/v_guideline"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/numberOfPhotosBG"
android:importantForAutofill="no"
android:inputType="number" />
<EditText
android:id="@+id/anglePerPhotosEdittext"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{``+String.format(`%.2f`, photoShoot.anglePerPhotos)+(char) 0x00B0}"
android:textAlignment="center"
android:ems="4"
android:textSize="15sp"
android:textColor="#FFFFFFFF"
app:layout_constraintBottom_toTopOf="@+id/anglePerPhotosSubtext"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@id/v_guideline"
app:layout_constraintTop_toTopOf="@+id/anglePerPhotosBG"
android:importantForAutofill="no"
android:inputType="numberDecimal" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
Current problems that I could use some markers:
I need cross changes, currently I don't know how to tell whether a change is coming from the EditText being changed by typing or not. When the change is coming from other box, I will format/round the value properly and update the EditText view.
I could just use a bool value that I would toggle depending on whether the change is coming from manual editing or value changing progromatically. This would help me prevent infinite loop. However, as I said above, I am not sure what to listen to in order to achieve that.
Another behavior I would like to have is that, when camera start-finish icons are moved and TotalMove (in degrees) changed, I want to have numberOfPhotos fixed and update anglePerPhotos only. I may need to add binding in RotaryView.java for that. If this is an overkill, I may just add a trigger/listener on RotaryPhotoShoot. Currently. when I make a change in numberOfPhotos after I change the camera positions, angle is calculated properly as expected.
One little bug(?) I have is that, I cannot delete the last digit in numberOfPhotos field. See the following webm video below:
https://gfycat.com/distortedyoungdairycow
One thing I've realized is that, getter and setters in model class alone achieves what I need to do. This indeed removes the need to mess around with listeners and custom adapters. Since I'm using two EditTexts interchangeably, I may end up using them still.
Note that you can use any built-in Java functions (see string formatting I used in anglePerPhotos field). If necessary, I know how to import a class in <data></data>
block.
I'll add one more link before I finish this update-answer for those who may be lost how to set if Data Binding in their project for the first time:
https://www.youtube.com/watch?v=v4XO_y3RErI
Upvotes: 1