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.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
Access Modifiers
One final piece of the puzzle that helps setters and getters work is access modifiers. Take a look at the following snippet from the model above:
private ArrayList mReminderArrayList;
public void setReminderArrayList(ArrayList reminderArrayList) {
this.mReminderArrayList = reminderArrayList;
}
Notice the private and public keywords? These are access modifiers and they’re responsible for specifying which elements of your model class are accessible to other classes, helping to encapsulate your objects.
- Public — accessible by all other objects. The public methods and variables form the API of the class.
- Private — only accessible to this object. Not even available to subclasses.
- Protected — accessible to this object and its subclasses. Not available to any other objects.
Member variables should always be set to private or protected. Access to them from outside the class should be via public getters and setters, as demonstrated in the above code snippet. This avoids hard-to-trace side-effects and tightly-coupled code.
If you made a subclass of the model above and wanted your subclass to have access to mReminderArrayList
, you would change its access modifier to protected. This grants access to that variable in your subclass, while allowing you to further customize or work with that variable.
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:
- In the fragment, you declare a member variable to store a custom object that implements
OnItemInListSelectedListener
and you name itmCallback
. - Next, you create the interface and declare the required methods — your interface can have many methods.
- In
onAttach()
, a fragment lifecycle method, you check if the activity your fragment is attached to conforms toOnItemInListSelectedListener
. If not, then it can’t serve your purposes and you have a problem. TheClassCastException
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.