David Janes' Code Weblog

November 11, 2011

Beautiful Code

android,code fragments,java · admin · 8:40 am ·

This is what I’m aiming for — powerful declarations, minimal logic. UIButtonGroup, UIWebView and UIBundleHelper carry a lot of the weight here and I hope to explain (and share) all of these soon.

public class TTActivityInfo
  extends TTAbstractActivity
{
  public Button uiLeftButton;
  public Button uiRightButton;
  public UIButtonGroup buttonGroup;

  public WebView uiWebView;

  static String lActivityTitle = "Welcome!";
  static String lLeftButtonTitle = "Welcome";
  static String lRightButtonTitle = "About the app";

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

    this.setContentView(R.layout.tt_activity_info);

    UIHelper.bindViews(this);
    UIHelper.hookupButtons(this);

    _configureWebView();
    _configureNavigationBar();
    _configureButtons();

    UIBundleHelper.restoreActivityStateFromBundle(this, savedInstanceState);

    if (savedInstanceState == null) {
      _forceWebViewToCurrentState();
    }
  }

  @Override
  protected void onSaveInstanceState(Bundle _bundle)
  {
    UIBundleHelper.saveActivityStateToBundle(this, _bundle);
  }

  protected void _configureNavigationBar()
  {
    TTUI.configureNavigationBarTitle(this, lActivityTitle);
  }

  protected void _configureButtons()
  {
    TTUI.configureBlueButton(this, uiLeftButton, lLeftButtonTitle);
    TTUI.configureBlueButton(this, uiRightButton, lRightButtonTitle);

    buttonGroup = new UIButtonGroup(uiLeftButton, uiRightButton);
    buttonGroup.select(uiLeftButton);
  }

  protected void _configureWebView()
  {
    if (uiWebView == null) {
      return;
    }

    uiWebView.getSettings().setJavaScriptEnabled(true);
    uiWebView.setWebViewClient(new UIWebViewClient(this, "uiWebView"));
  }

  protected void _forceWebViewToCurrentState()
  {
    if (uiWebView == null) {
      return;
    }

    if (buttonGroup.getSelectedButton() == uiRightButton) {
      uiWebView.loadUrl("http://www.google.com");
    } else {
      uiWebView.loadUrl("http://www.davidjanes.com");
    }
  }

  public void _handleButtonSelected(Button _button)
  {
    buttonGroup.select(_button);

    _forceWebViewToCurrentState();
  }

  public void uiLeftButton_onClick(Button _button)
  {
    _handleButtonSelected(_button);
  }

  public void uiRightButton_onClick(Button _button)
  {
    _handleButtonSelected(_button);
  }
}

November 10, 2011

Activity (I): The First Rule of Android Activities

android,java · admin · 5:43 pm ·

Android Applications are structured around Activities. Understanding how Activities work and structuring your code within that framework is key to creating successful Android applications. Do not try to fight this: I know from hard personal experience that this does not work.

Here is the first thing you need to know about Android Activities, and understanding this will shape everything you do while coding them in the future:

Activities can be fucking destroyed and recreated at any time – even while the user is interacting with them.

Do you understand this and the implications of this? Probably not, but I’ll outline the implications of this over a few blog posts and how to work with it.

When your activity is destroyed, you lose everything – all your variables, everything you’ve setup, popup alerts, the Activity object itself. All gone.

Here are some times that an Activity will be destroyed and recreated:

  • You rotate your Android
  •  You open or close a Keyboard
  •  Another Activity is showing and Android needs the memory

In Android parlance, these are called “Configuration Changes“. There are ways to block this from happening, but I suggest it’s better to roll with the punches and “do the right thing”.

Fortunately Android destroys and recreates Activities in a well-defined structured manner and provides the proper places (albeit Java-clumsy) to save and restore the state so that the user won’t see that the whole interface they were looking at was destroyed and remade. More about this soon.

October 24, 2011

Automatically Binding Resources to Methods in Android Applications

android,code fragments,java · admin · 4:33 pm ·

