Tuesday, 27 November 2012

Android: How to set the height and width of a DialogFragment

If you've played around with creating dialogs using the DialogFragment class, you've probably noticed that the layout_width and layout_height parameters that you assign in your DialogFragment's xml layout file are ignored (disrespected!) and the operating system assigns height and width to your DialogFragment however it so wishes!

Because of this problem and because it is highly recommended to create dialogs with the DialogFragment class, what I do now is specify the layout_width and layout_height parameters in my DialogFragment's xml layout file as match_parent. And then, more importantly, I specify the dialog's height and width in my DialogFragment.onStart() method as follows:
 
@Override
public void onStart() {
  super.onStart();

  // safety check
  if (getDialog() == null) {
    return;
  }

  int dialogWidth = ... // specify a value here
  int dialogHeight = ... // specify a value here

  getDialog().getWindow().setLayout(dialogWidth, dialogHeight);

  // ... other stuff you want to do in your onStart() method
}
 

Android: DialogFragment enter and exit animations

Adding enter and exit animations to your DialogFragment's dialog is a three-step process:
  1. define the enter and exit animations;
  2. add your animations to a style;
  3. set the style as your dialog's "window animations".
That's the gist of it. In greater detail, and assuming you want fade in and fade out animations, this is what you need to do.

First, define the animations. Add a fade_in_dialog.xml file to your res/anim folder which has the following xml code...

<?xml version="1.0" encoding="utf-8"?>
<alpha
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:interpolator="@android:anim/accelerate_interpolator"
  android:fromAlpha="0.0"
  android:toAlpha="1.0"
  android:duration="400" />

And a fade_out_dialog.xml file to your res/anim folder which has the following xml code...

<?xml version="1.0" encoding="utf-8"?>
<alpha
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:interpolator="@android:anim/decelerate_interpolator"
  android:fromAlpha="1.0"
  android:toAlpha="0.0"
  android:duration="400" />

Second, add a style to your res/styles.xml file defined as follows:

<style
  name="dialog_animation_fade" >
  <item name="android:windowEnterAnimation">@anim/fade_in_dialog</item>
  <item name="android:windowExitAnimation">@anim/fade_out_dialog</item>
</style>

Third (and final!), add the following code to your DialogFragment.onStart() method:

@Override
public void onStart() {
  super.onStart();

  // safety check
  if (getDialog() == null) {
    return;
  }

  // set the animations to use on showing and hiding the dialog
  getDialog().getWindow().setWindowAnimations(
      R.style.dialog_animation_fade);
  // alternative way of doing it
  //getDialog().getWindow().getAttributes().
  //    windowAnimations = R.style.dialog_animation_fade;

  // ... other stuff you want to do in your onStart() method
}

Done! Your dialogs should now fade in and fade out when shown and dismissed respectively.

Monday, 19 November 2012

Android Debug Bridge: detect Google Nexus 7 tablet

Spent an awful, awful, awful amount of time this morning trying to hook up a Google Nexus 7 tablet to Android Debug Bridge on my computer.

These are the steps I took which you'd think would suffice:
  • Go to Settings in the tablet,
  • Make Developer Options visible by clicking About Tablet seven times,
  • Enable USB Debugging in Developer Options,
  • Select the app to debug in the Select Debug App option of Developer Options,
  • Install the driver onto my computer as per the instructions here: Using Hardware Devices. I tried the driver in the [Android SDK]\extras\google folder of my computer as well as the one available for download from ASUS.
However, on plugging my tablet into the USB port of my computer and entering the command adb devices in the command prompt, the device was still not listed, i.e. not detected by Android Debug Bridge.

After much wild guessing and frantic searching on Stack Overflow, I finally found the solution, as follows:
  • Go to Settings,
  • then Storage,
  • then USB Computer Connection,
  • then select Camera (PTP) instead of Media Device (MTP).
That should do it!

Monday, 22 October 2012

Android - how to add padding or a border to a Bitmap

Here's a neat (I'd like to think) method (which you can tinker with) for adding padding or a border to a Bitmap:
 
 
/** Creates and returns a new bitmap which is the same as the provided bitmap
 * but with horizontal or vertical padding (if necessary)
 * either side of the original bitmap
 * so that the resulting bitmap is a square.
 * @param bitmap is the bitmap to pad.
 * @return the padded bitmap.*/
