Slow button click response on Android

371 views
Skip to first unread message

david.bradl...@gmail.com

unread,
May 14, 2015, 11:05:55 PM5/14/15
to codenameone...@googlegroups.com
I have built a small calculator in a popup dialog.  My numbered buttons simply append a number to the text field.  The response on the Nexus 7, the only device I have right now other than the simulator, is often slow.  Sometimes it take 4-5 seconds for me to hear the echo click and for the number to show up in the next field.  Sometime the number doesn't show up until I tap another number, and then they both go into the text field at once.  Any ideas?  Code is attached...

package com.dbw.firstplayer;

import com.codename1.charts.util.ColorUtil;
import com.codename1.ui.*;
import com.codename1.ui.events.ActionEvent;
import com.codename1.ui.events.ActionListener;
import com.codename1.ui.table.TableLayout;

import java.util.ArrayList;

/**
 * Created by dwilliams on 4/10/2015.
 */

public class NumberDialog extends Dialog {
 
private final int TEXT_COLOR = ColorUtil.WHITE;
 
private TableLayout _layout;
 
private NumberTextField _display;
 
private Button _btn1;
 
private Button _btn2;
 
private Button _btn3;
 
private Button _btn4;
 
private Button _btn5;
 
private Button _btn6;
 
private Button _btn7;
 
private Button _btn8;
 
private Button _btn9;
 
private Button _btn0;
 
private Button _btnDivide;
 
private Button _btnMultiply;
 
private Button _btnPlus;
 
private Button _btnMinus;
 
private Button _btnEquals;
 
private Button _btnClear;
 
private Button _btnBackSpace;
 
private Button _btnOK;
 
private Button _btnCancel;
 
private Label _lblPendingOp;

 
enum OP {
 DIVIDE
("/"),
 MULTIPLY
("x"),
 PLUS
("+"),
 MINUS
("-"),
 EQUALS
(""),
 NUMBER
("");

 
String _text; // String to display in _lblPendingOp