AKA I’m as mad as hell at Java and not taking it any more Part II.

When developing Android applications, binding resources to end method calls is even more annoying than binding resources to variables.

One approach is to create an anonymous inner classes implementing various listener interface.

UIButton postButton = (Button) findViewById(R.id.share_post);
postButton.setOnClickListener(new View.OnClickListener() {
  public void onClick(View v) {
    // do stuff ...
  }
}

The other is to just implement the listener interface with whatever activity you’re using and figure out what button is being pressed.

I’d just as soon have the data wired up automatically to functions if it’s there. Here’s how I’m doing it now.

In MyActivity:

// class fields
public ImageButton uiButtonDining;

// in onCreate
UIHelper.bindImageButtons(this);

This will automatically wire to the following function

public void uiButtonDining_onClick(ImageButton _imageButton)
{
  // ... do stuff ...
}

I’ve only done this for ImageButton so far but I’m hoping this approach will scale for various other widgets.

The Code

Here’s The Magic for UIHelper.java:

static public void bindImageButtons(final Activity _activity) {
  Field[] instance_fields = _activity.getClass().getDeclaredFields();
  for (final Field instance_field : instance_fields) {
    if (!ImageButton.class.isAssignableFrom(instance_field.getType())) {
      continue;
    }

    LogHelper.debug(true, _activity, "field=" + instance_field.getName());

    try {
      final ImageButton button = (ImageButton) instance_field.get(_activity);
      if (button == null) {
        LogHelper.debug(true, _activity, 
        "Button is stil NULL", "field=" + instance_field.getName());
        continue;
      }

      String callback_onClick_name = instance_field.getName() + "_onClick";
      try {
        final Method callback_onClick = _activity.getClass().getMethod(
         callback_onClick_name, ImageButton.class);

        button.setOnClickListener(new View.OnClickListener() {
          public void onClick(View v) {
            LogHelper.debug(false, _activity, "clicked", "field=" + instance_field.getName());

            try {
              callback_onClick.invoke(_activity, button);
            } catch (IllegalAccessException x) {
              LogHelper.debug(true, _activity, 
              "Couldn't invoke callback", "callback=", callback_onClick, x);
            } catch (InvocationTargetException x) {
              LogHelper.debug(true, _activity, 
              "Couldn't invoke callback", "callback=", callback_onClick, x);
            }
          }
        });
      } catch (NoSuchMethodException x) {
        LogHelper.debug(true, _activity, 
        "no callback", "method=", callback_onClick_name);
      }
    } catch (IllegalAccessException x) {
      LogHelper.debug(true, _activity, 
      "Can't set R.id", "name=", instance_field.getName(), x);
    }
  }
}

October 23, 2011

Automatically Binding Resources to Fields in Android Applications

android,code fragments,java · admin · 4:53 pm ·

AKA I’m as mad as hell at Java and not taking it any more.

Here’s how I’ve been getting access to various UI resources in my Android applications. Almost certainly, you’re doing the same thing too.

layout/file.xml:

<TextView android:id="@+id/title" ... />

File.java:

TextView title = (TextView) parent.findViewById(R.id.title);

Over and over for as many UI elements as I need to access. To hell with that.

Here’s my new method of doing this

First, give the ID and the Fields the same name:

layout/file.xml:

<TextView android:id="@+id/uiTitleTextView" ... />

File.java:

public TextView uiTitleTextView;

Note that that’s a class field — it’s not in the method, and it must be public Then in onCreate I just do this:

UIHelper.bindViews(this);

And that’s it: every field just gets wired up.

The Code

Here’s The Magic for UIHelper.java:

static public void bindViews(Activity _activity)
{
  Field[] instance_fields = _activity.getClass().getDeclaredFields();
  for (Field instance_field : instance_fields) {
    if (!View.class.isAssignableFrom(instance_field.getType())) {
      continue;
    }
    
    try {
      Field id_field = R.id.class.getField(instance_field.getName());
      int r = id_field.getInt(R.id.class);
      
      View view = _activity.findViewById(r);
      if (view == null) {
        LogHelper.debug(true, _activity, 
          "Field expected in Layout but not found", "name=", 
          instance_field.getName());
        continue;
      }
      
      instance_field.set(_activity, view);
    } catch (NoSuchFieldException x) {
      LogHelper.debug(true, _activity, 
        "Field expected in R.id but not found", "name=", 
        instance_field.getName());
    } catch (IllegalAccessException x) {
      LogHelper.debug(true, _activity, 
        "Can't set R.id", "name=", 
        instance_field.getName(), x);
    }
  }
}

Making R.java universal in Android apps

android,java · admin · 4:03 pm ·

Android projects make R – the way of accessing resources by ID – tied to the particular project you’re building. This presents several problems:

  1. you can’t use the same source tree to general many applications, because your “common” source tree won’t be able to access R
  2. you can’t make universal libraries that work with R because R will be in some arbitrary package tied to a specific project

The solution is to make an R.java that’s not tied to a particular project. The method I came up for this is to duplicate the project specific R into a “well known” package. Because the ID names are the same in both Rs, you’ve essentially duck-typed yourself to a reusable R. Tim implemented this and has the details on his blog.

We’ll be making use of this in the next couple of blog posts.

How to use custom fonts in Android applications

android,code fragments,java · admin · 3:07 pm ·

Here’s how to use custom fonts in your Android application.

  1. Select your font – for example, Ubuntu. Make sure you use a royalty-free font (such as that one) or you pay the appropriate licensing fee (because you could get in a world of legal pain and because don’t be a dick)
  2. In the root folder of your Android project, make sure there’s a folder called assets/fonts. The fonts subdirectory is entirely optional but to my taste.
  3. You cannot specify the font (really the typeface) in your resources, so you have to do in code as follows
Typeface font = Typeface.createFromAsset(_activity.getAssets(), "fonts/Ubuntu-Bold.ttf");
textView.setTypeface(font);

And that’s it.

November 7, 2010

Twitter4J – working with the source in Android

android,java · David Janes · 8:58 am ·

Twitter4J is a is highly spoken of Java library for working with the Twitter API. We needed to implement a simple List Widget that would display Twitter statuses based on a Twitter List, but unfortunately at this time Twitter4J doesn’t supply a method to easily do this (and the one that does do it requires authentication).

Here’s how we did it. First, to the Android project, we:

  • removed the Twitter4J JAR file from libs
  • added twitter4j directory (and subdirectories) to src – this is the directory with the .java files in it
  • added the following libraries to the project (in libs):
    • commons-logging-1.1.1.jar (from here)
    • log4j-1.2.16.jar (from here)
    • slf4j-api-1.6.1.jar
    • slf4j-simple-1.6.1.jar (from here)

I’m a little scared by the number of libraries we had to add just to handle logging (on a limited mobile phone!), but what can you do?

Then we added one new method (sigh) to the code:

public ResponseList getUserListStatuses(String listOwnerScreenName, String id, Paging paging)
  throws TwitterException
{
  return StatusJSONImpl.createStatusList(
   http.get(
    conf.getRestBaseURL() + listOwnerScreenName +"/lists/" + id + "/statuses.json",
    paging.asPostParameterArray(Paging.SMCP, Paging.PER_PAGE), auth));
}

The only issue is we’re getting a lot of weird warnings, but it seems to work well. We’ll probably take this out if the Twitter4J folks add the method to the regular releases.

November 23, 2009

Animated Slideshow on Android

android,code fragments,java · David Janes · 7:20 pm ·

After a brutal day of running into many Android bugs and misdocumentations, I’ve finally figured out how to create a slideshow of images for Android, with each imaging fading to the next. This is not unlike AnimationDrawable, except with a smoother and slower transition between images.

First, you need a resource file with something like this in it. The FrameLayout is the clever bit: it stacks everyone of its children on top of each other.

<FrameLayout
 android:id="@+id/frame"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
>
 <ImageView
 android:id="@+id/slide_1"
 android:layout_gravity="center_vertical"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 />
 <ImageView
 android:id="@+id/slide_2"
 android:layout_gravity="center_vertical"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 />
</FrameLayout>

Then you need code that looks like this:

public class TopListActivity
  extends Activity
{
  private static class AnimationAlphaTimer
    extends TimerTask
    implements Animation.AnimationListener
  {
    TopListActivity topList;
    Vector<BitmapDrawable> images;
    int count = 0;

    public AnimationAlphaTimer(TopListActivity _topList)
    {
      this.topList = _topList;

      this.images = new Vector<BitmapDrawable>();
      for (int i = 0; ; i++) {
      // LOAD IMAGES HERE
      }

      if (this.images.size() > 0) {
        this.topList.slide_0.setBackgroundDrawable(this.images.get(0));

        if (this.images.size() > 1) {
          this.topList.slide_1.setBackgroundDrawable(this.images.get(1));
        }
      }

      this.count = 1;
    }

    public void launch()
    {
      if (this.images.size() >= 2) {
        (new Timer(false)).schedule(this, 100);
      }
    }

    @Override
    public void run()
    {
      this.doit();
      this.cancel();
    }

    private void doit()
    {
      if ((this.count % 2) == 0) {
        AlphaAnimation animation = new AlphaAnimation(1.0f, 0.0f);
        animation.setStartOffset(3000);
        animation.setDuration(3000);
        animation.setFillAfter(true);
        animation.setAnimationListener(this);

        this.topList.slide_1.startAnimation(animation);
      } else {
        AlphaAnimation animation = new AlphaAnimation(0.0f, 1.0f);
        animation.setStartOffset(3000);
        animation.setDuration(3000);
        animation.setFillAfter(true);
        animation.setAnimationListener(this);

        this.topList.slide_1.startAnimation(animation);
      }
    }

    public void onAnimationEnd(Animation animation)
    {
      if ((this.count % 2) == 0) {
        this.topList.slide_1.setBackgroundDrawable(
          this.images.get((this.count + 1) % (this.images.size()))
        );
      } else {
        this.topList.slide_0.setBackgroundDrawable(
          this.images.get((this.count + 1) % (this.images.size()))
        );
      }

      this.count++;
      this.doit();
    }

    public void onAnimationRepeat(Animation animation)
    {
    }
    public void onAnimationStart(Animation animation)
    {
    }
  }

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

    (new AnimationAlphaTimer(this)).launch();
  }
}