public static Bitmap padBitmap(Bitmap bitmap)
{
  int paddingX;
  int paddingY;

  if (bitmap.getWidth() == bitmap.getHeight())
  {
    paddingX = 0;
    paddingY = 0;
  }
  else if (bitmap.getWidth() > bitmap.getHeight())
  {
    paddingX = 0;
    paddingY = bitmap.getWidth() - bitmap.getHeight();
  }
  else
  {
    paddingX = bitmap.getHeight() - bitmap.getWidth();
    paddingY = 0;
  }

  Bitmap paddedBitmap = Bitmap.createBitmap(
      bitmap.getWidth() + paddingX,
      bitmap.getHeight() + paddingY,
      Bitmap.Config.ARGB_8888);

  Canvas canvas = new Canvas(paddedBitmap);
  canvas.drawARGB(0xFF, 0xFF, 0xFF, 0xFF); // this represents white color
  canvas.drawBitmap(
      bitmap,
      paddingX / 2,
      paddingY / 2,
      new Paint(Paint.FILTER_BITMAP_FLAG));

  return paddedBitmap;
}
 

Sunday, 21 October 2012

Advice on preparing images for an Android app

Here's some things to keep in mind when preparing (or advising or delegating to someone to prepare) graphics/images for an Android app:
  • Images file names must consist of lower case letters, numbers and the underscore character only, i.e. [ a-z | 0-9 | _ | . ]. Upper case characters not allowed. Also, all images are to be contained in a single drawable folder so better to give images meaningful names to minimise renaming etc later, e.g. myactivity_header_black.png or button_blue_pressed.png.
  • For all images which are to act as buttons, have two states: a normal state and a pressed state. Likewise, for all images which are to act as text input boxes, have two states: a normal state and a focused/selected state.
  • Device screen sizes come in all sorts. The easiest thing I find is to design for a 960*640 screen and to bear in mind when designing that some devices will be slightly taller, slighter wider etc. Also, chop screen designs down to their smallest image components so that click handlers etc can be placed on the images themselves rather than on certain co-ordinates of the screen (remember: the position of items will vary from screen to screen).
  • Related to the previous point: bear in mind that images which are to span the entire height or width of the screen (backgrounds, headers, footers etc) will get stretched and shrunk slightly depending on the screen size of the handset.
  • Avoid putting text in your images (buttons etc) except where necessary (e.g. where a special font or look of the text is required). Add text in code instead. As well as allowing images to be re-used (and thus reducing the size of the app), this also makes localisation easier.
  • Avoid padding graphics with whitespace (this can be done in code) except where necessary for shadows etc.

Writing code in Blogger

Here's a technique taken from Mijalko's Blog for writing computer code in Blogger, i.e. surround your code with pre and code tags as follows:

<pre style="border: 1px dashed rgb(153, 153, 153); padding: 5px; overflow: auto; font-family: Andale Mono,Lucida Console,Monaco,fixed,monospace; color: rgb(0, 0, 0); background-color: rgb(238, 238, 238); font-size: 12px; line-height: 14px; width: 100%;">
<code>
This is code.
</code>
</pre>


The output will look like this:

This is code.

Done! :)

Saturday, 20 October 2012

Android - how to scale a bitmap and retain the original's quality

If you've tried the Bitmap.createScaledBitmap(Bitmap src, int dstWidth, int dstHeight, boolean filter) method you'll have experienced the bad (blocky) quality of the Bitmap that it produces. Here's an alternative method I found (didn't compose myself) for scaling Bitmaps and retaining the quality of the original:

/**
 * Scales the provided bitmap to have the height and width provided.
 * (Alternative method for scaling bitmaps
 * since Bitmap.createScaledBitmap(...) produces bad (blocky) quality bitmaps.)
 * 
 * @param bitmap is the bitmap to scale.
 * @param newWidth is the desired width of the scaled bitmap.
 * @param newHeight is the desired height of the scaled bitmap.
 * @return the scaled bitmap.
 */
 public static Bitmap scaleBitmap(Bitmap bitmap, int newWidth, int newHeight) {
  Bitmap scaledBitmap = Bitmap.createBitmap(newWidth, newHeight, Config.ARGB_8888);

  float scaleX = newWidth / (float) bitmap.getWidth();
  float scaleY = newHeight / (float) bitmap.getHeight();
  float pivotX = 0;
  float pivotY = 0;

  Matrix scaleMatrix = new Matrix();
  scaleMatrix.setScale(scaleX, scaleY, pivotX, pivotY);

  Canvas canvas = new Canvas(scaledBitmap);
  canvas.setMatrix(scaleMatrix);
  canvas.drawBitmap(bitmap, 0, 0, new Paint(Paint.FILTER_BITMAP_FLAG));

  return scaledBitmap;
}