 OP
(String txt) {
 _text
= txt;
 
}

 
public String toString() {
 
return _text;
 
}
 
};

 
private OP _lastOp = OP.NUMBER; // Type of button last pressed.
 
private OP _pendingOp = OP.EQUALS; // Type of pending operation. Cannot be OP.NUMBER
 
private long _lastValue = 0;
 
private boolean _isClearOnNextDigit = false;

 
private ArrayList<INumberListener> _numberListeners = new ArrayList<INumberListener>();

 
public NumberDialog() {
 setTitle
("Enter a Score");
 
TableLayout.Constraint c;
 _layout
= new TableLayout(6, 5);
 setLayout
(_layout);

 
int row;
 
final int COL_SPAN = 5; // span all columns

 
// row zero: sign, display, pending operation
 row
= 0;
 _display
= new NumberTextField();
 c
= _layout.createConstraint(row, 0);
 c
.setHorizontalSpan(COL_SPAN - 1);
 addComponent
(c, _display.getComponent());
 _lblPendingOp
= new Label();
 _lblPendingOp
.getStyle().setFgColor(TEXT_COLOR);
 c
= _layout.createConstraint(row, 4);
 addComponent
(c, _lblPendingOp);

 
// row one: 7, 8, 9, /, and C
 row
= 1;
 _btn7
= createNumberButton(7);
 c
= _layout.createConstraint(row, 0);
 addComponent
(c, _btn7);
 _btn8
= createNumberButton(8);
 c
= _layout.createConstraint(row, 1);
 addComponent
(c, _btn8);
 _btn9
= createNumberButton(9);
 c
= _layout.createConstraint(row, 2);
 addComponent
(c, _btn9);
 _btnDivide
= createOpButton(OP.DIVIDE);
 c
= _layout.createConstraint(row, 3);
 addComponent
(c, _btnDivide);
 _btnClear
= createClearButton();
 c
= _layout.createConstraint(row, 4);
 addComponent
(c, _btnClear);

 
// row two: 4, 5, 6, and backspace
 row
= 2;
 _btn4
= createNumberButton(4);
 c
= _layout.createConstraint(row, 0);
 addComponent
(c, _btn4);
 _btn5
= createNumberButton(5);
 c
= _layout.createConstraint(row, 1);
 addComponent
(c, _btn5);
 _btn6
= createNumberButton(6);
 c
= _layout.createConstraint(row, 2);
 addComponent
(c, _btn6);
 _btnMultiply
= createOpButton(OP.MULTIPLY);
 c
= _layout.createConstraint(row, 3);
 addComponent
(c, _btnMultiply);
 _btnBackSpace
= createBackspaceButton();
 c
= _layout.createConstraint(row, 4);
 addComponent
(c, _btnBackSpace);

 
// row three: 1, 2, 3, -, and equals
 row
= 3;
 _btn1
= createNumberButton(1);
 c
= _layout.createConstraint(row, 0);
 addComponent
(c, _btn1);
 _btn2
= createNumberButton(2);
 c
= _layout.createConstraint(row, 1);
 addComponent
(c, _btn2);
 _btn3
= createNumberButton(3);
 c
= _layout.createConstraint(row, 2);
 addComponent
(c, _btn3);
 _btnMinus
= createOpButton(OP.MINUS);
 c
= _layout.createConstraint(row, 3);
 addComponent
(c, _btnMinus);
 _btnEquals
= createOpButton(OP.EQUALS);
 c
= _layout.createConstraint(row, 4);
 c
.setVerticalSpan(2);
 addComponent
(c, _btnEquals);

 
// row four: 0, +, =
 row
= 4;
 _btn0
= createNumberButton(0);
 c
= _layout.createConstraint(row, 0);
 c
.setHorizontalSpan(3);
 addComponent
(c, _btn0);
 _btnPlus
= createOpButton(OP.PLUS);
 c
= _layout.createConstraint(row, 3);
 addComponent
(c, _btnPlus);

 
// row 5: ok, cancel
 row
= 5;
 _btnOK
= createOKButton();
 c
= _layout.createConstraint(row, 0);
 c
.setHorizontalSpan(2);
 addComponent
(c, _btnOK);
 _btnCancel
= createCancelButton();
 c
= _layout.createConstraint(row, 2);
 c
.setHorizontalSpan(3);
 addComponent
(c, _btnCancel);
 
}

 
public void addNumberListener(INumberListener l) {
 
if (!_numberListeners.contains(l)) {
 _numberListeners
.add(l);
 
}
 
}

 
private Button createNumberButton(long n) {
 
Button btn = new Button(Long.toString(n));
 btn
.getStyle().setFgColor(TEXT_COLOR);
 btn
.addActionListener(new AppendNumberActionListener(n));
 
return btn;
 
}

 
private Button createClearButton() {
 
Button btn = new Button("C");
 btn
.getStyle().setFgColor(TEXT_COLOR);
 btn
.addActionListener(new ClearActionListener());
 
return btn;
 
}

 
private Button createBackspaceButton() {
 
Button btn = new Button("<");
 btn
.getStyle().setFgColor(TEXT_COLOR);
 btn
.addActionListener(new BackspaceActionListener());
 
return btn;
 
}

 
private Button createOpButton(OP op) {
 
Button btn = new Button(op == OP.EQUALS ? "=" : op.toString());
 btn
.getStyle().setFgColor(TEXT_COLOR);
 btn
.addActionListener(new OperationActionListener(op));
 
return btn;
 
}


 
private Button createOKButton() {
 
Button btn = new Button("OK");
 btn
.getStyle().setFgColor(TEXT_COLOR);
 btn
.addActionListener(new ActionListener() {
 
@Override
 
public void actionPerformed(ActionEvent evt) {
 evaluate
();
 fireNumberListeners
();
 dispose
();
 
}
 
});
 
return btn;
 
}

 
private Button createCancelButton() {
 
Button btn = new Button("Cancel");
 btn
.getStyle().setFgColor(TEXT_COLOR);
 btn
.addActionListener(new ActionListener() {
 
@Override
 
public void actionPerformed(ActionEvent evt) {
 dispose
();
 
}
 
});
 
return btn;
 
}

 
private void evaluate() {
 
long value = _display.getValue();

 
switch (_pendingOp) {
 
case DIVIDE:
 _lastValue
/= value;
 
break;
 
case MULTIPLY:
 _lastValue
*= value;
 
break;
 
case PLUS:
 _lastValue
+= value;
 
break;
 
case MINUS:
 _lastValue
-= value;
 
break;
 
default:
 _lastValue
= value;
 
}
 _display
.forceValue(_lastValue);
 
}

 
private void fireNumberListeners() {
 
for (INumberListener l: _numberListeners) {
 l
.onChange(_lastValue);
 
}
 
}

 
private class AppendNumberActionListener implements ActionListener {
 
private long _value;

 
public AppendNumberActionListener(long value) {
 _value
= value;
 
}

 
@Override
 
public void actionPerformed(ActionEvent evt) {
 
if (_lastOp == OP.NUMBER) {
 _display
.appendDigit(_value);
 
} else if (_lastOp.equals(OP.EQUALS)) {
 _display
.setValue(_value);
 
} else {
 _display
.setValue(_value);
 
}
 _lastOp
= OP.NUMBER;
 
}
 
}

 
private class ClearActionListener implements ActionListener {

 
public ClearActionListener() {
 
}

 
@Override
 
public void actionPerformed(ActionEvent evt) {
 _display
.setValue(0);
 _lastOp
= OP.EQUALS;
 _pendingOp
= OP.EQUALS;
 _lblPendingOp
.setText(OP.EQUALS.toString());
 
}
 
}

 
private class BackspaceActionListener implements ActionListener {

 
public BackspaceActionListener() {
 
}

 
@Override
 
public void actionPerformed(ActionEvent evt) {
 
if (_lastOp == OP.EQUALS || _lastOp == OP.NUMBER) {
 _display
.backSpace();
 
} else {
 _lastOp
= OP.EQUALS;
 _lblPendingOp
.setText(OP.EQUALS.toString());
 
}
 
}
 
}

 
private class OperationActionListener implements ActionListener {
 
private OP _op;

 
public OperationActionListener(OP op) {
 _op
= op;
 
}

 
@Override
 
public void actionPerformed(ActionEvent evt) {
 evaluate
();
 _lastOp
= _op;
 _pendingOp
= _op;
 _lblPendingOp
.setText(_op.toString());
 
}
 
}

 
private class NumberTextField {
 
private TextField _textfield;
 
private long _value = 0;

 
public NumberTextField() {
 _textfield
= new TextField("0", 10);
 _textfield
.setEditable(false);
 _textfield
.setAlignment(Component.RIGHT);
 
}

 
public Component getComponent() {
 
return _textfield;
 
}

 
public void setValue(long value) {
 
if (value < -99999 || value > 99999) {
 
return;
 
}
 _value
= value;
 _textfield
.setText(Long.toString(value));
 _textfield
.repaint();
 
}

 
public void forceValue(long value) {
 _value
= value;
 _textfield
.setText(Long.toString(value));
 _textfield
.repaint();
 
}

 
public long getValue() {
 
return _value;
 
}

 
public void appendDigit(long d) {
 setValue
(_value * 10 + d);
 
}

 
public void backSpace() {
 setValue
(_value/10);
 
}
 
}
}