The “create a Timer trick” in onResume is courtesy of Diego Torres. If you just try to run the animation, it’s likely just to choke.

December 6, 2008

JavaFX 1.0 released

html / javascript,java · David Janes · 6:54 am ·

CNET has a very detailed article on JavaFX, Sun’s better-late-than-never-maybe-hopefully answer to Adobe’s Flash / AIR and Microsoft’s Silverlight:

With a back-to-the-future technology called JavaFX to be launched Thursday, Sun Microsystems hopes to attract a new class of developer while building a much-needed new revenue source.

JavaFX 1.0 returns to the sales pitch that Sun used during Java’s launch more than 13 years ago: a foundation for software on a wide variety of computing “clients” such as desktop computers or mobile phones. JavaFX builds on current Java technology but adds two major pieces.

First is a new software foundation designed to run so-called rich Internet applications–network-enabled programs with lush user interfaces. Second is a new programming language called JavaFX Script that’s intended to be easier to use than traditional Java.

The benefit for me (and maybe you) is that most web shops have three different classes of developers: backend programmers, HTML/CSS coders, and The Flash Guy. The problem with The Flash Guy is that they have to be involved to make any changes to Flash components; JavaFX instead provides components that can easily be manipulated by coders and by web designers (with a little training I think).

The JavaFX site looks a hell of a lot better than anything Sun’s produced in the past too, so maybe they’re learning that looks matter too. Here’s some examples to play with.

Powered by WordPress

Switch to our mobile site