Search code examples
androidarraysseekbar

Seekbar creating EditTexts and then getting entries for further use


This code creates a seekbar and makes the seekbar create as many EditText fields as the slider is at / remove ones that would be too much. This code is in OnActivityCreated

final LinearLayout linearLayout = (LinearLayout) getActivity()
  .findViewById(R.id.npv_calcfields);
EditText editText = new EditText(getActivity());
editText.setId(i);
editText.setLayoutParams(new LayoutParams(
  LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT));
  SeekBar bar = (SeekBar) getActivity().findViewById(R.id.npv_seekbar);
  final TextView selection = (TextView) getActivity()
    .findViewById(R.id.npv_selected);
  bar.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
    public void onProgressChanged(SeekBar seekbar, int progress,
      boolean fromUser) {
      selection.setText("You have selected " + progress + " periods.");
      if (progress == 0) {
        String normalstring = getActivity().getResources()
          .getString(R.string.npv1);
        selection.setText(normalstring);
      }
      if (i > progress) {
        while (i > progress) {
          i--;
          EditText editText = (EditText) getActivity()
            .findViewById(i);
          linearLayout.removeView(editText);
        }
      } else {
        while (i < progress) {
          EditText editText = new EditText(getActivity());
          editText.setId(i);
          editText.setLayoutParams(new LayoutParams(
            LayoutParams.FILL_PARENT,
            LayoutParams.WRAP_CONTENT));
          linearLayout.addView(editText);
          editText.setHint("Cash Flow " + i);
          i++;
        }
      }
    }
    public void onStopTrackingTouch(SeekBar arg0) {
    }
    public void onStartTrackingTouch(SeekBar arg0) {
    }
  });

This code is in the general class area:

int i = 0;
EditText r = (EditText) getActivity().findViewById(R.id.npv_rate);
Button calc = (Button) getActivity().findViewById(R.id.npv_calc);
EditText[] DynamicField = new EditText[16];

Now I want users to input numbers into those edittext fields and then I want to do some math on them: Entry / (Math.pow(1+r, i) with i beeing the id of the field. The first entry should therefore be calculated as this: entry/(1+r)^0. This is what I tried but it doesn't work. It just crashes on startup.

calc.setOnClickListener(new OnClickListener() {
  public void onClick(View arg0) {
    Double r1 = Double.parseDouble(r.getText().toString());
    EditText editText = (EditText) getActivity().findViewById(i);
    TextView answer = (TextView) getActivity().findViewById(R.id.npv_answer);
    double[] CashFlows;
    CashFlows = new double[i];
    double result = 0;
    CashFlows[i] = (Double.parseDouble(editText.getText()
      .toString())) / (Math.pow(1 + r1, i));
    for (double d : CashFlows) {
      result += d;
    }
    answer.setText("answer is " + result);
  }
});

What did I do wrong? by the way only the last code segment isnt working. if i comment that out it all works fine i tested it :) just dosent do anything obviuosly :)

ok a little background on the errorlog that you can see here: http://pastebin.com/G8iX6Pkm EDIT: the entire class file can be seen here: http://pastebin.com/dxA91dst, the entire project can be found here: https://github.com/killerpixler/Android-Financial-Calculator.git

the class file is a fragment that gets loaded in a DetailsActivity when somebody clicks on a listitem from the Main activity. Like i said the error has to be in the button listener because it was working before i added it.


