skip to Main Content

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

enter image description here

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

enter image description here

How can I add and then update the values?

Any help would be highly appreciated.

5

Answers


  1. 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

    ===============================================================

    public frgment extends Fragment implements GetValueInterface{
    
    in onCreate function{
    int totalquantity;
    
    call this after adapter initialization 
    adapter.registergetValueInterface(this)
    
    }
    
     
    @Override Void setQuantity(Int _gotquantity){
    _gotquantity+=_gotquantity;
    totalquantityedittext.setText(_gotquantity,TextView.BufferType.EDITABLE)
    
    
    }
    
    }
    

    and in adapter class

    Adapter class
    {
    
    public GetValueInterface instance;
    
    function registergetValueInterface(GetValueInterface obj){
    
    instance=obj;
    
    }
    
    
     //and in quantity edittext text watcher 
    
    call instance.setQuantity(quanity)
    
    }
    

    like this you can minus the quantity as well on removal of item

    Login or Signup to reply.
  2. 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 a LiveData (or Flow) 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 your Livedata/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.

    Login or Signup to reply.
  3. 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:

    class Root{
       ...
       public void addItem(){
          ItemController cont = new ItemController(() -> updateSums());
          ...
       }
    
       public void updateSums(){
          //TODO
       }
    }
    
    class ItemController{
       Runnable updater;
       public ItemController(Runnable updater){
          this.updater = updater;
       }
       public void onSomethingChanged(){
          updater.run();
       }
    }
    
    Login or Signup to reply.
  4. You should work with LiveData and DataBinding

    In order to do so you activate it in your build.gradle file:

    android {
        ...
        buildFeatures {
            dataBinding true
        }
    }
    

    Then add the following code.

    Add a ViewModel class:

    public class ViewModel extends AndroidViewModel
    {
        private final MediatorLiveData<Integer> mTotalQuantity = new MediatorLiveData<>();
        private final MediatorLiveData<Integer> mTotalOrder = new MediatorLiveData<>();
        private final List<LiveData<Integer>> mOrders = new ArrayList<>();
        private final List<LiveData<Integer>> mQuantities = new ArrayList<>();
    
        public ViewModel(@NonNull Application application)
        {
            super(application);
            mTotalOrder.setValue(0);
            mTotalQuantity.setValue(0);
        }
    
        public LiveData<Integer> getTotal()
        {
            return mTotalOrder;
        }
    
        public LiveData<Integer> getQuantity()
        {
            return mTotalQuantity;
        }
    
        public void attachQuantity(LiveData<Integer> quantity)
        {
            mQuantities.add(quantity);
            mTotalQuantity.addSource(quantity, (q) -> calculateQuantities());
        }
    
        public void detachQuantity(LiveData<Integer> quantity)
        {
            mQuantities.remove(quantity);
            mTotalQuantity.removeSource(quantity);
            calculateQuantities();
        }
    
        private void calculateQuantities()
        {
            int total = 0;
            for (LiveData<Integer> quantity : mQuantities)
            {
                Integer value = quantity.getValue();
                if (value != null)
                {
                    total += value;
                }
            }
            mTotalQuantity.setValue(total);
        }
    
        public void attachTotal(LiveData<Integer> total)
        {
            mOrders.add(total);
            mTotalOrder.addSource(total, (q) -> calculateOrders());
        }
    
        public void detachTotal(LiveData<Integer> total)
        {
            mOrders.remove(total);
            mTotalOrder.removeSource(total);
            calculateOrders();
        }
    
        private void calculateOrders()
        {
            int total = 0;
            for (LiveData<Integer> order : mOrders)
            {
                Integer value = order.getValue();
                if (value != null)
                {
                    total += value;
                }
            }
            mTotalOrder.setValue(total);
        }
    }
    

    In your fragment class add the following:

    private LiveData<Integer> mQuantity = new MutableLiveData<>(0);
    private LiveData<Integer> mOrderValue = new MutableLiveData<>(0);
    private ViewModel mViewModel;
    private FragmentBinding mBinding;
    
    @Override
    public void onCreate(@Nullable Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        mViewModel = new ViewModelProvider(requireActivity()).get(ViewModel.class);
        mViewModel.attachQuantity(mQuantity);
        mViewModel.attachTotal(mOrderValue);
    }
    
    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState)
    {
        mBinding = DataBindingUtil.setContentView(this, R.layout.fragment);
        mBinding.quantity = mQuantity;
        mBinding.order_value = mOrderValue;
    }
    
    @Override
    public void onDestroy()
    {
        super.onDestroy();
        mViewModel.detachQuantity(mQuantity);
        mViewModel.detachTotal(mOrderValue);
    }
    

    Add this to your product layout:

    <layout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto">
        <data>
    
            <variable
                name="quantity"
                type="android.arch.lifecycle.MutableLiveData&lt;Integer&gt;" />
    
            <variable
                name="order_value"
                type="android.arch.lifecycle.MutableLiveData&lt;Integer&gt;" />
        </data>
                ...
    
                <EditText
                    android:id="@+id/prod_qty"
                    
                    // This does the magic
                    android:text="@{quantity}" />
                ...
    
                <EditText
                    android:id="@+id/prod_price"
                    
                    // And this of course...
                    android:text="@{order_value}" />
                ...
    </layout>
    

    And this to your fragment layout:

    <layout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto">
        <data>
    
            <variable
                name="viewmodel"
                type="yourpackage.ViewModel" />
        </data>
                ...
    
                <EditText
                    android:id="@+id/total_qty"
                    
                    // This does the magic
                    android:text="@{viewmodel.quantity}" />
                ...
    
                <EditText
                    android:id="@+id/order_value"
                    
                    // And this of course..
                    android:text="@{viewmodel.total}" />
                ...
    </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:

    ViewModelProvider(requireActivity()).get(ViewModel.class);
    

    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.

    Login or Signup to reply.
  5. 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:

    1. 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.

    2. 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.

    3. 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; }

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search