Posts Tagged ‘Redpoint’

Creating soft-keys that work with text watchers in Android

1 Comment »

Recently, I created a screen with soft keys that could be used for number input.  Scroll down for some source. Everything was working perfectly.  Each number key called field.append(“1”), passing in the correct number.  Then, a text watcher would be notified of the change and reformat the field.  The problem arose when I wrote the backspace method.  TextView doesn’t have a convenience method for removing characters from a field, so I used setText, passing in the number with the last character removed.  Unfortunately, when I did that, the text wacher wasn’t notified of the change!  Ugh…  Android is very well designed, but some implementation details are really not written well.  I decided it was time to look in the Android source.  If the append method caused a notification to the text watcher, I would write a method that worked the same way.

Supporting number keys

This was really simple.

private class CharacterButtonListener implements
      View.OnClickListener {
    private TextView field;
    private String character;
 
    public CharacterButtonListener(TextView field,
        String character) {
      this.field = field;
      this.character = character;
    }
 
    @Override
    public void onClick(View v) {
      this.field.append(this.character);
    }
  }

Supporting the backspace key

Since the setText method wasn’t notifying the TextWatcher, I looked into Android’s source to find this solution.  It’s ugly, but it works.

this.buttonBack.setOnClickListener(new View.OnClickListener() {
      @Override
      public void onClick(View v) {
        TextView number = numberText;
        CharSequence text = number.getText();
        // This is annoyingly ugly, but is from the Android source
        if (!(text instanceof Editable)) {
          number.setText(text, BufferType.EDITABLE);
        }
        Editable editable = (Editable)number.getText();
        // Now that we have the editable, edit it.
        // This line is not from the Android source.
        if (text.length() > 0) {
          editable.delete(text.length() - 1, text.length());
        }
      }
    });

That’s it!   I tried another solution, having each soft-key fire a key event in the field.  It worked, but since the field is automatically formatted, it didn’t feel right.  Also, Android doesn’t let the application set the position of the cursor (caret) in the text view, so if the user re-opened the number input dialog to edit the number, the cursor appeared at the left side of the field, which meant that the user had to re-position the cursor before pressing any keys, which was a pain.


Loading string resources in Android

18 Comments »

To support i18n, internationalization, Android provides a resource file, usually /res/values/strings.xml.  That XML and others are compiled and each resource is assigned a unique integer ID.  The IDs are placed into a resource file, “R”, as public static final variables.  Here’s how to get the value of a string resource in your application.

In a layout XML

android:text="@string/resource_name"

In an Activity

this.getString(R.string.resource_name)

In an area of the program in which you have access to a Context or Application, such as a ListAdapter.

context.getString(R.string.resource_name)
application.getString(R.string.resource_name)

Large, finger-sized buttons in Android

14 Comments »

July 18, 2009: Updated for Android 1.5

Recently, I created a simple dial pad style number dialog.  I wanted to make the numbers as large as possible to make finger input simple.  Getting the buttons to stretch evenly and not look strange turned out to be a challenge, but I eventually arrived at a solution.  For the full source, scroll down.

Big buttons in Android

Big buttons in Android

  • The dialog uses a relative layout.  It’s usually best at placing objects on the screen no matter what the resolution or orientation.
  • The numbers are contained in a table layout, which is good at spacing things evenly.
  • To get the numbers to stretch to the bottom of the screen, layout_above is set to the OK button, which is aligned to the bottom of the dialog.  The width is fill_parent so that the buttons fill the width of the screen.
  • To get each row to be the same height in HTML, you would set the height to a percent.  In Android, use layout_weight.  Setting the weight of each row to the same value will make them the same size.
  • Also use layout_weight on each button to even out their widths.
  • In version 1.5, Android started to be “smart” about how it lays out items.  Since the content of the “Back” button is longer than any other button, Android will make that column in the table wider.  It will do so even though the layout weights of all of the items are the same.  To override that behavior, set the width of every item to 0.  That way, when Android stretches the buttons out to fit in the table, every button will start from the same width, 0.
