I have made a very basic weighted average calculator as Android Application (in Java) and it worked fine.
The user just inputs 3 values a
, b
, c
in three EditText
s and it calculates formula:
d = 0.25 * a + 0.25 * b + 0.5 * c;
Result of this mathematical equation is displayed in a TextView
, all of this in the MainActivity
at the press of a button.
But after creating a new project for better UI (used Android Studio template Navigation Drawer Activity) I realized that my code is not working.
How could i modify that calculator to get input in my fragment (named CalculatorWeightedFragment
) to make calculations and display the result in a TextView in the same fragment when my button is pressed?
At this point all I’m getting is the display of something static and no action assigned to the button.
The code is below:
CalculatorWeightedFragment.java
public class CalculatorWeightedFragment extends Fragment { private CalculatorWeightedViewModel calculatorWeightedViewModel; public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { calculatorWeightedViewModel = ViewModelProviders.of(this).get(calculatorWeightedViewModel.class); View root = inflater.inflate(R.layout.fragment_calculator_w, container, false); final TextView textView = root.findViewById(R.id.id_result); // result d stored in id_result calculatorWeightedViewModel.takeText().observe(getViewLifecycleOwner(), new Observer<String>() { @Override public void onChanged(@Nullable String str) { textView.setText(str); } }); return root; } }
CalculatorWeightedViewModel.java
public class CalculatorWeightedViewModel extends ViewModel { private MutableLiveData<String> testString; public CalculatorWeightedViewModel() { testString = new MutableLiveData<>(); testString.setValue("" + 0.5); } public LiveData<String> takeText() { return testString; } }
Advertisement
Answer
Base on your xml layout content:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/root" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <EditText android:id="@+id/id_a" android:layout_width="match_parent" android:layout_height="wrap_content" /> <EditText android:id="@+id/id_b" android:layout_width="match_parent" android:layout_height="wrap_content" /> <EditText android:id="@+id/id_c" android:layout_width="match_parent" android:layout_height="wrap_content" /> <Button android:id="@+id/id_buton_rata" android:layout_width="match_parent" android:layout_height="wrap_content" android:onClick="calculateFunction" /> <TextView android:id="@+id/id_result" android:layout_width="match_parent" android:layout_height="wrap_content" /> </LinearLayout>
Example #1 (without LiveData
and ViewModel
)
I create some new simple fragment (as example)
public class CalculatorWeightedFragment extends Fragment { private Button button; private EditText editTextA; private EditText editTextB; private EditText editTextC; private TextView textViewD; @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { return inflater.inflate(R.layout.fragment_calculator_w, container, false); } @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { button = view.findViewById(R.id.id_buton_rata); editTextA = view.findViewById(R.id.id_a); editTextB = view.findViewById(R.id.id_b); editTextC = view.findViewById(R.id.id_c); textViewD = view.findViewById(R.id.id_result); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { double result = calculateResult(); String output = String.format("Result: %s", result); textViewD.setText(output); } }); } private double calculateResult() { try { int a = Integer.parseInt(editTextA.getText().toString()); int b = Integer.parseInt(editTextB.getText().toString()); int c = Integer.parseInt(editTextC.getText().toString()); // Result return 0.25 * a + 0.25 * b + 0.5 * c; } catch (NumberFormatException e) { e.printStackTrace(); return 0; } } }
Example #2 (with LiveData
and ViewModel
)
When you are using LiveData
and ViewModel
, you no need button in your Fragment
. You can observe all of the changes.
Activity where you are creating this fragment. To have ViewModel in Fragment, you can use dependency injection (e.g. using Dagger library) or get it from ViewModelProviders
(or AndroidViewModelFactory
).
Here, to make example as simple as possible, I’m passing it via constructor.
Activity
public class MainActivity extends AppCompatActivity { @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); CalculatorWeightedViewModel vm = new CalculatorWeightedViewModel(); CalculatorWeightedFragment fragment = new CalculatorWeightedFragment(vm); FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); transaction.add(R.id.frame_layout, fragment); transaction.commit(); } }
ViewModel
You can store you values (a
, b
, c
) as fieldsfor different purpose. Here, I’m recalculating result live, without any extra fiels. To make it as simple as possible.
package com.example.stack_android; import androidx.lifecycle.LiveData; import androidx.lifecycle.MutableLiveData; import androidx.lifecycle.ViewModel; public class CalculatorWeightedViewModel extends ViewModel { private MutableLiveData<String> testString = new MutableLiveData<>(); public LiveData<String> takeText() { return testString; } void updateValues(double a, double b, double c) { double result = recalculate(a, b, c); String value = "Result: " + result; testString.postValue(value); } void showError() { testString.postValue("Wrong Value"); } private double recalculate(double a, double b, double c) { return 0.25 * a + 0.25 * b + 0.5 * c; } }
Fragment:
Additionaly you can create separate listeners (for each of the fields). Here, I’m using one (named TextWatcher listener
) for all of the fields.
public class CalculatorWeightedFragment extends Fragment { private CalculatorWeightedViewModel viewModel; private EditText editTextA; private EditText editTextB; private EditText editTextC; private TextView textViewD; public CalculatorWeightedFragment(CalculatorWeightedViewModel viewModel) { this.viewModel = viewModel; } @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { return inflater.inflate(R.layout.fragment_calculator_w, container, false); } @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { editTextA = view.findViewById(R.id.id_a); editTextB = view.findViewById(R.id.id_b); editTextC = view.findViewById(R.id.id_c); textViewD = view.findViewById(R.id.id_result); TextWatcher listener = new TextWatcher() { @Override public void onTextChanged(CharSequence s, int start, int before, int count) { textChanged(); } @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { // Not used } @Override public void afterTextChanged(Editable s) { // Not used } }; editTextA.addTextChangedListener(listener); editTextB.addTextChangedListener(listener); editTextC.addTextChangedListener(listener); viewModel.takeText().observe(getViewLifecycleOwner(), new Observer<String>() { @Override public void onChanged(String newValue) { textViewD.setText(newValue); } }); } private void textChanged() { try { int a = Integer.parseInt(editTextA.getText().toString()); int b = Integer.parseInt(editTextB.getText().toString()); int c = Integer.parseInt(editTextC.getText().toString()); viewModel.updateValues(a, b, c); } catch (NumberFormatException e) { viewModel.showError(); e.printStackTrace(); } } }
It should works like there: