This document summarizes a session on activities and intents from the Android Academy. It covered:
1. Three ways to set listeners on buttons - declaring in XML, inline anonymous classes, and implementing an interface. The observer pattern was used for listeners.
2. Making toasts using the static factory method pattern. Toasts provide simple feedback to the user.
3. Using intents to navigate between activities both explicitly and implicitly. Intents can pass optional extras between activities.
4. Storing persistent data with SharedPreferences which can save data between sessions.
5. Broadcasting messages between loosely coupled components with broadcast receivers. Security concerns with broadcasts were also discussed.
16. I ❤ Today’s Lecture!
In this lecture, besides the Android stuff,
we will show-case 2 design patterns:
Hidden Agenda for Today
The Observer Pattern
The Static Factory Method Pattern
17. Design Patterns will be in Purple slides.
Design patterns will get this background.
If you don’t know design patterns,
it’s never too late to learn.
21. Nested Classes
In Java, a class (or interface) can be declared inside another class.
class A{
class B{
// ...
}
}
Read more: https://docs.oracle.com/javase/tutorial/java/javaOO/nested.html
Inside C, you can use A.B:
class C{
A.B abMember;
A.B doSomething(){ /* … */ }
}
22. Nested Classes
There are 2 types of nested classes:
class A{
// This one is called a static nested class.
static class B{
// ...
}
// This one is called an inner class - because there’s no static.
class B{
// ...
}
}
Read more: https://docs.oracle.com/javase/tutorial/java/javaOO/innerclasses.html
23. Nested Classes - a Map
Read more: https://docs.oracle.com/javase/tutorial/java/javaOO/anonymousclasses.html
Nested Class
Static Nested
Class
Inner Class
Local class
Anonymous
Class
Examples:
ViewHolder
AsyncTask
Examples:
Runnable
OnClick
25. Our Starting Point
in activity_main.xml:
<Button
android:text="Hooking to buttons"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="goToButtonsDemo"
/>
In MainActivity.java:
public void goToButtonsDemo(View view) {
Intent i = new Intent(this, ButtonsDemoActivity.class);
startActivity(i);
}
27. Way #1: android:onClick=”...” in xml
In activity_buttons_demo.xml declare the button with an OnClick:
<Button
android:text="Red +"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:onClick="increaseRed"
android:background="@color/lightred"
/>
In ButtonsDemoActivity.java, implement the method that will be called
public void increaseRed(View view) {
// ...
}
28. Way #2: Inline OnClickListener
In the Activity’s onCreate method, grab the buttons and set their
OnClickListener with an inline implementation
@Override
protected void onCreate(Bundle savedInstanceState) {
// ...
Button increaseGreen = (Button)findViewById(R.id.buttonsdemo_incGreen);
increaseGreen.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// ...
}
});
}
29. Way #3: Single OnClickListener
(1) Make the activity implement View.OnClickListener:
public class ButtonsDemoActivity extends ActionBarActivity
implements View.OnClickListener
(2) In OnCreate, set the button’s listener to the activity (with this):
@Override
protected void onCreate(Bundle savedInstanceState) {
// …
Button decreaseRed = (Button)findViewById(R.id.buttonsdemo_decRed);
decreaseRed.setOnClickListener(this);
}
30. Way #3: Single OnClickListener
(3) Implement the interface, use a switch to tell which button view
was clicked:
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.buttonsdemo_decRed:
red = calcNewValue(red, -1);
break;
case R.id.buttonsdemo_decGreen:
green = calcNewValue(green, -1);
break;
case R.id.buttonsdemo_decBlue:
blue = calcNewValue(blue, -1);
break;
}
refreshDisplay();
31. Demo: The Color Mixer
1 Button: Way #1
2 Buttons: Way #2
3 Buttons: Way #3
32. Not really 3 ways...
In fact, there’s only one way to have a button do something:
Set a View.OnClickListener.
Way #1 does this implicitly,
and ways 2,3 does this explicitly.
33. What is a listener?
Listener: an interface that contains a single callback method.
When the user interacts with the UI, the Views trigger a call to the
listener (if it’s not null) and calls the callback.
As long as the view and the listener agrees on the contract between
them (~ The method’s signature), the view doesn’t care what the
implementation is.
This is a great example of the Observer design pattern.
Source: http://developer.android.com/guide/topics/ui/ui-events.html#EventListeners
34. The Observer Design Pattern
A Behavioral design pattern, commonly used in UI (but not only).
Read a lot more: https://en.wikibooks.org/wiki/Computer_Science_Design_Patterns/Observer
View.OnClickListener
View
Your Listener
35. How does xml onClick (way #1) work
When you use the xml’s onClick way,
the View uses a View.DeclaredOnClickListener,
It uses reflection (which is slow on android),
but It’s Lazy and Cached,
and is not validated at Compile-Time.
Since android is Open-Source, check out the implementation at the
link below.
https://github.com/android/platform_frameworks_base/blob/master/core/java/android/view/View.java Line
4429
36. Which is better?
Way #1: onClick=”...” → DeclaredOnClickListener
Way #2: inline listener implementation
Way #3: activity interface implementation
http://stackoverflow.com/questions/21319996/android-onclick-in-xml-vs-onclicklistener
Pros: Clean code, no findViewById at all
Cons: Reflection, No Compile-Time validation, API Level ≥ 4, not readable
Pros: Readable code
Cons: +1 class, +1 method, ~500 bytes, +Boilerplate code, if you have a lot of clicks
becomes mess
Pros: No object allocations
Cons: Felix: The horrible switch (over view.getId())
37. What other listeners are available?
Read more: http://developer.android.com/guide/topics/ui/ui-events.html
Event Handler
onClick View.OnClickListener
onLongClick View.OnLongClickListener
onFocusChange
View.OnFocusChangeListener
onKey View.OnKeyListener
onTouch View.OnTouchListener
onCreateContextMenu View.OnCreateContextMenuListener
AdapterView.OnItemClickListener
38. Non-void Listeners
Some flows requires listeners to tell
when they take care of the event.
These listeners return a boolean:
true if the event is consumed,
or false if it isn’t.
onTouchListener is a good example.
39. OnClick || OnTouch?
OnTouch gets called on any change in the touch detection,
and contains touch data. The MotionEvent is passed to the root view,
and it travels until it finds an OnTouchListener that would handle it.
Some may react to the event without consuming it - common practice
for when using Gesture Detectors.
For “Everyday tapping” - You usually should go with onClick.
Read More: http://codetheory.in/understanding-android-input-touch-events/
40. Take Aways - Listeners
1.UI Interaction is implemented with Listeners.
2.Some listeners returns a boolean value to indicate handling.
3.OnTouch is really important,
and in most cases, you won’t need it.
42. Toasts
provides simple feedback about an
operation in a small popup.
The current activity remains visible and
interactive - But the toast itself is not
interactive.
Toast disappear after a short time.
http://developer.android.com/guide/topics/ui/notifiers/toasts.html
45. Static Factory Method
●The best ways to create an object.
●Have a name
●Can cache and not always create new class
●They return an object
●Reduce the verbosity of creating parameterized type instances
Read more: http://www.informit.com/articles/article.aspx?p=1216151
46. Static Factory Method
public static Toast makeText(Context context, CharSequence text, @Duration int duration) {
Toast result = new Toast(context);
LayoutInflater inflate = (LayoutInflater)
context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View v = inflate.inflate(com.android.internal.R.layout.transient_notification, null);
TextView tv = (TextView)v.findViewById(com.android.internal.R.id.message);
tv.setText(text);
result.mNextView = v;
result.mDuration = duration;
return result;
}
Read more:
48. Not in course: Snackbars
Defined in the Material Design spec, SnackBars also allow user
interaction - and also work great with FABs -
Both are out-of-scope from our course.
http://developer.android.com/reference/android/support/design/widget/Snackbar.html
49. If you need user interaction, you can use a notification (which we’ll
see in Session #6)
and if you really need a custom toast design,
you can - but it’s not covered here.
Also not talking about...
50. - It lets newly created objects understand what has been going on.
- Global information about an application environment
- Creating New objects: Creating new views, adapters, listeners:
TextView tv = new TextView(getContext());
ListAdapter adapter = new SimpleCursorAdapter(getApplicationContext(), ...);
- Accessing Standard Common Resources: Services like
LAYOUT_INFLATER_SERVICE, SharedPreferences:
context.getSystemService(LAYOUT_INFLATER_SERVICE);
getApplicationContext().getSharedPreferences(*name*, *mode*);
- Accessing Components Implicitly: Regarding content providers, broadcasts,
intent: getApplicationContext().getContentResolver().query(uri, ...);
Context - #1 reason of memory leaks
- Further reads: https://possiblemobile.com/2013/06/context/
54. and also a bit about Navigation
Intents and StartActivity
C
55. Intents
messaging object you can to request an
action from another app component.
Main Use-cases:
- Starting an activity
- Broadcasting a message
- Starting a service (wait for Session 6)
http://developer.android.com/guide/components/intents-filters.html
56. Intent Types
Explicit Intent - have a ComponentName -
so Android knows exactly what to call.
Implicit Intent, doesn’t have a
ComponentName - so Android uses Intent
Filters to know what to do.
57. used for Explicit Intents
used for Implicit Intents
used to tell things to the recipient
used to tell things to the messenger
Intents have...
Component Name
Action
Data (and Type)
Category
Extras
Flags
58. Intent Filters
Intent filters are used to advertise what
intents your app can handle.
We’ve already seen MainActivity’s
intent filter in Session #1.
Read more: http://developer.android.com/guide/components/intents-filters.html#Receiving
59. Explicitly
Intent intent = new Intent(this, TargetActivity.class);
startActivity(intent);
or
Intent intent = new Intent(getApplicationContext(), TargetActivity.class);
getApplicationContext().startActivity(intent);
60. Implicitly
Intent intent = new Intent(android.content.Intent.ACTION_VIEW,
Uri.parse("geo:37.7749,-122.4194"));
startActivity(intent);
61. But what if?
public class MainActivity extends AppCompatActivity {
public static final String TARGET_ACTIVITY_DATA_KEY = "KEY1";
public static final String TARGET_ACTIVITY_MORE_DATA_KEY = "KEY2";
public static final String TARGET_ACTIVITY_EVEN_MORE_DATA_KEY = "KEY3";
@Override
protected void onCreate(Bundle savedInstanceState) {
Intent intent = new Intent(this, TargetActivity.class);
intent.putExtra(TARGET_ACTIVITY_DATA_KEY,"Luke Skywalker");
intent.putExtra(TARGET_ACTIVITY_MORE_DATA_KEY,"Darth Vader");
intent.putExtra(TARGET_ACTIVITY_EVEN_MORE_DATA_KEY,"Han Solo");
startActivity(intent);
}
62. But what if?
public class TargetActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_target);
Bundle extras = getIntent().getExtras();
String first = (String) extras.get(MainActivity.TARGET_ACTIVITY_DATA_KEY);
String second = extras.getString(MainActivity.TARGET_ACTIVITY_MORE_DATA_KEY);
int error = extras.getInt(MainActivity.TARGET_ACTIVITY_EVEN_MORE_DATA_KEY);
initViews(first,second,error);
}
63. What wrong?
- Non readable
- Mix of static variables
- Other activities should know what target expect
- Run-time error
65. Solution
public class TargetActivity extends AppCompatActivity {
public static final String TARGET_ACTIVITY_DATA_KEY = "KEY1";
public static final String TARGET_ACTIVITY_MORE_DATA_KEY = "KEY2";
public static final String TARGET_ACTIVITY_EVEN_MORE_DATA_KEY = "KEY3";
public static Intent createIntent(Context context,String first,String second,
String third){
Intent intent = new Intent(context, TargetActivity.class);
intent.putExtra(TARGET_ACTIVITY_DATA_KEY,first);
intent.putExtra(TARGET_ACTIVITY_MORE_DATA_KEY,second);
intent.putExtra(TARGET_ACTIVITY_EVEN_MORE_DATA_KEY,third);
return intent;
}
67. SharedPreferences
- Great thing to store persistent various.
- Not accessible by other apps*
- Used to save things between sessions
- Sometimes could be great communication tool (like intent) using
“onSharedPreferenceChangeListener”
68. MainActivity
public static final String TEXT_SHARED_PREF_KEY = "TEXT_SHARED_PREF_KEY";
public static final String SHARED_PREF_KEY = "KEY_FOR_SHARED_PREF";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button saveBtn = (Button) findViewById(R.id.btn_am_save_data);
Button restoreBtn = (Button) findViewById(R.id.btn_am_restore_data);
saveBtn.setOnClickListener(this);
restoreBtn.setOnClickListener(this);
}
72. Broadcast Receivers
Great guy :)
- Loose Coupling
- 1-to-n relationship
- The onReceive() method is always executed on the main thread
- You can notify components in your entire application, so the
communicating components do not have to "see" each other.
73.
74. But...
- Marshaling data via intent really hard
- Register/Unregister it when you do not needed him (BaseActivity)
- Not build to transfer large objects
75. Receiver
public class ResponseReceiver extends BroadcastReceiver {
public static final String ACTION_RESP =
"com.example.intent.action.PROGRESS_DOWNLOAD";
@Override
public void onReceive(Context context, Intent intent) {
TextView result = (TextView) findViewById(R.id.tv_am_progress);
String text = intent.getStringExtra(DownloadService.PROGRESS);
result.setText(text + "%");
}
}
76. Receiver
@Override
public void onClick(View v) {
Intent intent = new Intent(this, DownloadService.class);
startService(intent);
IntentFilter filter = new IntentFilter(ResponseReceiver.ACTION_RESP);
filter.addCategory(Intent.CATEGORY_DEFAULT);
receiver = new ResponseReceiver();
registerReceiver(receiver, filter);
}
@Override
protected void onStop() {
unregisterReceiver(receiver);
super.onStop();
}