I am working on android studio. I am using fragments to make the app workflow. The app is basically for order taking. One customer can have more than one order. So for it, I have used RecyclerView. Below is my GUI
What do I want to do?
I want to update my Total Qty
and Order Value
in such a way that whenever the user enters the quantity both edittexts
should be updated. For example, in the above image, I searched for one product, its rate is 287.30 and when I entered the quantity 5 then Total Qty: 5
and Order Value: 287.30
. After that, I select another product and enter its quantity 8 then both edit text should be updated like Total Qty: 13
and Order Value: 509.15
, and when I remove any product then it should subtract the total quantity and order value.
The Total Qty:
and Order Value:
is in my main fragment. Below is my code
Fragment Layout
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:orientation="vertical">
<android.support.v7.widget.RecyclerView
android:id="@+id/products"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:focusable="true" />
</LinearLayout>
<LinearLayout
android:id="@+id/product_info"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10sp"
android:orientation="horizontal"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tv_product">
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight=".5"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Total Qty:" />
<EditText
android:id="@+id/total_qty"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:editable="false"
android:focusable="false"
android:focusableInTouchMode="false"
android:gravity="center_horizontal"
android:hint=""
android:inputType="number" />
</LinearLayout>
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight=".5"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Order Value :" />
<EditText
android:id="@+id/order_value"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:editable="false"
android:focusable="false"
android:focusableInTouchMode="false"
android:gravity="center_horizontal"
android:hint=""
android:inputType="none" />
</LinearLayout>
</LinearLayout>
Fragment Code
// created getter setters
public EditText totalQty,totalOrder;
public EditText getTotalQty(){
return totalQty;
}
public void setTotalQty(EditText et){
this.totalQty = et;
}
public EditText getTotalOrder(){return totalOrder;}
public void setTotalOrder(EditText ett){this.totalOrder=ett;}
addProduct.setOnClickListener(v -> {
mProducts.add(new ProductDetail());
mProductsAdapter.setProducts(mProducts);
productsRecyclerView.smoothScrollToPosition(mProducts.size() - 1);
});
Product Layout
<AutoCompleteTextView
android:id="@+id/tv_product"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10sp"
android:gravity="start"
android:hint="Enter Product"
android:inputType="text"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<LinearLayout
android:id="@+id/product_info"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10sp"
android:orientation="horizontal"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tv_product">
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight=".5"
android:orientation="vertical">
<EditText
android:id="@+id/prod_qty"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:editable="false"
android:focusable="true"
android:focusableInTouchMode="true"
android:gravity="left"
android:hint="Enter Quantity"
android:inputType="number" />
</LinearLayout>
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight=".5"
android:orientation="vertical">
<EditText
android:id="@+id/prod_price"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:editable="false"
android:focusable="false"
android:focusableInTouchMode="false"
android:gravity="left"
android:hint="Prod Price"
android:inputType="numberDecimal" />
</LinearLayout>
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight=".5"
android:orientation="vertical">
<EditText
android:id="@+id/prod_specs"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:editable="false"
android:focusable="false"
android:focusableInTouchMode="false"
android:gravity="left"
android:hint="Prod Specs"
android:inputType="none" />
</LinearLayout>
</LinearLayout>
<Button
android:id="@+id/btn_rmv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="1dp"
android:layout_marginBottom="1dp"
android:text="Remove Product"
android:textColor="@color/white"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/product_infos" />
Product Adapter
View productView = inflater.inflate(R.layout.product_layout, parent, false);
ProductViewHolder viewHolder = new ProductViewHolder(productView);
viewHolder.tvProduct.setAdapter(prodAdapter);
viewHolder.tvProduct.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count)
{
}
@Override
public void afterTextChanged(Editable s) {
mProductManager = new ProductManager(context);
int position = viewHolder.getAdapterPosition();
if (position != -1) {
ProductDetail productDetail = mProductManager.getProductByPrdName(s.toString());
mProducts.get(position).setProductNameFull(s.toString());
viewHolder.price.setText(productDetail.getPrice());
viewHolder.spec.setText(productDetail.getProductSpec());
viewHolder.pgrp.setText(productDetail.getProductGroup());
viewHolder.ccode.setText(productDetail.getCompanyCode());
viewHolder.cname.setText(productDetail.getCompanyName());
viewHolder.pcode.setText(productDetail.getProductCode());
}
}
});
viewHolder.quantity.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void afterTextChanged(Editable s) {
int position = viewHolder.getAdapterPosition();
if (position != -1) {
Integer val = null;
try {
val = Integer.valueOf(s.toString());
} catch (Exception e) {
}
mProducts.get(position).setQuantity(val);
}
}
});
viewHolder.removeBtn.setOnClickListener(v -> {
int position = viewHolder.getAdapterPosition();
if (position != -1) {
mProducts.remove(position);
notifyItemRemoved(position);
}
});
return viewHolder;
Update 1
Below is my Fragment Class
public class SurveyFormFragment extends Fragment {
.
.
.
.
private final LiveData<Integer> mQuantity = new MutableLiveData<>(0);
private final LiveData<Integer> mOrderValue = new MutableLiveData<>(0);
private ViewModel mViewModel;
private FragmentBinding mBinding;
@RequiresApi(api = Build.VERSION_CODES.M)
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
Log.d("myfragment", "onCreateView");
getActivity().setTitle(getString(R.string.title_new_survey));
mContext = getActivity();
if (view == null) {
view = inflater.inflate(R.layout.new_survey_form_layout, container, false);
mLinearLayout = view.findViewById(R.id.ll_prod);
this.initElements(view);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
this.initListeners(getActivity());
}
Bundle arguments = getArguments();
if (arguments != null && arguments.containsKey("booking_id")) {
isNewSurvey = false;
initSurveyData(arguments.getString("booking_id"));
this.updatingSurveyId = arguments.getString("booking_id");
}
}
Log.d("package", getActivity().getPackageName());
Log.d(TAG, Common.getCarrierName(getActivity()));
return view;
}
@Override
public void onStart() {
super.onStart();
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
Log.d("myfragment", "onActivityCreated");
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED);
Log.d("myfragment", "onCreate");
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d("myfragment", "onDestroy");
}
@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
//save now
savedInstanceState.putString("faisal_qayyum", consumerNameEditText.getText().toString());
super.onSaveInstanceState(savedInstanceState);
Log.d("myfragment", "onSaveInstanceState saving " + consumerNameEditText.getText().toString());
}
@Override
public void onViewStateRestored(Bundle savedInstanceState) {
super.onViewStateRestored(savedInstanceState);
//restore now
Log.d("myfragment", "onViewStateRestored");
}
.
.
.
.
}
Error
How can I add and then update the values?
Any help would be highly appreciated.
5
Answers
implement interface between adapter and fragment like this (it is just sodu to tell how you have to implement things also you have to check it out how to implement interfaces
===============================================================
and in adapter class
like this you can minus the quantity as well on removal of item
I will disappoint you by not sharing code. I will share you a simple graphic of what you need to do.
[
Fragment
]: displays your list of orders. When an event is triggered (user tapped something or changed something), forwards this event to a ViewModel. Also Observes for the ViewModel data state.[
ViewModel
]: exposes a series of functions to listen for user events (e.g. the user tapped this order, or the user changed the quantity of this order to NN, etc.). Also offers aLiveData
(orFlow
) with the State and Data your Fragment needs. Maybe even talks to the repository where this data originates from (where you save your Orders in your persistent or backend).Interactions:
When the UI modified something, you tell the viewModel: "This data changed, the order X, now has a quantity of Y".
The ViewModel will process this, update the
State
of the screen, and send the new data via yourLivedata/Flow
that the Fragment observes.When the Fragment receives this new data, it will update the UI accordingly, as that’s its most important job.
This whole thing has been greatly documented in the official Android recommended architecture guides. I suggest you start from there instead of trying to fight the framework and making your life more complicated.
Actually it’s releated with Interactions like
LiveData
.But rather than write a looot of code, simply I’m passing runnable to childs for call it when somethink changed 🙂
For example:
You should work with LiveData and DataBinding
In order to do so you activate it in your build.gradle file:
Then add the following code.
Add a ViewModel class:
In your fragment class add the following:
Add this to your product layout:
And this to your fragment layout:
Note that in both classes you need to get the viewmodel from the activity, not from the fragment itself, so you share the same viewmodel for both classes:
If you only get values of LiveData objects from within the UI Thread (which is the case in my code, as onChange of LiveData and binding updates are always called from the UI thread), you do not need to synchronize the lists in the ViewModel. Otherwise you need to make the whole story thread safe.
It is easy to understand that you want to take action when changing the value in a text field. So the first thing is to add a TextChangeListener or TextWatcher. Then, you have to work on the received text value to update the rest of the dependencies: Your product list or whatever it is.
Just a few basic things you need to take care of:
If you are working with fragment communication, propagate your changes by reloading in onResume of the fragment or activity. Or you can use EventBus or any other suitable mechanism.
Be sure to notify the adapter once you have changed the underlying data. I see a call to "notify**" after your code mProducts.get(position).setQuantity(val); is missing in your Product adapter. When modifying the underlying data, it is necessary to notify the adapter using "notify**" calls to update the recycler view.
The MutableLiveData error you posted above can be cleared with MutableLiveData declaration such as private final MutableLiveData mutableData = new MutableLiveData(0) in your case. You can have a function to just return LiveData as LiveData getData() { return mutableData; }