Monday, 10 February 2014

Android - how to make a custom view which looks like a Spinner

I'll show you in this post how to make a custom view which looks like a Spinner and to which you can assign your own text and click listener. There's just two steps.

Step 1 - define an xml layout for your custom view as follows:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:id="@+id/view_root"
  android:layout_width="match_parent"
  android:layout_height="wrap_content"
  style="?android:attr/spinnerStyle" >

  <include
    layout="@android:layout/simple_spinner_item"
    android:id="@+id/view_text"
    android:layout_width="match_parent"
    android:layout_height="wrap_content" />

</FrameLayout>

We'll assume you've saved the above layout in res/layout/spinner_lookalike.xml.

Step 2 - create your custom view class as follows:

/**
 * This class is essentially a {@link FrameLayout}
 * which looks like a {@link Spinner}.
 */
public class SpinnerLookalikeView extends FrameLayout {

  private ViewGroup rootView;
  private TextView textView;

  /**
   * Constructor to use when creating View from code.
   * */
  public SpinnerLookalikeView(Context context) {
    super(context);
    initialise();
  }

  /**
   * Constructor that is used when inflating View from XML.
   * */
  public SpinnerLookalikeView(Context context, AttributeSet attrs) {
    super(context, attrs);
      initialise();
  }

  /**
   * Constructor that is used when inflating View from XML
   * and applying a class-specific base style.
   * */
  public SpinnerLookalikeView(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
    initialise();
  }

  @Override
  public void setOnClickListener(OnClickListener listener) {
    rootView.setOnClickListener(listener);
  }

  /**
   * Sets the text shown in this view.
   * 
   * @param text
   */
  public void setText(String text) {
    textView.setText(text);
  }

  /**
   * Initialisation method to be called by the constructors of this class only.
   */
  private void initialise() {
    LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    inflater.inflate(R.layout.spinner_lookalike, this);

    rootView = (ViewGroup) findViewById(R.id.view_root);
    textView = (TextView) findViewById(R.id.view_text);
  }

}

That's it! Now you can add this custom view to your activities and assign text and a click listener to it.

Sunday, 26 January 2014

Android: NumberPicker - calling setWrapSelectorWheel(false) does nothing!!

In case you're using a NumberPicker instance, you have a good range of min-max values and you're calling NumberPicker.setWrapSelectorWheel(false) but it isn't disabling wrapping around the min/max values, it could be a simple issue of re-ordering your method calls. So, this won't work...

numberPicker.setWrapSelectorWheel(false);
numberPicker.setMinValue(0);
numberPicker.setMaxValue(10);
numberPicker.setValue(5);

... but this will...

numberPicker.setMinValue(0);
numberPicker.setMaxValue(10);
numberPicker.setValue(5);
numberPicker.setWrapSelectorWheel(false);

The key is to call NumberPicker.setWrapSelectorWheel(false) after you've set your NumberPicker's min and max values.

Saturday, 23 November 2013

Java/Android - Method for getting a url's domain name