IDE: IDEA
Desktop OS: Windows 8
Simulator; N/A
Device: Nexus 7

Shai Almog

unread,
May 15, 2015, 12:26:12 AM5/15/15
to codenameone...@googlegroups.com, david.bradl...@gmail.com, david.bradl...@gmail.com
I'm assuming you are blocking the EDT at some point although its hard to read all the code.
The EDT violation detection tool in the simulator also detects delays in the EDT.

david.bradl...@gmail.com

unread,
May 15, 2015, 1:02:36 AM5/15/15
to codenameone...@googlegroups.com, david.bradl...@gmail.com
The EDT violation detection tool gave me some interesting stack traces.  Looks like a problem is first occurring when my dialog pops up.  I haven't made sense of it, yet, but I suspect there's a clue in here somewhere.

Connected to the target VM, address: '127.0.0.1:54936', transport: 'socket'
Rendering frame took too long 176 milliseconds
java
.lang.ArrayIndexOutOfBoundsException: 0
    at com
.codename1.ui.Component.pointerPressed(Component.java:2693)
    at com
.codename1.ui.Display.handleEvent(Display.java:1945)
    at com
.codename1.ui.Display.edtLoopImpl(Display.java:1058)
    at com
.codename1.ui.Display.invokeAndBlock(Display.java:1193)
    at com
