David Janes' Code Weblog

October 23, 2011

Dynamically resize and stack many UIViews

code fragments,iphone · admin · 5:58 pm ·

The iOS’s UILabel is kind of an annoying beast – out-of-the-box it takes coding to get the correct height or to “top justify” it. If there’s too much text inside a UILabel, sometimes it shrinks the text which visually doesn’t make it look that great – the ideal solution would be to split the text over multiple lines and adjust everything else downward dynamically. Also if you have to have multiple labels with different styles a typical solution would be to use a UIWebView controller. However, this isn’t really suitable inside a UITableView where you have to have many views and you need to know the exact space to allocate.

Here’s a helper function that will take an Array of UIViews and stack them up, returning the height. If any of the UIViews are UILabels, it’ll resize them to the appropriate height.

/*
 *  Stack views on top of each other starting at a point.
 *  If they are labels, we resize. Return the height used.
 */
+ (CGFloat) uiStack:(NSArray*)_views startingAt:(CGPoint)_point
{
  if (!_views || !_views.count) {
    return  0;
  }

  for (UIView* view in _views) {
    if ([view isKindOfClass:[UILabel class]]) {
      UILabel* label = (UILabel*) view;
      CGSize labelSize = [label.text
                sizeWithFont:label.font
                constrainedToSize:CGSizeMake(label.frame.size.width, 1000.0)
                lineBreakMode:label.lineBreakMode];

      label.frame = CGRectMake(_point.x, _point.y, view.frame.size.width, labelSize.height);
      _point.y += labelSize.height;
    } else {
      view.frame = CGRectMake(_point.x, _point.y, view.frame.size.width, view.frame.size.height);
      _point.y += view.frame.size.height;
    }
  }

  return _point.y;
}

Enjoy!

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.

September 18, 2011

Adding alpha levels to images with PIL

code fragments,pil,python,tips · admin · 4:54 am ·

I recently was tasked by a client with replacing a number of images — PNGs with alpha levels, such that parts of the image were transparent — with new pictures. The issue was how to get the exact same alpha level holes in the new images.

Here’s how I did it. I renamed one of the original images “template.png” and assumed that all the source images will the exactly the same size as the template. I then wrote this Python script using the Python Imaging Library.

import sys
import os
import re
import Image

#
#   Get the template
#
itemplate = Image.open("template.png")
itemplate.getpixel(( 0, 0 ))
r, g, b, alpha = itemplate.split()

#
#   Get the JPGs - they must be exactly the same size
#
for in_file in os.listdir("."):
    if not in_file.endswith(".jpg"):
        continue

    isrc = Image.open(in_file)
    if isrc.size != itemplate.size:
        continue

    idst = isrc.convert("RGBA")
    idst.putalpha(alpha)

    idst.save(in_file[:-4] + ".png")

March 14, 2011

“Have Startups Become a Fetish?”

startups,sxsw,toronto · admin · 5:36 am ·

I’m totally loving this Gigaom article by Stacey Higgenbotham:

Since the magical breakout of Twitter in 2007 and the Foursquare success of 2009, SXSW has become more and more cluttered with startups trying to break out. It has also become a celebration of startups in general. However, that celebration has turned into a fetish — placing the act of creating a startup on a pedestal without casting any sort of critical eye on the quality or likelihood of that startup or idea succeeding.

[...] But amid the hundreds of startups launching, pitching, forming or otherwise trying to break out at SXSW, how many of them are real businesses? How many of them are thought out beyond a scrawl on a napkin, or a quick debate ahead of a startup weekend?

[...] The thousands of startups today that are pitching themselves at app competitions or in industry conferences all seem to think being a startup is enough. That daring to come up with some idea, any idea, and build a beta site is enough. That the users will come and then the business model will come and then the money will come. Google, Facebook and Twitter are their icons. Somehow the act of creating a startup has become the goal instead of the building of a business.

[...] My issue is less with those littering the web with launch pages — if people want to take some time to test out a web site idea in their spare time, that’s far better than watching Two and Half Men reruns — but with the media, the venture firms and the ecosystem that has been built up to worship this idea of a startup. Maybe a little less fawning in the coverage and a little more skepticism is needed.

I quoted a little more than I wanted to here but there’s so many good lines – read the whole thing. An “the emperor has no clothes moment”? Remember, the characters in Neil Stephenson novels — if that’s the life you’re trying to recreate — actually had business plans and actually tried to execute them.

 

February 28, 2011

Foursquare, Python & OAuth2

