Unnecessary operations in view layer when observed object changes - android

Suppose that you listen for changes on a User object via viewmodel and observable livedata like this:
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
viewModel.getUser().observe(this, user -> {
// Update UI :
// Update userNameTextView
// Load profile image to imageView
// Update follower count
});
}
As observed User changes, you update the views. The actual change on object is probably just the follower count but you act like whole user object has changed Ex: setting the same image to imageview.
Isn't this bad practice and waste of resources? What should you do in this case?

One possible solution would be to use databinding for binding your User object by making it extend BaseObservable or making it's fields ObservableFields of their corresponding data types.
You can still use LiveData as the data container/channel between the View & the ViewModel for re-binding the whole User object.
And then for partial updates, you can notifyPropertyChanged on the fields you've changed, here is an example:
private static class User extends BaseObservable {
private String firstName;
private String lastName;
#Bindable
public String getFirstName() {
return this.firstName;
}
#Bindable
public String getLastName() {
return this.lastName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
notifyPropertyChanged(BR.firstName);
}
public void setLastName(String lastName) {
this.lastName = lastName;
notifyPropertyChanged(BR.lastName);
}
}

Related

Room: How to get actual balance of a member and use it for setter method to set balance of the specific member

I use Android Room for my database. I have a title screen and when I click on a button there, my activity opens. The activity shows me a list of members saved in the database. My entity Member consists of first name, surname and balance. I can add new members. The user can set first name and surname, the balance is automatically set with the value 0. I do have another entity called transactions, where I can add transaction and link transactions with members.
If I add two new transactions for a member a, the member a's balance goes up. I can click on a member in my member activity, then I see additional information of the member. There, I can see the actual balance he has with a SUM() query in my DAO.
But I don't know, how to use that query to set Balance of a member. I also don't know where(in which class) I should use the set Balance method of the member class.
Can anybody help me with this? If you need any specific code, just write in the comments and I'll add it to this question. Thanks!
Entity Member:
#TypeConverters(Converters.class)
#Entity(tableName = "member_table")
public class Member {
#PrimaryKey(autoGenerate = true)
#ColumnInfo(name = "MemberID")
private long id;
#ColumnInfo(name = "FirstName")
private String firstname;
#ColumnInfo(name = "Surname")
private String surname;
#ColumnInfo(name = "MemberBalance")
private BigDecimal balance;
private boolean isSelected = false;
public String getFirstname() {
return firstname;
}
public void setFirstname(String firstname) {
this.firstname = firstname;
}
public String getSurname() {
return surname;
}
public void setSurname(String surname) {
this.surname = surname;
}
public BigDecimal getBalance() {
return balance;
}
public void setBalance(BigDecimal balance) {
this.balance = balance;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public Member(String firstname, String surname) {
this.firstname = firstname;
this.surname = surname;
this.balance = new BigDecimal(0).setScale(2, RoundingMode.HALF_EVEN);
}
public void setSelected(boolean selected) {
isSelected = selected;
}
public boolean isSelected() {
return isSelected;
}
}
Entity Transaction:
#TypeConverters(Converters.class)
#Entity(tableName = "transaction_table")
public class Transaction {
#PrimaryKey(autoGenerate = true)
#ColumnInfo(name = "TransactionID")
private long id;
#ColumnInfo(name = "TransactionName")
private String transactionName;
#ColumnInfo(name = "TransactionBalance")
private BigDecimal balance;
public BigDecimal getBalance() {
return balance;
}
public void setBalance(BigDecimal balance) {
this.balance = balance;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getTransactionName() {
return transactionName;
}
public void setTransactionName(String transactionName) {
this.transactionName = transactionName;
}
public Transaction(String transactionName, BigDecimal balance) {
this.transactionName = transactionName;
this.balance = new BigDecimal(String.valueOf(balance)).setScale(2, RoundingMode.HALF_EVEN);
}
}
First picture: This is my member activity where I see the list of all members. The balance is always 0.00 because I never use setBalance() method, and the balance should be the value gotten by the getBalance() method.
Second picture: This is my transaction activity where I can see the list of all transactions.
Third picture: This activity starts when I click on a member. Then I see the member names and below all the transactions he took part. The balance of the member here is taken with a DAO-Query: SELECT SUM(TransactionBalance)FROM member_transaction_table INNER JOIN transaction_table ON transaction_table.TransactionID = member_transaction_table.Transaction_ID WHERE member_transaction_table.Member_ID =:memberid
I can't figure out how and where to let the app calculate the transactions. The value returned by the calculations should be set with the setBalance() method.
I would recommend not to use column balance in Member class at all as it looks like all data should be stored in this transaction entities. If you have to show balance for a particular Member you should calculate his balance every time. This approach has one major drawback it isn't effective from a performance point of view. If performance is important than having the balance column is a good idea, but you have to update the Member entity every time new transaction added to the system.
Solved my problem by setting balance when I add a transaction. So I take the value of the transaction and simply add it to my member balance.

How to make the changes made to the model effective with DataBinding after setModel?

DataBinding: how can I make sure that as a result of a modification of the data model the view is updated accordingly?
Eg:
public class MyActivity extends AppCompatActivity {
private MyActivityBinding mBinding;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mBinding = DataBindingUtil.setContentView(this, R.layout.my_activity);
mBinding.setMyModel(new MyModel());
}
public void onClickAnItem(View view) {
MyModel model = mBinding.getMyModel();
model.setField1 = "Jhon";
model.setField2 = "Dho";
mBinding.executePendingBindings();
}
}
In this case the model "MyModel" has been modified but view is not updated; what did I miss?
Reading documentation I found a solution, first of all:
Any plain old Java object (POJO) may be used for data binding, but modifying a POJO will not cause the UI to update!
To give MyModel data object the ability to notify when data changes I made this modifications:
private class MyModel extends BaseObservable {
private String field1;
private String field2;
#Bindable
public String getField1() {
return this.field1;
}
#Bindable
public String getField2() {
return this.field2;
}
public void setField1(String firstName) {
this.field1 = firstName;
notifyPropertyChanged(BR.field1);
}
public void setField2(String lastName) {
this.field2 = lastName;
notifyPropertyChanged(BR.field2);
}
}
I hope this can help someone else
Documentation here

How to collect data from a registration form which consists of two activities and submit to MySQL database

I'm developing an app, and I have a user registration form which is very long.
Since the registration form is long, I decided to use multiple activities for it, so the user will have to click on continue button to access the next part of the form.
The challenge I am facing is collecting the other information from previous activities and submitting it as one thing to MySQL database.
Can anyone show me how to collect the data from the form which consists of two activities and submit it to the database?
Create a model for all your requirements. For example,
import java.io.Serializable;
public class RegistrationModel implements Serializable
{
private String name, age, dob, address, pincode, hobby, profession;
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
public String getAge()
{
return age;
}
public void setAge(String age)
{
this.age = age;
}
public String getDob()
{
return dob;
}
public void setDob(String dob)
{
this.dob = dob;
}
public String getAddress()
{
return address;
}
public void setAddress(String address)
{
this.address = address;
}
public String getPincode()
{
return pincode;
}
public void setPincode(String pincode)
{
this.pincode = pincode;
}
public String getHobby()
{
return hobby;
}
public void setHobby(String hobby)
{
this.hobby = hobby;
}
public String getProfession()
{
return profession;
}
public void setProfession(String profession)
{
this.profession = profession;
}
}
In your first activity, when next button is clicked fill the model and pass it to the next activity using serialization.
RegistrationModel model = new RegistrationModel();
model.setName("XXX");
model.setAddress("XXX");
// Like this, fill all data in first activity
Intent intent = new Intent(MainActivity.this, SecondActivity.class);
intent.putExtra("Data", model);
startActivity(intent);
Then in the SecondActivity, get your pre-filled model like this:
RegistrationModel model = ((RegistrationModel)getIntent().getSerializableExtra("Data"))
And continue with the registration.
I think that a good approach to your problem is just use fragments instead of two activities. The main advantaje is that you can swich from one fragment to another easily.
I mean, you can have one main activity that holds the user object. then:
Activity shows the first fragment.
When user fill the form and push next, the fragment call back the activity with the form data. Use a callback interface for this.
Activity instantiate the user variable with data and shows second fragment.
When the user fill the second form and push finish, call back the activity with form data. Again you can do this with a callback interface.
Finally, the activity add the new data to the user variable and send all the information to database.
From documentation, how to communicate Activity and fragments.
https://developer.android.com/training/basics/fragments/communicating.html
Example of one fragment:
public class HeadlinesFragment extends Fragment {
OnFormFillesListener mCallback;
// Container Activity must implement this interface
public interface OnFormFillesListener {
public void onFirstForm(String name, String nick ...);
public void onFSecondForm(String character, int id ...);
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
// This makes sure that the container activity has implemented
// the callback interface. If not, it throws an exception
try {
mCallback = (OnFormFillesListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement OnFormFillesListener");
}
}
...
public void onNext() {
mCallback.onFirstForm(name, nick ...);
}
}
And the Activity:
public static class MainActivity extends Activity
implements OnFormFillesListener {
private User mUser;
...
public void onFirstForm(String name, String nick ...) {
// instantiate the user
mUser = new User (name, nick, ...);
// show second fragment
}
public void onFSecondForm(String character, int id ...) {
// add new data to user
mUser.setCharacter(character);
mUser.serId(id);
...
// send User to Database
}
}

How to send user details collected from different activities during signup to the final activity?

I am busy adding a signup feature to my app and I have 4 activities which I use to collect the user's information. The activities are supposed to collect the details input by the user and send them all to the final activity where they will be sent to the database.
The first activity asks users to choose whether they want to use their email or phone number to sign up.
The second activity asks users to choose their username and password.
The third activity asks the user to add more personal details as well as to add a profile picture to their account.
The final activity asks the users to input their geographic details and then sends all the information sent from the other activities to the database.
The problem I am facing is that if i use Intents, i need to send data from activity to activity. Which is giving me a lot of errors, how can I the information collected in each activity to the final one and then send them all in one go.
You can use Singleton design pattern which preserves single object between all activities.
For example:
public class SignUpSingleton {
private int emailOrPhone;
private String username;
private String password;
private String firstName;
private String lastName;
private String country;
private String city;
//remaining fields here
private static SignUpSingleton instance = new SignUpSingleton();
private SignUpSingleton(){}
public static SignUpSingleton getInstance(){
return instance;
}
public int getEmailOrPhone() {
return emailOrPhone;
}
public void setEmailOrPhone(int emailOrPhone) {
this.emailOrPhone = emailOrPhone;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
}
In the first activity:
SignUpSingleton.getInstance().setEmailOrPhone(1); //1 or 2
In the second activity:
SignUpSingleton.getInstance().setUsername("Tom");
SignUpSingleton.getInstance().setPassword("pass");
And so on for third and forth.
In the last activity you can send all data to the database at once, e.g:
storeInDb(
SignUpSingleton.getInstance().getEmailOrPhone(),
SignUpSingleton.getInstance().getUsername(),
SignUpSingleton.getInstance().getPassword(),
SignUpSingleton.getInstance().getFirstName(),
SignUpSingleton.getInstance().getLastName(),
//remaining params here
);
Assuming you have a method called storeInDb or alike, and inside it the database code.
Please use my code i am sure to help you
i am giving demo class of three activity
is this your FirstActivity
public class FirstActivity extends Activity
{
public void onCreate(Bundle bundle)
{
super.onCreate(bundle);
setContentView(R.layout.main);
}
public void onResume()
{
super.onResume();
//put this code as per your requirement
// i am just giving idea
Intent i=new Intent(this,SecondActivity.class);
i.putExtra("name","Riyaz Parasara");
i.putExtra("email","riyazparasara#gmail.com");
i.putExtra("phone","+918955094537");
i.putExtra("country","india");
startActivity(i);
}
}
is this your secondActivity
public class SecondActivity extends Activity
{
private String name,email,phone,county;
public void onCreate(Bundle bundle)
{
super.onCreate(bundle);
setContentView(R.layout.main);
//this is first activity data
//you can get firstactivity data in second activity
//and store data into varialbles
name=getIntent().getStringExtra("name");
email=getIntent().getStringExtra("email");
phone=getIntent().getStringExtra("phone");
country=getIntent().getStringExtra("country");
}
public void onResume()
{
super.onResume();
//put this code as per your requirement
// i am just giving idea
Intent i=new Intent(this,ThirdActivity.class);
//this is first activity data put in intent
i.putExtra("name",name);
i.putExtra("email",email);
i.putExtra("phone",phone);
i.putExtra("country",country);
//this is second activity data you also put on this intent
i.putExtra("sex","male");
i.putExtra("age","24");
i.putExtra("city","jaipur");
startActivity(i);
}
}
is this your FinalActivity please read code comments carefully
public class FinalActivity extends Activity
{
private String name,email,phone,county,sex,age,city;
public void onCreate(Bundle bundle)
{
super.onCreate(bundle);
setContentView(R.layout.main);
//this is first activity data and second activity data
//you can get firstactivity and secondactivity data in Final activity
//and store data into varialbles
name=getIntent().getStringExtra("name");
email=getIntent().getStringExtra("email");
phone=getIntent().getStringExtra("phone");
country=getIntent().getStringExtra("country");
sex=getIntent().getStringExtra("sex");
age=getIntent().getStringExtra("age");
city=getIntent().getStringExtra("city");
//all data are in instance variable please use this data whenever
}
public void onResume()
{
super.onResume();
//if you need to send data another activity
//please repeat again previous steps as per your requirement
}
}
Use a POJO class User, for instance, which will have all the attributes(name, email, location to name a few) you're likely be needing to complete the Sign up process.
Pass and update this User class object in every activity. The User class needs to implement Serializable or Percelable in order to be passable through Intent though.
public class User implements Serilizable {
private String usename;
private String email;
private Location location;
// Other attributes
// Getters and Setters
}

Someone knows how to use card views with Realm?

I want to use card views in my app but I'm using Realm database, I really appreciate a brief explanation or an example. Thank you :)
Card View is User Interface that you use to show your content to the the user. For example, A card containing, the name and age of the user, image of the user etc. Whereas Realm is a mobile database, which stores the actual values of the user like name, age, path of the image and so on.
So, you use Card View to display the values and Realm to store the actual values.
More read up on Card View can be done here
and read up on Realm can be from here
public class User extends RealmObject {
#PrimaryKey
private String id;
private String firstName;
private String lastName;
private int age;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
In Your activity, You can store values to the Realm object like below,
User user = new User()
user.setFirstName("John")
user.setLastName("Kennedy")
user.setAge(40)
Realm realm = Realm.getInstance(this)
realm.executeTransaction {
realm.copyToRealmOrUpdate(user)
}
//Below is written in Kotlin language. You can find similar one in Java from the link given
val userJohn: User = realm.where(User::class.java)?.equalTo("firstName", "John").findFirst()
//Values of User John can be accessed like below
println(userJohn.firstName)
println(userJohn.lastName)
println(userJohn.age)
The above is Realm Example.
Below are some of the card view examples
http://code.tutsplus.com/tutorials/getting-started-with-recyclerview-and-cardview-on-android--cms-23465
http://javatechig.com/android/android-cardview-example
http://www.truiton.com/2015/03/android-cardview-example/
Because Realm makes the Objects for you, you simply use the getter and setter methods to populate the views on your cards. This would be done with the Adapter associated with your RecyclerView, or whatever List type View you are using.

Categories

Resources