Below is a method which extracts a url's domain name and which uses simple String matching. What it actually does is extract the bit between the first "://" (or index 0 if there's no "://" contained) and the first subsequent "/" (or index String.length() if there's no subsequent "/"). The remaining, preceding "www(_)*." bit is chopped off. I'm sure there'll be cases where this won't be good enough but it should be good enough in most cases!

I read on forums that the java.net.URI class could do this (and was preferred to the java.net.URL class) but I encountered problems with the URI class. Notably, URI.getHost() gives a null value if the url does not include the scheme, i.e. the "http(s)" bit.


/**
* Extracts the domain name from {@code url}
* by means of String manipulation
* rather than using the {@link URI} or {@link URL} class.
*
* @param url is non-null.
* @return the domain name within {@code url}.
*/
public String getUrlDomainName(String url) {
  String domainName = new String(url);

  int index = domainName.indexOf("://");

  if (index != -1) {
    // keep everything after the "://"
    domainName = domainName.substring(index + 3);
  }

  index = domainName.indexOf('/');

  if (index != -1) {
    // keep everything before the '/'
    domainName = domainName.substring(0, index);
  }

  // check for and remove a preceding 'www'
  // followed by any sequence of characters (non-greedy)
  // followed by a '.'
  // from the beginning of the string
  domainName = domainName.replaceFirst("^www.*?\\.", "");

  return domainName;
}

Tuesday, 5 March 2013

Facebook Android SDK 3.0 - getting a user's profile picture

Here's another Facebook operation which you'd think should not only be easy to do but also be easy to find in the documentation. It certainly was easy to do, but once again wasn't so easy to find in the documentation. Oh and, strictly speaking, you don't need the Facebook SDK for this. You just need to make a HTTP GET call to the Facebook user's picture connection. (You'll need the Facebook SDK to get the user's Facebook id beforehand.)

final String userFacebookId = ...

new AsyncTask<Void, Void, Bitmap>()
{
  @Override
  protected Bitmap doInBackground(Void... params)
  {
    // safety check
    if (userFacebookId == null)
      return null;

    String url = String.format(
        "https://graph.facebook.com/%s/picture",
        userFacebookId);

    // you'll need to wrap the two method calls
    // which follow in try-catch-finally blocks
    // and remember to close your input stream

    InputStream inputStream = new URL(url).openStream();
    Bitmap bitmap = BitmapFactory.decodeStream(inputStream);

    return bitmap;
  }

  @Override
  protected void onPostExecute(Bitmap bitmap)
  {
    // safety check
    if (bitmap != null
        && !isChangingConfigurations()
        && !isFinishing())
      // do what you need to do with the bitmap :)
  }
}.execute();

Saturday, 2 March 2013

When methods and functions do too much

It's like asking a robot to get you some milk and it comes back to you with flavoured milk. You might want flavoured milk, but you might not! Your methods should only do as much as they are contracted to do. No more.

Here's another example: you create an Android AlertDialog with the AlertDialog.Builder class. Now every time you click one of the buttons of your AlertDialog, the AlertDialog is automically dismissed, as well as notifying you that a button was clicked. But you don't want the AlertDialog to be dismissed, you only want to be notified that a button was clicked!

Sometimes doing more is less helpful.

Friday, 1 March 2013

Android - load and show html from a file into a TextView

There's a known bug in the Android WebView such that a WebView set with a transparent background colour does not appear with a transparent background on all devices. None of the workarounds are definitive and work for all devices. An alternative to loading html in a WebView is to load it in a TextView. There are limitations as to what HTML tags can be used (see here) but if you're only doing basic HTML tagging, this will work:

try
{
  InputStream inputStream = getResources().getAssets().open("myFile.html");

  String html = IOUtils.toString(inputStream);

  myTextView.setText(Html.fromHtml(html));
}
catch (IOException exception)
{
  myTextView.setText("Failed loading html.");
}

Thursday, 28 February 2013

Facebook Android SDK 3.0 - how to log out and close a session

In the previous two Blog posts (here and here), I showed how to connect a user to their Facebook account (i.e. open a Facebook session) and how to make a Facebook API request (requesting additional Facebook permissions en route if necessary). In this final Blog post, I'll show to close the Facebook session, as follows:

public void disconnectFacebookAccount()
{
  showProgressDialog("Disconnecting Facebook account...");

  new AsyncTask<Void, Void, Boolean>()
  {
    @Override
    protected Boolean doInBackground(Void... params)
    {
      if (Session.getActiveSession() != null)
        Session.getActiveSession().closeAndClearTokenInformation();

      clearFacebookInfoFromSharedPreferences();

      // perform any other operations you need to do perform here
      // such as clearing local database tables and so forth

      return true;
    }

    @Override
    protected void onPostExecute(Boolean result)
    {
      // safety check
      if (isFinishing())
        return;

      if (result == null
          || result == false)
        onFailedFacebookDisconnect();
      else
        onSucceededFacebookDisconnect();
    }
  }.execute();
}

That's it! That's the end of my Facebook Android 3.0 series. You should have enough know-how now for all kinds of Facebook API requests! :)