Search code examples
javaandroidandroid-fragmentstextwatcherandroid-textwatcher

TextWatcher implementation with RadioButtons


Please see attached image and code snippet to aid in explanation. From the attached image I would like the user to enter a cost, quantity and select either Include Tax or Exclude tax and a new cost is automatically generated where indicated without pressing a Button, but to no avail I am unable to do this. Someone please help. Thanks

See Image Here enter image description here

After implementing the Changes that were suggested and trying to enter an input in the cost field I was met with the error seen below. Please provide additional feedback. Thanks

Error image enter image description here

import android.content.Context;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageButton;
import android.widget.EditText;
import android.widget.RadioButton;
import android.widget.RadioGroup;

import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.util.Locale;


public class Calculator extends Fragment {

private static EditText itemText, editCost, editQuantity, calCost, rTax;
private static RadioGroup rGroup;
private static RadioButton rb;
View gView;

private double bTotal = 0, aTotal = 0, trueCost = 0, taxValue = 16.5, cost = 0, newCost = 0;
private int quantity = 1;

DecimalFormatSymbols symbols = DecimalFormatSymbols.getInstance(Locale.US);
DecimalFormat decimalFormat = new DecimalFormat("###,###.##", symbols);

CalculatorListener activityCommander;


public interface CalculatorListener {
    void addtoCart(String itemName, int qty, double beforeTax, double afterTax, double bTotal, double aTotal);
}

@Override
public void onAttach(Context context) {
    super.onAttach(context);
    try {
        activityCommander = (CalculatorListener) context;
    } catch (ClassCastException e) {
        throw new ClassCastException(context.toString());
    }
}


public Calculator() {
    // Required empty public constructor
}


@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    // Inflate the layout for this fragment
    gView = inflater.inflate(R.layout.fragment_calculator, container, false);


    editCost = (EditText) gView.findViewById(R.id.editcost);
    itemText = (EditText) gView.findViewById(R.id.itemText);
    editQuantity = (EditText) gView.findViewById(R.id.editquantity);
    calCost = (EditText) gView.findViewById(R.id.calcost);
    rTax = (EditText) gView.findViewById(R.id.rtax);
    rGroup = (RadioGroup) gView.findViewById(R.id.rgroup);

    final ImageButton FieldButton = (ImageButton) gView.findViewById(R.id.FieldButton);
    final ImageButton TaxButton = (ImageButton) gView.findViewById(R.id.TaxButton);
    final ImageButton CalButton = (ImageButton) gView.findViewById(R.id.CalButton);

    rTax.setEnabled(false);
    calCost.setEnabled(false);

    rGroup.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
        @Override
        public void onCheckedChanged(RadioGroup group, int checkedId) {

            rb = (RadioButton)gView.findViewById(checkedId);
        }
    });

    editCost.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) {

            try{
                update();
            }catch (NumberFormatException e)
            {
                e.printStackTrace();
            }
        }
    });

    editQuantity.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) {

            try{
                update();
            }catch (NumberFormatException e)
            {
                e.printStackTrace();
            }
        }
    });


    FieldButton.setOnClickListener(
            new View.OnClickListener() {
                public void onClick(View v) {
                    clearfield();
                }

            }
    );
    TaxButton.setOnClickListener(
            new View.OnClickListener() {
                public void onClick(View v) {
                    adjtax();
                }

            }
    );
    CalButton.setOnClickListener(
            new View.OnClickListener() {
                public void onClick(View v) {
                    //toCart();
                }

            }
    );


    return gView;

}

public void clearfield() {
    editCost.setText("");
    editCost.setBackgroundResource(R.drawable.edittxt);
    editQuantity.setText("");
    editQuantity.setBackgroundResource(R.drawable.edittxt);
    calCost.setText("");
    calCost.setBackgroundResource(R.drawable.edittxt);
    itemText.setText("");
    itemText.setBackgroundResource(R.drawable.edittxt);
    rGroup.clearCheck();
}

public void adjtax() {
    editCost.setBackgroundResource(R.drawable.edittxt);
    editQuantity.setBackgroundResource(R.drawable.edittxt);
    calCost.setBackgroundResource(R.drawable.edittxt);
    itemText.setBackgroundResource(R.drawable.edittxt);
    rTax.setEnabled(true);
    rTax.setText("");
    rTax.setBackgroundResource(R.drawable.edittxtfocus);
    rTax.setOnFocusChangeListener(new View.OnFocusChangeListener() {
        @Override
        public void onFocusChange(View v, boolean hasFocus) {
            if (hasFocus) {
                rTax.setEnabled(true);

            } else {
                rTax.setEnabled(false);
                v.setBackgroundResource(R.drawable.edittxt);
            }
        }
    });

}


public void update(){


            if (rTax.getText().toString().isEmpty()) {
               taxValue = 16.5;
            } else if (!rTax.getText().toString().isEmpty()) {
                taxValue = Double.parseDouble(rTax.getText().toString());
            }


            //CHECKS THE TAX VALUE IF IT NEEDS TO BE CONVERTED
            if (taxValue > 1) {
                taxValue = taxValue / 100;
            } else {
                taxValue = taxValue * 1;
            }

            //CUSTOM VALIDATOR FOR QUANTITY FIELD
            if (editQuantity.getText().toString().isEmpty()) {
                quantity = 1;
            } else if (!editQuantity.getText().toString().isEmpty()) {
                quantity = Integer.parseInt(editQuantity.getText().toString());
            }


            if(rb.getText() == "Include Tax"){
                newCost = (((cost = Double.parseDouble(editCost.getText().toString())) * taxValue) + cost) * quantity;
                calCost.setText(decimalFormat.format(newCost).toString());
            }
            else if(rb.getText() == "Exclude Tax"){
                newCost = ((cost = Double.parseDouble(editCost.getText().toString())) * quantity);
                calCost.setText(decimalFormat.format(newCost).toString());
            }

            trueCost = cost * quantity;
            bTotal = trueCost;
            aTotal = newCost;
}


}

Solution

  • Move the rgroup.setOnCheckedChangeListener out of the update() method and into the onCreateView(). You should not have to set the listener every time the text has been updated.

    The update method called after text entry can probably just update the tax value if a valid value has been entered and either the check boxes have been selected.

    Update with another suggestion

    I would lookup the radio button by comparing text as you are doing, some time in the future you may want to change the text in the resource file or apply another locale and this code will stop working.

    if(rb.getText() == "Include Tax")
    

    I would suggest comparing against the id itself:

    if (checkedId == R.id.radio1 )
    

    Another Suggestion:

    Consider changing your variable names to lead with a lower case character. Leading with an upper case letter makes it look like either class name or a constant and make the code a bit more difficult to read.

    private EditText itemText;
    private EditText editCost;
    private EditText editQuantity;
    private EditText calcost;
    private EdtiText rTax;
    

    You can also remove all(some) the focus change listeners you have and set those attributes in the android drawable resources. Take a look here.

    Update 9/9/2016 22:22

    A bit better, but as you've found out calls to update can throw a null pointer if rb had never been initialized. You should check for a null rb in the update method and give the user a notice to select an option. You could also assign rb to either of the two values from the start in the onCreateView, so it is never null.

    You should probably also add a call to update() after setting rb in the radio group callback. This will allow the screen to update as soon as they choose an option.