CodeSnips

Sunday, January 23, 2011

Getting Layout Dimensions in Android

It's always interesting to learn a new operating system or API, but it can often be a little frustrating. You never know when you're going to run into a couple of hours of head scratching. Often, it seems to be the simplest tasks - such as determining the height or width of an element you can plainly see rendered on the screen - that turn into hours of twiddling.

In Android, you attach a view tree to your Actitity in order to be able to interact with the UI.
Once this is done, you can get references to your widgets (views) using a call like this from your Activity:
TextView t = (TextView) findViewById(R.id.txtName);

Now, views all sport a method like this: t.getHeight()
Which, presumably returns the actual height of that view element. Problem is, this method invariably returns zero.

At first I thought it was because I was calling this during the onCreate() method in my activity class. Perhaps the layout wasn't fully calculated at this time. So I moved my code to the onResume() method. This method is called whenever the activity is entered or re-entered. But again, no luck. Strange, as the layout at this point should be fully ready to display. I think there's a bug here in Android (I'm at version 2.2 currently.)

Luckily, I accidentally hit the search button while running a test on my EVO and, lo and behold, the height values I was trying to report on a TextView in my layout suddenly had non-zero values in them. Obviously, some sort of layout process had been forced, and the state of the view tree had been updated.

How can I get notified when the layout is fully calculated? The short answer is to attached a listener for a callback when the layout is calculated. Here's what it looks like.

@Override
public void onCreate(Bundle savedInstanceState)
{
  super.onCreate(savedInstanceState);
  RelativeLayout vMain = (RelativeLayout)

     this.getLayoutInflater().inflate(R.layout.main, null);
  vMain.getViewTreeObserver().addOnGlobalLayoutListener(
  new ViewTreeObserver.OnGlobalLayoutListener() {
    public void onGlobalLayout() {
      DisplayLayoutDimensions();
    }
  });
  
setContentView(vMain);
}


public void DisplayLayoutDimensions()
{
  StringWriter sw = new StringWriter(1000);
  PrintWriter out = new PrintWriter(sw);
  TextView t = (TextView) findViewById(R.id.textview);
  ImageView img = (ImageView) findViewById(R.id.hp67);
  out.printf("\nImage Drawable\n");
  out.printf("ImageView Height dp: %d\n", img.getHeight());
  out.printf("ImageView Measured Height dp: %\n",
     img.getMeasuredHeight());
  t.setText(sw.toString());
}

Friday, January 21, 2011

Android image drawing

When the emulator is in programming mode (or a program is currently entered/loaded), I wanted to display a magnetic card image in the magnetic card holder slot in the same place it would go on the real HP67.

Problem: how to have the image be invisible when no program is loaded or being entered?

There is a setVisibility() method on the ImageView class, but I'm darned if I could get it to work. So, I adjusted the alpha instead, using the setAlpha() method, and this works beautifully.

Here's how I did it. This works, but right now it uses absolute margin offsets in the RelativeLayout view group. Later, I'm going to investigate how to make this more dynamic to the actual screen dimensions on the device.


  1. Place the card image file in the project's res\drawable folder. In my case, the card file is card.png.
  2. Add a tag to my main.xml layout - which is a RelativeLayout view group. Positioning the image where it should be displayed. The android:src attribute points to the image in the drawable folder. The id is assigned as well so we can get a reference to this view later:

    <RelativeLayout>
    ...
    <ImageView
    android:id="@+id/card"
    android:layout_alignParentLeft="true"
    android:layout_alignParentTop="true"
    android:layout_width="260dp"
    android:layout_height="50dp"
    android:layout_marginLeft="30dp"
    android:layout_marginTop="87dp"
    android:src="@drawable/card"
    />
    </RelativeLayout>
  3. In the activity class where this view is used, load the view. In my case, I want to have the image be initially invisible, so I set the alpha to zero immediately after setting the content view:

    _vMain = (RelativeLayout) this.getLayoutInflater().inflate(R.layout.main, null);
    setContentView(_vMain);
    ImageView card = (ImageView) findViewById(R.id.card);
    card.setAlpha(0);
  4. Later, when the user clicks the program-mode button, I have logic that reveals the card image like this:

    ImageView card = (ImageView) findViewById(R.id.card);
    card.setAlpha(255);

Sunday, January 9, 2011

Android windowBackground and Title hiding

To create a window without a window title, and including a custom window background - in this case an image:

1. Create a style in your strings.xml file under the res\values folder in your project:
<style name="hp67Background" parent="android:Theme">
<item name="android:windowBackground">@drawable/hp67</item>
<item name="android:windowNoTitle">true</item>
</style>

This style inherits from the android theme via the "parent" attribute. The background can be any drawable type, including, if desired a color code - such as #ff0000. In this case, I'm using a png image (hp67.png) saved under the res\drawable folder.

The android:windowNoTitle setting is set to true.

2. Edit your manifest XML file to include the style as a theme on the tag:

<application android:icon="@drawable/icon" android:label="@string/app_name" android:theme="@style/hp67Background">

This should do the trick!

TableLayout Example

After piecing together posts from the Android developer Google group , and referring to the Android SDK reference, I was able to create a working XML example for the TableLayout:


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="#300000"
android:textColor="#ff0000"
android:text="@string/display_value"
/>
<TableLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<TableRow>
<Button
style="@style/calcButton"
android:layout_column="1"
android:layout_height="wrap_content"
android:text="@string/one"
/>
<Button
android:layout_column="2"
android:layout_height="wrap_content"
android:text="2"
/>
<Button
android:layout_column="3"
android:layout_height="wrap_content"
android:text="3"
/>
<Button
android:layout_column="4"
android:layout_height="wrap_content"
android:text="+"
/>
</TableRow>
<TableRow>
<Button
android:layout_column="1"
android:layout_height="wrap_content"
android:text="4"
/>
<Button
android:layout_column="2"
android:layout_height="wrap_content"
android:text="5"
/>
<Button
android:layout_column="3"
android:layout_height="wrap_content"
android:text="6"
/>
<Button
android:layout_column="4"
android:layout_height="wrap_content"
android:text="-"
/>
</TableRow>
<TableRow>
<Button
android:layout_column="1"
android:layout_height="wrap_content"
android:text="7"
/>
<Button
android:layout_column="2"
android:layout_height="wrap_content"
android:text="8"
/>
<Button
android:layout_column="3"
android:layout_height="wrap_content"
android:text="9"
/>
<Button
android:layout_column="4"
android:layout_height="wrap_content"
android:text="/"
/>
</TableRow>
<TableRow>
<Button
android:layout_column="2"
android:layout_height="wrap_content"
android:text="0"
/>
<Button
android:layout_column="3"
android:layout_height="wrap_content"
android:text="="
/>
<Button
android:layout_column="4"
android:layout_height="wrap_content"
android:text="&#x00f7;"
/>
</TableRow>
</TableLayout>

</LinearLayout>