.codename1.ui.Display.invokeAndBlock(Display.java:1228)
    at com
.codename1.ui.Form.showModal(Form.java:1644)
    at com
.codename1.ui.Dialog.showModal(Dialog.java:1089)
    at com
.codename1.ui.Dialog.show(Dialog.java:470)
    at com
.codename1.ui.Dialog.showPackedImpl(Dialog.java:1386)
    at com
.codename1.ui.Dialog.showPacked(Dialog.java:1294)
    at com
.codename1.ui.Dialog.showImpl(Dialog.java:1049)
    at com
.codename1.ui.Dialog.show(Dialog.java:1031)
    at com
.dbw.firstplayer.ColumnForm$NumberLabel$PopupNumberDialogListener.actionPerformed(ColumnForm.java:301)
    at com
.codename1.ui.util.EventDispatcher.fireActionEvent(EventDispatcher.java:338)
    at com
.codename1.ui.Component.pointerPressed(Component.java:2719)
    at com
.codename1.ui.Form.pointerPressed(Form.java:2087)
    at com
.codename1.ui.Component.pointerPressed(Component.java:2693)
    at com
.codename1.ui.Display.handleEvent(Display.java:1931)
    at com
.codename1.ui.Display.edtLoopImpl(Display.java:1058)
    at com
.codename1.ui.Display.mainEDTLoop(Display.java:989)
    at com
.codename1.ui.RunnableWrapper.run(RunnableWrapper.java:120)
    at com
.codename1.impl.CodenameOneThread.run(CodenameOneThread.java:176)
[EDT] 0:0:0,0 - Exception in AppName version 1.0
[EDT] 0:0:0,0 - OS ios
[EDT] 0:0:0,0 - Error java.lang.ArrayIndexOutOfBoundsException: 0
[EDT] 0:0:0,0 - Current Form null
[EDT] 0:0:0,0 - Exception: java.lang.ArrayIndexOutOfBoundsException - 0
java
.lang.ArrayIndexOutOfBoundsException: 0
    at com
.codename1.ui.Component.pointerPressed(Component.java:2693)
    at com
.codename1.ui.Display.handleEvent(Display.java:1945)
    at com
.codename1.ui.Display.edtLoopImpl(Display.java:1058)
    at com
.codename1.ui.Display.invokeAndBlock(Display.java:1193)
    at com
.codename1.ui.Display.invokeAndBlock(Display.java:1228)
    at com
.codename1.ui.Form.showModal(Form.java:1644)
    at com
.codename1.ui.Dialog.showModal(Dialog.java:1089)
    at com
.codename1.ui.Dialog.show(Dialog.java:470)
    at com
.codename1.ui.Dialog.showPackedImpl(Dialog.java:1386)
    at com
.codename1.ui.Dialog.showPacked(Dialog.java:1294)
    at com
.codename1.ui.Dialog.showImpl(Dialog.java:1049)
    at com
.codename1.ui.Dialog.show(Dialog.java:1031)
    at com
.dbw.firstplayer.ColumnForm$NumberLabel$PopupNumberDialogListener.actionPerformed(ColumnForm.java:301)
    at com
.codename1.ui.util.EventDispatcher.fireActionEvent(EventDispatcher.java:338)
    at com
.codename1.ui.Component.pointerPressed(Component.java:2719)
    at com
.codename1.ui.Form.pointerPressed(Form.java:2087)
    at com
.codename1.ui.Component.pointerPressed(Component.java:2693)
    at com
.codename1.ui.Display.handleEvent(Display.java:1931)
    at com
.codename1.ui.Display.edtLoopImpl(Display.java:1058)
    at com
.codename1.ui.Display.mainEDTLoop(Display.java:989)
    at com
.codename1.ui.RunnableWrapper.run(RunnableWrapper.java:120)
    at com
.codename1.impl.CodenameOneThread.run(CodenameOneThread.java:176)

Shai Almog

unread,
May 15, 2015, 12:09:49 PM5/15/15
to codenameone...@googlegroups.com, david.bradl...@gmail.com, david.bradl...@gmail.com
These are a bit baffling... They have nothing to do with EDT violation.
It means a pointer event was received with bad data which should never happen. Did you possibly use Display API's like pointer pressed or other such API's?