authentication,code fragments,python · admin · 11:21 am ·

Foursquare has revved their API, but there’s no Python Library to support it. Here’s how to use it from Python

  1. Get OAuth info from foursquare — the callback URL does not matter _but_ you must record here. https://foursquare.com/oauth/register. Fill in the results into CLIENT_ID, CLIENT_SECRET and CALLBACK_URL
  2. Run this, enter the URL in browser, get code as FS_ACCESS_CODE, add back here
  3. Run this, fill in FS_ACCESS_TOKEN
  4. Run this, you’re authorized: enjoy!

Notes

  1. Python OAuth2 from here:  https://github.com/rodbegbie/python-oauth2
  2. This is entirely standing on the shoulder’s of Rod Begbie’s work.

Code:

import oauth2
import json
import pprint

OAUTH_BASE = "https://foursquare.com/oauth2/authenticate"

CLIENT_ID = ""
CLIENT_SECRET = ""
CALLBACK_URL = ""

FS_ACCESS_CODE = ""
FS_ACCESS_TOKEN = ""

client = oauth2.Client2(CLIENT_ID, CLIENT_SECRET, OAUTH_BASE)

if not CLIENT_ID:
  print "=== Register your Foursquare data here"
  print "=== Copy back CLIENT_ID, CLIENT_SECRET and CALLBACK_URL here"
  print "https://foursquare.com/oauth/"
elif FS_ACCESS_TOKEN:
  headers, content = client.request("https://api.foursquare.com/v2/users/self",
   access_token = FS_ACCESS_TOKEN)

  pprint.pprint({
    "headers" : headers,
    "content" : json.loads(content),
  })
elif not FS_ACCESS_CODE:
  print "=== Enter this URL in your browser."
  print "=== Copy the CODE in the URL that results into FS_ACCESS_CODE"
  print client.authorization_url(redirect_uri = CALLBACK_URL, 
    endpoint='authenticate')
else:
  print "=== Here is your Access Token"
  print "=== Copy into FS_ACCESS_TOKEN as start using Foursquare"
  print client.access_token(FS_ACCESS_CODE, redirect_uri= CALLBACK_URL,
    grant_type='authorization_code')["access_token"]

December 28, 2010

Display only one jPicker at a time

code fragments,javascript,jquery · David Janes · 9:26 am ·

I’ve been playing with jQuery (in general) and jPicker (in particular) over the last few days. I’m using jPicker because it’s not only a color picker but it also supports alpha-levels.

One – major – issue I’ve had is that jPicker doesn’t make it very easy to display only one control at a time, making the screen a bit of a visual mess when multiple color fields are being used.

Here’s how I fixed this.

$(document).ready(function() {
    $('body').click(function(evt) {
        if ($('div.jPicker.Container:visible').length > 0) {
            if ($(evt.target).parents('span.jPicker').length > 0) {
                $('div.jPicker.Container').each(function() {
                    if ($(this).css('z-index') == '10') {
                        $(this).hide('fast');
                    }
                });
            } else if ($(evt.target).parents('div.jPicker.Container').length == 0) {
                $('div.jPicker.Container').hide('fast');
            }
        }
    });
});

Notes:

  • this will dismiss the current jPicker if you click outside of one
  • the first if makes sure there’s something to do first
  • the second and third ifs take care of the case where the user clicks on the control to popup a jPicker. This takes advantage of the fact that the current jPicker is shown at z-index = 20 (which makes me wonder if we could do a lot of this with just a CSS selector for z-index = 10)
  • the final if takes care of the case where we’ve clicked outside of all jPicker controls

December 24, 2010

Python recipe – sorting strings with numbers in them

code fragments,python · David Janes · 9:16 am ·

Here’s a quick Christmas Eve recipe for sorting lists of strings that have numbers in them at some place.

import re
import string

numeric_rex = re.compile('(\d+)')

def numeric_sorter(a, b):
    av = filter(None, numeric_rex.split(a))
    bv = filter(None, numeric_rex.split(b))

    while av and bv:
        af = av.pop(0)
        bf = bv.pop(0)

        if af[0] in string.digits and bf[0] in string.digits:
            r = cmp(int(af), int(bf))
        else:
            r = cmp(af, bf)

        if r != 0:
            return  r

    if av:
        return  1
    elif bv:
        return  -1
    else:
        return  0

Here’s an example of it being used:

