- Hands-On Android UI Development
- Jason Morris
- 1101字
- 2021-07-02 23:26:09
Listening for some events
When listening for user-interface events in Android, you'll typically hook up a listener object of some sort to the widgets you want to receive events on. However, how the listener object is defined may follow a number of different patterns, and listeners can take a number of different forms. You'll often see a simple anonymous class being defined as the listener, which is something like this:
closeButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
finish();
}
});
However, while this pattern is common (especially because the much shorter lambda syntax was only introduced in Java 8, and Android didn't properly support it until 2017), it's not always your best choice for several reasons:
- This anonymous class is not reusable at all. It serves one purpose, for a single object in the entire application.
- You just allocated a new object that will also need to be garbage collected. This is not a big deal, but can sometimes be avoided or minimized by grouping listeners into classes that handle multiple related events.
- Any local variables from onCreate that are captured by the anonymous inner class must have references copied over into the new class as fields. You don't see this happen, but the compiler does it automatically (it's why the fields must be final).
If Java 8 is available on your project, you can, of course, use lambdas, and shorten the syntax. However, this still results in an anonymous inner class being created. Another pattern for listening for events is to have the class that contains the layout (typically an Activity or Fragment) implement the listener interfaces, and use a switch statement to handle events from different widgets:
public class MyListenerActivity extends Activity implements View.OnClickListener {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.eventful_layout);
findViewById(R.id.open).setOnClickListener(this);
findViewById(R.id.find).setOnClickListener(this);
findViewById(R.id.close).setOnClickListener(this);
}
// ...
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.open:
onOpen();
break;
case R.id.find:
onFind();
break;
case R.id.close:
onClose();
break;
}
}
}
This has two advantages: there are no new listener objects, and all the layout and event logic is now encapsulated within the Activity class. The switch statement carries a tiny overhead, but as the layout increases in size, it becomes a lot of boilerplate to maintain and somewhat encourages you to place simple event code directly into the onClick method, instead of always just dispatching to another method. This simple event code almost always leads to more complex event code, and eventually to a horrible mess in your code base.
So, what is the right way to handle events? The answer is that there isn't one, but when deciding how to handle events, you should always consider how you will reuse the event handler code--don't repeat yourself. For the date selection widget from the last chapter, the expectation is that when the user taps on the date, they will see a calendar dialog open, allowing them to choose a new date. This will need an event handler, and such a handler should be reusable since you may want it elsewhere, so follow these steps to build the date-picker event listener:
- Right-click on your default package (that is, com.packtpub.claim) and select New| Java Class:

- Name the new class ui.DatePickerWrapper; Android Studio will create a new package named ui automatically and place DatePickerWrapper inside it.
- In the Interfaces list, add the following listener interfaces (use commas "," to separate the interfaces):
- android.view.View.OnClickListener: To receive an event when the user taps on the date picker
- android.view.View.OnFocusChangeListener: To receive an event if the date picker receives keyboard focus; this is important to handle if the user chooses to navigate the form using the "next" button on the keyboard, or has a physical keyboard attached to their device
- android.app.DatePickerDialog.OnDateSetListener: To receive an event when the user selects a new date from DatePickerDialog:

- Click OK to create the new package and class.
- If Android Studio has not created the skeleton methods for the listeners, select class name as DatePickerWrapper in the source, and use the code assistant to implement the methods:

- Now you'll need a way to format the date string for the user, and it should be localized, so declare a java.text.DateFormat for this purpose:
private final DateFormat dateFormat = DateFormat.getDateInstance(DateFormat.LONG);
- This class is a wrapper, and will also need some fields to keep track of what it is wrapping, namely, the TextView, where it will display the date to the user (and where the user can tap to open the date picker dialog), an instance of DatePickerDialog to display to the user, and the currently selected/displayed Date:
private final TextView display;
private DatePickerDialog dialog = null;
private Date currentDate = null;
- Then, we need a simple constructor that will capture TextView for display, and set it up as a date display and configure the events:
public DatePickerWrapper(final TextView display) {
this.display = display;
this.display.setFocusable(true);
this.display.setClickable(true);
this.display.setOnClickListener(this);
this.display.setOnFocusChangeListener(this);
this.setDate(new Date());
}
- Now, we'll need getter and setter-like methods to change and retrieve the state of the date picker:
public void setDate(final Date date) {
if(date == null) {
throw new IllegalArgumentException("date may not be null");
}
this.currentDate = (Date) date.clone();
this.display.setText(dateFormat.format(currentDate));
if(this.dialog != null) {
final GregorianCalendar calendar = new GregorianCalendar();
calendar.setTime(currentDate);
this.dialog.updateDate(
calendar.get(Calendar.YEAR),
calendar.get(Calendar.MONTH),
calendar.get(Calendar.DAY_OF_MONTH)
);
}
}
public Date getDate() {
return currentDate;
}
- Before we can actually handle the event, we need a method to display DatePickerDialog that will allow the user to change the date:
void openDatePickerDialog() {
if (dialog == null) {
final GregorianCalendar calendar = new GregorianCalendar();
calendar.setTime(getDate());
dialog = new DatePickerDialog(
display.getContext(),
this,
calendar.get(Calendar.YEAR),
calendar.get(Calendar.MONTH),
calendar.get(Calendar.DAY_OF_MONTH)
);
}
dialog.show();
}
- Then, we need to complete the event listener methods so that when the user selects the displayed date, we open the DatePickerDialog, allowing them to change the selected date:
@Override
public void onClick(final View v) {
openDatePickerDialog();
}
@Override
public void onFocusChange(final View v, final boolean hasFocus) {
if (hasFocus) {
openDatePickerDialog();
}
}
- Finally, we need to handle the event that comes back from the DatePickerDialog, indicating that the user has chosen a date:
@Override
public void onDateSet(
final DatePicker view,
final int year,
final int month,
final int dayOfMonth) {
final Calendar calendar = new GregorianCalendar(
year, month, dayOfMonth
);
setDate(calendar.getTime());
}
Now you have a class that can turn any TextView object into a space where the user can select a date via the standard DatePickerDialog. This is an ideal example of a good place to encapsulate events; you actually have three different event handlers that perform a related group of actions, and maintain user interface state in a single class that can be reused throughout your application.
- FuelPHP Application Development Blueprints
- 零基礎學Visual C++第3版
- Fundamentals of Linux
- 大學計算機基礎實驗教程
- Docker進階與實戰
- 數據庫系統原理及MySQL應用教程
- Mastering PHP Design Patterns
- Data Analysis with IBM SPSS Statistics
- Mastering Yii
- 概率成形編碼調制技術理論及應用
- 基于Struts、Hibernate、Spring架構的Web應用開發
- 算法設計與分析:基于C++編程語言的描述
- R語言:邁向大數據之路
- Analytics for the Internet of Things(IoT)
- VC++ 2008專題應用程序開發實例精講