david.bradl...@gmail.com

unread,
May 15, 2015, 4:19:02 PM5/15/15
to codenameone...@googlegroups.com, david.bradl...@gmail.com
Yes, the dialog was launched from a Label where I used addPointerPressedListener().  What is the proper way to detect taps on a label?  I guess I could turn it into a button and change the style to make it looks flat.  In Swing, I would have added a mouse listener, but I don't think that option is available in Codename One.

--Dave

david.bradl...@gmail.com

unread,
May 15, 2015, 4:59:29 PM5/15/15
to codenameone...@googlegroups.com, david.bradl...@gmail.com
Well, I changed my labels to buttons and used actionPerformed(), and I'm still having the same problems.  Guess that wasn't it.

--Dave

Shai Almog

unread,
May 16, 2015, 1:42:47 AM5/16/15
to codenameone...@googlegroups.com, david.bradl...@gmail.com, david.bradl...@gmail.com
Try using pointer released listener although the best way to do this is with action event on the buttons (action listener).

Bryan Buchanan

unread,
May 16, 2015, 5:46:19 PM5/16/15
to codenameone...@googlegroups.com, david.bradl...@gmail.com
I've done something a bit similar, and have not run into the problem you're having.

I layed out the calculator in the UI builder and attached action events to each of the buttons.


Each one of the buttons has these action events:

   @Override
    protected void onSellPriceForm_Q7Action(Component c, ActionEvent event) {

        super.onSellPriceForm_Q7Action(c, event);

        xsellprice += "7";
        Label b = (Label) findByName("SellPriceLabel", c);
        b.setText(xsellprice);
    }

    @Override
    protected void onSellPriceForm_Q8Action(Component c, ActionEvent event) {

        super.onSellPriceForm_Q8Action(c, event);

        xsellprice += "8";
        Label b = (Label) findByName("SellPriceLabel", c);
        b.setText(xsellprice);
    }

    @Override
    protected void onSellPriceForm_Q9Action(Component c, ActionEvent event) {

        super.onSellPriceForm_Q9Action(c, event);

        xsellprice += "9";
        Label b = (Label) findByName("SellPriceLabel", c);
        b.setText(xsellprice);
    }

on my Nexus7 this works fine. (xsellprice is the string displayed in the label at the top, which will eventually get parsed in this code:
@Override
    protected void onSellPriceForm_SellPriceButtonOKAction(Component c, ActionEvent event) {

        super.onSellPriceForm_SellPriceButtonOKAction(c, event);

        if (xsellprice.equals("")) {
            xsellprice = "0.00";
        }
        lineitem.setSellPrice(Money.parse(xsellprice));
        xsellprice = "";

        back();
    }


Maybe of some help.


david.bradl...@gmail.com

unread,
May 18, 2015, 1:30:21 PM5/18/15
to codenameone...@googlegroups.com
Hi, Bryan,

Thanks for your suggestion.  I decided to rebuild my calculator with the GUI builder, instead of by hand.  I ran into the same problem I had before.  So, I guess whatever issue I have is not related to the calculator, but perhaps to the form from which I pop it up.  For testing, I added a button to a different form to popup my calculator, and the problem was gone.  I guess I will focus on the form from which I launch now.

--Dave

david.bradl...@gmail.com

unread,
Jun 26, 2015, 2:10:58 PM6/26/15
to codenameone...@googlegroups.com, david.bradl...@gmail.com
I'm getting back to this issue after being away for a couple of months. I've narrowed the problem down to the following: If the layout manager in the form from which I launch the popup is a BoxLayout with the direction set to the Y_AXIS, everything works fine.  If the layout is a FlowLayout, BoxLayout with X_AXIS, or a BorderLayout, I get the original problem described above.

I'll try to come up with a minimal piece of code to demonstrate the error and submit a bug.

--David

Shai Almog

unread,
Jun 27, 2015, 1:25:08 AM6/27/15
to codenameone...@googlegroups.com, david.bradl...@gmail.com, david.bradl...@gmail.com
Try to avoid FlowLayout as the top most layout manager. It has issues that stem from the fact that we don't reflow.
Reply all
Reply to author
Forward
0 new messages