Java For Android

Java for Android is subtly different to vanilla Java. Learn about the differences and what they mean for your code in this Java for Android article. By Darryl Bayliss.

Leave a rating/review
Save for later
Share
You are currently viewing page 3 of 4 of this article. Click here to view the first page.

Fragments

Before tackling the next topic in Java for Android, you need to know a little bit more about how the UI of an Android app is constructed. Activities are great for managing the entire content of the screen, but they don’t like to share. Luckily there’s a great way to break down the UI into smaller component called fragments.

Fragments are like activities, but with the added bonus of being able to be embed in activities as though they were views. They have lifecycle methods similar to an activity’s onCreate() and can receive input just like an activity.

A layout for a fragment looks exactly the same as a layout for an activity; it contains a few view declarations. You even hook them up to the code in the same way, by declaring the view as a variable and finding the view through the identifier you provide in the layout.

The following code creates a fragment from a layout file at res/layout/fragment_embedded.xml:

public class EmbeddedFragment extends Fragment {
  @Override
  public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    // Inflate the layout for this fragment
    return inflater.inflate(R.layout.fragment_embedded, container, false);
  }
}

You first extend your class to inherit the behavior of a fragment and then use one of its lifecycle methods, namely onCreateView(), to set up the fragment. Then you return a layout to show for that particular fragment.

This is all simple enough when your activity and embedded fragment work in isolation, but what if you need them to communicate? Here’s an example method inside an activity that is trying to communicate with a fragment:

private void updateFragment() {
  EmbeddedFragment fragment = (EmbeddedFragment) getFragmentManager().findFragmentById(R.id.fragment_embedded);
  fragment.setTextViewText("Hello Little Fragment");
}

Because the fragment exists within the activity, you access it with findFragmentById and an identifier defined in XML. Then, you can easily invoke a fragment’s public methods, as shown in the above example, setTextViewText().

Conversely, a fragment can access its associated activity by calling getActivity(). This works in many simple situations, but it’s more fun to discuss in terms of an intricate scenario.

Interfaces

What if your activity was home to three lively fragments, each doing its own job, but needing to inform other fragments of what it’s doing at specific times?

In theory, fragments should only concern themselves with their own purpose and needn’t know they exist next to others; the activity is the mastermind in a sense. It’s the only one that has access to all the fragments and knows what they do.

This calls for a Java feature known as Interfaces.

An interface is like a class, but without the implementation. Instead it defines the public API. Classes can then implement these interfaces and your code no longer relies on concrete class implementations, instead knowing only that “this is an unknown object upon which I can call these interface methods”.

Interfaces allow your objects to work indirectly with other objects without exposing their inner workings. Think of something that is extremely complicated to build but quite simple to use: a car, a television set or even the device you’re using to read this tutorial.

You probably don’t know all of the inner workings of these things, but you certainly know how to operate them. Interfaces do the same thing.

In Android, interfaces are useful for facilitating communication fragment to activity, or fragment to fragment. It works like this:

public class EmbeddedFragment extends Fragment {
  // 1
  OnItemInListSelectedListener mCallback;

  // 2
  public interface OnItemInListSelectedListener {
    public void onItemInListSelected(int position);
  }

  @Override
  public void onAttach(Activity activity) {
    super.onAttach(activity);
    // 3      
    try {
      mCallback = (OnItemInListSelectedListener) activity;
    } 
    catch (ClassCastException e) {
      throw new ClassCastException(activity.toString()  + " must implement OnItemInListSelectedListener");
    }
  }

  @Override
  public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    return inflater.inflate(R.layout.fragment_embedded, container, false);
  }
}

Look at this in detail:

  1. In the fragment, you declare a member variable to store a custom object that implements OnItemInListSelectedListener and you name it mCallback.
  2. Next, you create the interface and declare the required methods — your interface can have many methods.
  3. In onAttach(), a fragment lifecycle method, you check if the activity your fragment is attached to conforms to OnItemInListSelectedListener. If not, then it can’t serve your purposes and you have a problem. The ClassCastException describes this during runtime. It’s best to signal this to yourself and other programmers so you can catch the problem early.

The next thing is to make your activity use the interface:

public class MainMenuActivity extends Activity implements EmbeddedFragment.OnItemInListSelectedListener {

  ...

  public void onItemInListSelected(int position) {
      // Received a message from the Fragment, you'll do something neat here
  }
}

The implements keyword in the class definition states that this class will implement the specified interface. You then need to provide implementations for all the methods specified on the interface. On this custom interface there is only one method, and you can see that it has a skeleton implementation in the above code snippet.

That’s the hard part, but you’re still not communicating fragment to fragment. Say you have defined a second fragment, ListDetailFragment, with identifier fragment_list_detail and a method named showListItemDetails(int). To get them talking, you simply do what you did earlier to establish activity to fragment communications with one of the fragment’s public methods:

public void onItemInListSelected(int position) {
  ListDetailFragment listDetailFragment = (ListDetailFragment) getFragmentManager.findFragmentById(R.id.fragment_list_detail);
  listDetailFragment.showListItemDetails(position);   
}

Just like that, you’ve built a way to communicate between fragments and activities. Interfaces are useful for establishing communication while keeping components separate. Learning how components work with fragments and activities is a vital part of keeping your app architecture flexible.

Annotations

Have you noticed those peculiar lines of code above some of the method names in this article?

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

Those words prefixed with an @ are Annotations. They provide extra information at a variety of stages of an app. Annotations provide extra information to the compiler at runtime or even to generate extra code.

The most common annotation in Android is @Override, which exists to inform the compiler that a specific method should override one from the super class. If your method doesn’t actually override anything, then the compiler will fail and tell you, providing a nice safety net from any oddities you might have encountered.

Another well-known annotation is @TargetApi. It exists to allow your methods to indicate they are for use on a specific or newer version of Android. Using a method with @TargetApi set to a version higher than the current target of your app will cause the compiler to complain that you’re using functionality that isn’t available for your version of Android.

It’s a complaint, but it’s also a polite warning. You can still run your app despite the warning, but you can pretty much count on an ensuing crash.

When you’re building an Android app, you can almost count on needing these two annotations. There are others, and you can learn about the form the Android API documentation. It’s also possible to create your own annotations to automate specific tasks, generate code and other helpful functions.

A shining example of this is the third party library AndroidAnnotations. It provides a variety of custom annotations that simplify multi-line functions into single-line annotations and other useful features. Take a look at what annotations are available and what they offer to your apps.

Contributors

Over 300 content creators. Join our team.