In this next lesson:
- invoking Activity using implicit invocation and Intent Filter
Translated by Taras Leskiv (http://android-by-example.blogspot.com/)
Last lessons were overloaded with theory. This theory must be understood well for you not to have problems in practice. These topics are the basis - Task, Lifecycle, Intent. If you don’t understand something completely, you can always open the materials and read it again. Further we will implement examples that will confirm this theory and everything will become more clear.
In previous lessons we have learnt to invoke an Activity using Intent and explicitly defining the class. We are also aware that there is another way of Activity invocation - implicit. It is based on the point that Activity is invoked not by its name but by its functionality. That is, when we want to complete specific actions, we create and configure the corresponding Intent and sent it to look for those Activities that can solve our problem.
Let’s have a look how is it implemented in practice. We will create the application that will display the current time and date. We will implement it using three Activities:
- the first one will contain two buttons: Show time and Show date
- the second will display the time
- the third will display the date
Clicking the Show time button will invoke the second Activity and pressing Show date - the third Activity. But we will implement this not by explicitly defining Activity classes in the Intent but using the Intent Filter.
Let’s create a project:
Project name: P0261_IntentFilter
Build Target: Android 2.3.3
Application name: IntentFilter
Package name: ru.startandroid.develop.p0261intentfilter
Create Activity: MainActivity
Open main.xml and draw two buttons::
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="horizontal"> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/btnTime" android:text="Show time"> </Button> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/btnDate" android:text="Show date"> </Button> </LinearLayout>
MainActivity.java implementation:
package ru.startandroid.develop.p0261intentfilter; import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; public class MainActivity extends Activity implements OnClickListener { /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); Button btnTime = (Button) findViewById(R.id.btnTime); Button btnDate = (Button) findViewById(R.id.btnDate); btnTime.setOnClickListener(this); btnDate.setOnClickListener(this); } @Override public void onClick(View v) { Intent intent; switch(v.getId()) { case R.id.btnTime: intent = new Intent("ru.startandroid.intent.action.showtime"); startActivity(intent); break; case R.id.btnDate: intent = new Intent("ru.startandroid.intent.action.showdate"); startActivity(intent); break; } } }
In the code we have defined the buttons and set them Activity as a click listener. Inside onClick method we define which button has been clicked and create an Intent.
For creating an intent we use a constructor: Intent(String action). That is when creating an Intent we specify its attribute which is called action. It is a usual String constant. Action usually specifies an action that we want to perform. For example, there such system action-constants: ACTION_VIEW - view, ACTION_EDIT - edit, ACTION_PICK - selection from the list, ACTION_DIAL - make a call.
If the action is performed on some data, then one more Intent-attribute is specified - data. Inside it we can specify any object we need: user in the address book, map coordinates, phone number etc. That is action specifies what to do and data - with what to do it.
We will talk about data in the next lessons, we will use only action for now. I’ve already enumerated some system action-constants, but we can use our own action.
As you can see from the code, I came up with these actions:
ru.startandroid.intent.action.showtime
ru.startandroid.intent.action.showdate
The first one means that I want to invoke an Activity that will show me the current time. Second one - Activity with the current date.
You have to clearly understand the following thing: action is just a text. I could come up with action like abcdefg123456. But showtime text represents what I want to accomplish, it is more obvious and understandable. And I am using ru.startandroid.intent.action prefix to avoid collisions. There may be an application installed that already uses showtime action - I don’t want to intercept with it. That’s why my action is ru.startandroid.intent.action.showtime.
So we have created an Intent with action and sent it to the system to look for Activities. For Activity to fit for our intent it must contain an action attribute with the same value that action in the Intent. It means we need to create two Activities, configure their Intent Filters and implement displaying date and time.
Activity is created as usual - create ActivityTime class which is the subclass of android.app.Activity and register it in manifest as Activity. After it is registered in the manifest, we have to create an Intent Filter in the same place. To do so, select ActivityTime, click Add, choose Intent Filter and click OK.
After this analogically create Action inside Intent Filter and write ru.startandroid.intent.action.showtime inside the Name field.
We also have to create Category inside the Intent Filter and in the name field choose android.intent.category.DEFAULT from the list. I will not explain now why is it needed. But without this startActivity(Intent) invocation will not find an Activity.
Create the layout for new Activity and name it time.xml:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/tvTime" android:text="TextView" android:layout_gravity="center_horizontal" android:layout_marginTop="20dp" android:textSize="30sp"> </TextView> </LinearLayout>
Write code inside ActivityTime.java:
package ru.startandroid.develop.p0261intentfilter; import java.sql.Date; import java.text.SimpleDateFormat; import android.app.Activity; import android.os.Bundle; import android.widget.TextView; public class ActivityTime extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.time); SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss"); String time = sdf.format(new Date(System.currentTimeMillis())); TextView tvTime = (TextView) findViewById(R.id.tvTime); tvTime.setText(time); } }
Everything is simple here - calculate the current time and display it in the TextView.
Save everything and run the application.
Press Show time button:
time has been displayed. That is Intent with action = ru.startandroid.intent.action.showtime found and displayed Activity, which has action which is equal to ru.startandroid.intent.action.showtime inside its Intent Filter.
Let’s get back (Back button) and now press Show date button. The application will generate an error as it couldn’t find an Activity which corresponds to Intent with action = ru.startandroid.intent.action.showdate (we have created only for showtime).
Let’s create such an Activity and name it ActivityDate. Actions are all the same as when creating ActivityTime:
- class creation
- creating Activity in the manifest and creating an Intent Filter for it (with action = ru.startandroid.intent.action.showdate and category = android.intent.category.DEFAULT)
Name layout-file date.xml:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/tvDate" android:text="TextView" android:layout_gravity="center_horizontal" android:layout_marginTop="20dp" android:textSize="30sp"> </TextView> </LinearLayout>
ActivityDate.java code:
package ru.startandroid.develop.p0261intentfilter; import java.sql.Date; import java.text.SimpleDateFormat; import android.app.Activity; import android.os.Bundle; import android.widget.TextView; public class ActivityDate extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.date); SimpleDateFormat sdf = new SimpleDateFormat("dd.MM.yyyy"); String date = sdf.format(new Date(System.currentTimeMillis())); TextView tvDate = (TextView) findViewById(R.id.tvDate); tvDate.setText(date); } }
Save everything and run the application, press Show date and you will see date displayed. That means that Intent with action = ru.startandroid.intent.action.showdate found and displayed ActivityDate which fits it by Intent Filter.
To make knowledge solid, let’s do something else. If you remember, in the Lesson №22 about Intent Filter, I wrote that one Intent can find several appropriate Activities. In this case the user will be given a choice which Activity to use. Let’s implement this case. We will make one more Activity, which will react on Intent with action = ru.startandroid.intent.action.showdate. And it will display the current date analogically to ActivityDate. But the display format will be slightly different.
Let’s create such an Activity and name it ActivityDateEx. Actions are the same like when creating ActivityDate:
- class creation
- Activity creation in manifest and creating the Intent Filter for it (with action = ru.startandroid.intent.action.showdate and category = android.intent.category.DEFAULT)
We will not create a new layout-file and use already existing date.xml. Actually all three Activities could have used the same layout as they are absolutely identical - one TextView.
ActivityDateEx.java code:
package ru.startandroid.develop.p0261intentfilter; import java.sql.Date; import java.text.SimpleDateFormat; import android.app.Activity; import android.os.Bundle; import android.widget.TextView; public class ActivityDateEx extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.date); SimpleDateFormat sdf = new SimpleDateFormat("EEE, MMM d, yyyy"); String date = sdf.format(new Date(System.currentTimeMillis())); TextView tvDate = (TextView) findViewById(R.id.tvDate); tvDate.setText(date); } }
As you can see, the difference from ActivityDate is only date format. Save everything and run the application. Press Show date and you will see the following choice:
Intent found two Activities, but showed the name of the parent application and package for each of them. In our case - both Activities are from our application, that’s why the text is the same and we can’t distinguish which is which. Let’s fix it by providing proper names.
Press Back to close the selection dialog. Go to manifest and change the label for Activity:
Date basic for ActivityDate
Date extended for ActivityDateEx
Save everything and run the application. Press Show date and you will see the following choice:
This is significantly better. Press Date extended and see the date in the extended format in ActivityDateEx.
So we sent Intent with action. This Intent found Activity with proper Intent Filter and displayed it. If it found several Activities - it gave a choice. These examples are a great explanation of the mechanism.
If you got lost what to create and where, here is the project structure and manifest contents.
manifest contents (AndroidManifest.xml tab):
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="ru.startandroid.develop.p0261intentfilter" android:versionCode="1" android:versionName="1.0"> <uses-sdk android:minSdkVersion="10"></uses-sdk> <application android:icon="@drawable/icon" android:label="@string/app_name"> <activity android:name=".MainActivity" android:label="@string/app_name"> <intent-filter> <action android:name="android.intent.action.MAIN"></action> <category android:name="android.intent.category.LAUNCHER"></category> </intent-filter> </activity> <activity android:name="ActivityTime"> <intent-filter> <action android:name="ru.startandroid.intent.action.showtime"></action> <category android:name="android.intent.category.DEFAULT"></category> </intent-filter> </activity> <activity android:name="ActivityDate" android:label="Date basic"> <intent-filter> <action android:name="ru.startandroid.intent.action.showdate"></action> <category android:name="android.intent.category.DEFAULT"></category> </intent-filter> </activity> <activity android:name="ActivityDateEx" android:label="Date extended"> <intent-filter> <action android:name="ru.startandroid.intent.action.showdate"></action> <category android:name="android.intent.category.DEFAULT"></category> </intent-filter> </activity> </application> </manifest>
In the next lesson:
- reading action from Intent
Присоединяйтесь к нам в Telegram:
- в канале StartAndroid публикуются ссылки на новые статьи с сайта startandroid.ru и интересные материалы с хабра, medium.com и т.п.
- в чатах решаем возникающие вопросы и проблемы по различным темам: Android, Compose, Kotlin, RxJava, Dagger, Тестирование, Performance
- ну и если просто хочется поговорить с коллегами по разработке, то есть чат Флудильня