Solution

  • That NullPointerException comes from the fact that you initialize your Views using the getActivity() method where you declare them as fields in the F_NPV class. The method getActivity() method will return a valid Activity reference after the callback onAttach() is called, so the way you initialize the views will not work as, at that moment(when the fields of the Fragment class are initialized) the method getActivity will return null, no valid reference. The correct way to do that initialization is doing it in the onActivityCreated callback:

    EditText r;
    Button calc;
    //...
    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        r = (EditText) getActivity().findViewById(R.id.npv_rate);
        calc = (Button) getActivity().findViewById(R.id.npv_calc);
    //...
    

    Also, if I may, some suggestions regarding your code:

    You're doing some double's parsing from Strings and it may be a good idea to check the input so you don't throw a NumberFormatException. For example, if the user creates some EditTexts and then clicks the calculate Button(I know, it sounds silly, but there are chances the user will do it(I did it for example)), you'll throw a NumberFormatException as you try to parse an empty String. Instead make a little check:

    public void onClick(View arg0) {
        Double r1 = Double.parseDouble((r.getText().toString())
                        .equals("") ? "0" : r.getText().toString());
        EditText editText = (EditText) getActivity().findViewById(i);
        TextView answer = (TextView) getActivity().findViewById(R.id.npv_answer);
        double[] CashFlows;
        CashFlows = new double[i];
        double result = 0;
        String tmp = editText.getText().toString();
        CashFlows[i] = (Double.parseDouble(tmp.equals("") ? "0" : tmp))
                            / (Math.pow(1 + r1, i));
        //...
    

    Also, even if you have correct values in the EditText the above code will throw a NullPointerException, as the editText variable will be null. The reason for this is in the while loops that you used to create the fields. For example, if the user moves the SeekBar to 3 than the while loop will run 3 times, each time incrementing the i value. So i will be 0, 1, 2, so far correct but because you increment i each time the final i will be 4. Now in the onClick method you'll look for an EditText with the id i, but as there is no EditText in the layout with the id 4, the view will be null.

    Also, try to give your classes better names, you may know very well what they mean but you could be making things worse for someone that reads your code(like F_PNV, F_PV etc).

    Code for the onActivityCreated method. This should solve what you're trying to do(if I understand what you want):

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        r = (EditText) getActivity().findViewById(R.id.npv_rate);
        calc = (Button) getActivity().findViewById(R.id.npv_calc);
        final LinearLayout linearLayout = (LinearLayout) getActivity()
                .findViewById(R.id.npv_calcfields);
        SeekBar bar = (SeekBar) getActivity().findViewById(R.id.npv_seekbar);
        final TextView selection = (TextView) getActivity().findViewById(
                R.id.npv_selected);
        bar.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
    
            public void onProgressChanged(SeekBar seekbar, int progress,
                    boolean fromUser) {
                selection
                        .setText("You have selected " + progress + " periods.");
                if (progress == 0) {
                    String normalstring = getActivity().getResources()
                            .getString(R.string.npv1);
                    selection.setText(normalstring);
                    linearLayout.removeAllViews(); // the progress is 0 so
                                                    // remove all the views that
                                                    // are currently present
                } else {                
                    int currentChilds = linearLayout.getChildCount();
                    if (currentChilds < progress) {
                        while (currentChilds != progress) {
                            EditText editText = new EditText(getActivity());                        
                            editText.setLayoutParams(new LayoutParams(
                                    LayoutParams.FILL_PARENT,
                                    LayoutParams.WRAP_CONTENT));
                            linearLayout.addView(editText);
                            currentChilds++;                            
                        }
                    } else if (currentChilds > progress) {
                        while (currentChilds != progress) {
                            linearLayout.removeViewAt(linearLayout
                                    .getChildCount() - 1);
                            currentChilds--;
                        }
                    }
                }
            }
    
            public void onStopTrackingTouch(SeekBar arg0) {
            }
    
            public void onStartTrackingTouch(SeekBar arg0) {
            }
        });
    
        calc.setOnClickListener(new OnClickListener() {
    
            public void onClick(View view) {
                Double r1 = Double.parseDouble((r.getText().toString())
                        .equals("") ? "0" : r.getText().toString());
                TextView answer = (TextView) getActivity().findViewById(
                        R.id.npv_answer);
                final LinearLayout linearLayout = (LinearLayout) getActivity()
                        .findViewById(R.id.npv_calcfields);
                int size = linearLayout.getChildCount();
                double[] CashFlows = new double[size];
                double result = 0;
                for (int i = 0; i < size; i++) {
                    EditText editText = (EditText) linearLayout.getChildAt(i);
                    String tmp = editText.getText().toString();
                    CashFlows[i] = (Double.parseDouble(tmp.equals("") ? "0"
                            : tmp)) / (Math.pow(1 + r1, i));
                }
                for (double d : CashFlows) {
                    result += d;
                }
                answer.setText("answer is " + result);
            }
        });
    
    }