<?xml version="1.0" encoding="utf-8"?>
<!--
Layout for a number input dialog
Author: Connor Garvey
Created: Jan 11, 2009
Version 0.0.4
Since 0.0.4
-->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:padding="7dip">
  <EditText android:id="@+id/number"
      android:background="@android:drawable/editbox_background"
      android:cursorVisible="false"
      android:layout_width="fill_parent"
      android:layout_height="wrap_content"  />
  <View android:id="@+id/numberSeparator0"
      android:layout_width="fill_parent"
      android:layout_height="4dp"
      android:layout_below="@id/number" />
  <View android:id="@+id/numberSeparator1"
      android:background="@drawable/black_white_gradient"
      android:layout_width="fill_parent"
      android:layout_height="1dp"
      android:layout_below="@id/numberSeparator0" />
  <View android:id="@+id/numberSeparator2"
      android:layout_width="fill_parent"
      android:layout_height="4dp"
      android:layout_below="@id/numberSeparator1" />
  <Button android:id="@+id/ok"
      android:layout_width="fill_parent"
      android:layout_height="wrap_content"
      android:text="@string/message_ok"
      android:layout_alignParentBottom="true" />
	<TableLayout android:id="@+id/row1"
	    android:layout_width="fill_parent"
	    android:layout_height="wrap_content"
        android:layout_below="@id/numberSeparator2"
        android:layout_above="@id/ok"
        android:layout_weight="1">
    <TableRow
        android:layout_weight="1">
      <Button android:id="@+id/n1"
          android:layout_width="0dip"
          android:layout_height="fill_parent"
          android:text="1"
          android:layout_weight="1" />
      <Button android:id="@+id/n2"
          android:layout_width="0dip"
          android:layout_height="fill_parent"
          android:text="2"
          android:layout_weight="1" />
      <Button android:id="@+id/n3"
          android:layout_width="0dip"
          android:layout_height="fill_parent"
          android:text="3"
          android:layout_weight="1" />
    </TableRow>
    <TableRow
        android:layout_weight="1">
      <Button android:id="@+id/n4"
          android:layout_width="0dip"
          android:layout_height="fill_parent"
          android:text="4"
          android:layout_weight="1" />
      <Button android:id="@+id/n5"
          android:layout_width="0dip"
          android:layout_height="fill_parent"
          android:text="5"
          android:layout_weight="1" />
      <Button android:id="@+id/n6"
          android:layout_width="0dip"
          android:layout_height="fill_parent"
          android:text="6"
          android:layout_weight="1" />
    </TableRow>
    <TableRow
        android:layout_weight="1">
      <Button android:id="@+id/n7"
          android:layout_width="0dip"
          android:layout_height="fill_parent"
          android:text="7"
          android:layout_weight="1" />
      <Button android:id="@+id/n8"
          android:layout_width="0dip"
          android:layout_height="fill_parent"
          android:text="8"
          android:layout_weight="1" />
      <Button android:id="@+id/n9"
          android:layout_width="0dip"
          android:layout_height="fill_parent"
          android:text="9"
          android:layout_weight="1" />
    </TableRow>
    <TableRow
        android:layout_weight="1">
      <TextView
          android:layout_width="0dip"
          android:layout_height="fill_parent"
          android:layout_weight="1" />
      <Button android:id="@+id/n0"
          android:layout_width="0dip"
          android:layout_height="fill_parent"
          android:text="0"
          android:layout_weight="1" />
      <Button android:id="@+id/back"
          android:layout_width="0dip"
          android:layout_height="fill_parent"
          android:text="Back"
          android:layout_weight="1" />
    </TableRow>
  </TableLayout>
</RelativeLayout>

It is difficult to get references to buttons in dialogs. You first have to inflate the dialog’s view, then you can find a button by its ID.

LayoutInflater inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View view = inflater.inflate(R.layout.number_input_dialog, null);
this.button0 = (Button)view.findViewById(R.id.n0);
this.button0.setOnClickListener(new NumberButtonListener(this.number, "0"));