>>> items = [ "Item1", "Item10", "Item", "Item9", "Item100", "ItemLogo", ]
>>> items.sort()
>>> pprint.pprint(items)
['Item', 'Item1', 'Item10', 'Item100', 'Item9', 'ItemLogo']
>>> items.sort(numeric_sorter)
>>> pprint.pprint(items)
['Item', 'Item1', 'Item9', 'Item10', 'Item100', 'ItemLogo']

December 22, 2010

Installing Drupal on a Macbook Air

drupal,macintosh · David Janes · 1:58 pm ·

This documents how I installed Drupal on a brand new Macbook Air running Snow Leopard 10.6.5.

Web/Desktop/Shell: Install MySQL

  • Go here (“Mysql Community Server”) and download the 64 bit version
  • Double click on the DMG and Install
  • Drag the .PREF file in the DMG to the System Preferences

Note – I’m still having trouble starting MySQL from the command line. Right now I’m doing:

  • cd /usr/local/mysql/bin
  • nohup ./mysqld_safe &
  • disown %1

If you have advice, I’d appreciate it.

Web/Shell: Install Drupal

  • Download Drupal
  • Untar Drupal in ~/Sites/
  • In Apple > System Preferences > Sharing, select Web Sharing
  • In your browser go to http://localhost/~USERNAME/drupal-6.20/

In case it’s not clear what happened here:

  • Snow Leopard has an Apache Server built in
  • each user account gets it’s HTTP files in ~/Sites/
  • you turn on Apache by turning on Web Sharing
  • you browse by going to ~USERNAME/

You’ll notice at this stage we’re just seeing a directory listing, or if you’re a little more ambitious and click on “index.php”, PHP code. If you’re actually seeing a Drupal setup page, skip the next section.

Shell: Setup PHP

This is just following the excellent instructions from here.

  • sudo vim /etc/apache2/httpd.conf
  • uncomment LoadModule php5_module
  • bash (to start a subshell)
  • cd /etc
  • sudo cp php.ini.default php.ini
  • sudo chmod 666 php.ini
  • sudo vi /etc/php.ini
  • search for ;date.timezone
  • uncomment and change to:
    date.timezone=America/Toronto
  • restart apache: sudo apachectl restart
  • end the subshell: exit

Note – if you see the error “/usr/sbin/apachectl: line 82: ulimit: open files: cannot modify limit: Invalid argument“, follow the instructions here:

  • sudo vim /usr/sbin/apachectl
  • comment out ULIMIT_MAX_FILES at line 64

Local Web/Shell: Set up Drupal

  • go to the webpage install.php (on the URL figured out way above)
  • click on Install Drupal in English
  • if you get an error message about default.settings.php
    • cd ~/Sites/drupal-6.20/sites/default
    • cp default.settings.php settings.php
    • chmod a+w settings.php
  • if you get an error message about sites/defaults/files
    • cd ~/Sites/drupal-6.20/sites/default
    • mkdir files
    • chmod 777 files

At this point you should be brought to a “Database Configuration Page” and we have to leave the browser and go back to the command line.

Shell: MySQL Database Configuration

These instructions create a MySQL database called “drupal_db” with access by “drupal_user” using the password “drupal_password“.

  • /usr/local/mysql/bin/mysql -u root
    • create database drupal_db;
    • grant all privileges ON drupal_db.* TO "drupal_user"@"localhost" identified by "drupal_password";
    • grant all privileges ON drupal_db.* TO "drupal_user"@"127.0.0.1" identified by "drupal_password";
    • flush privileges;
    • exit;

Local Web: Enter MySQL information into Drupal

Back in the browser you should be on the Drupal Database Configuration page. Enter drupal_db, drupal_user and drupal_password (the values you created in the previous step)

Submit the form. If you get the following error message:

Failed to connect to your MySQL database server. MySQL reports the following message: No such file or directory.

Make sure you typed everything correctly. If necessary, click on Advanced Options and change the hostname from localhost to 127.0.0.1.

Local Web: Configure Site page for Drupal

  • Site Information:
    • Site Name: localhost (why not)
    • Site e-mail address: your@email.com
  • Administrator account:
    • Username: admin (why not)
    • E-mail address: your@email.com
    • Password:/Confirm password: your-favorite-password

Local Web: Drupal installation complete

After you hit submit, you should be finished installing Drupal, except for the next little cleanup step.

Shell: Clean up files

Drupal doesn’t like you leaving settings.php alterable by the web server around:

  • cd ~/Sites/drupal-6.20/sites/default
  • chmod go-w settings.php

The directory files must be left writable.

« Newer Posts · Older Posts »

Powered by WordPress

Switch to our mobile site