The three core components of applications: activates, services, and broadcast receivers, are all started through intents.
Intents can be used as a messaging system to activate components in the same application, or to start other applications.
Intents can be implicit or explicit:
- Implicit intent:
- Request for a service
- Any appropriate app on the device can fulfill the request
- If multiple activities are available (and none set as the default), the system will ask the user to pick one
- Explicit intent:
- Start a specific named
Activity
- Start a specific named
Intents can return values to the activity which started it.
Some common intents: alarm clock, calendar, camera, contacts, app, email, file storage, maps, music, phone, search, settings.
TODO
startActivity
startActivityForResult
startService
bindService
Android manifest:
-
Describes components:
- Activities
- Services
- Broadcast receivers
- Content providers
- Intent messages each component can handle TODO
-
Action categories:
- TODO
- e.g.
ACTION_CALL,ACTION_SEARCH
-
Broadcast actions:
- Notification of some event
- e.g.
ACTION_TIMEZONE_CHANGED,ACTION_POWER_CREATED
Context.startActivity()
Activity.startActivityForResult() // expect a return value
Activity.setResult() // Set the return value to give to activtity that started the activity
Context.startService()
Context.bindService()
TODO
Intent object properties:
- Component name
- Fully-qualified class name
- Set using
setComponent,setClass, orsetClassName - Optional (implicit intent)
- Action (e.g.
ACTION_CALL)- Required for implicit intents
- Many actions defined in the
Intentsclass - Other actions defined through the API (e.g.
MediaStoreclass hasACTION_IMAGE_CAPTURE) - Can define your own intent action names to allow other applications to call into your app
- Set using
setAction
- Category (of action)
- TODO
- Data
- URI (uniform resource identifier) of data
- Can set MIME type of the data/content
- Type (of data)
- Extras (Bundle with more data)
- Key-value dictionary
- e.g.
ACTION_TIMEZONE_CHANGEDhas an extra with the keytime-zone
- Flags
- TODO
If your multi-screen app is a multiple-activity app, navigation can be achieved by using explicit intents TODO.
Navigation Architecture:
- XML:
- Navigation graph
- Destinations are pages of your apps
- Actions are edges between the nodes, denoting logical relationship between destinations
- Nested graphs are possible
NavHost- Empty container in layout which holds a destination
- Destinations are swapped in and out
- Default implementation:
NavHostFragment
- Deep links
- Navigation graph
- Compose
NavHostcomposable defines set of routesNavControllerholds current navigation state, allows programmatic navigation to different routes based on events- Argument placeholders allow parameters to be passed to screens
- Navigation with Fragments and Compose:
androidx.compose.ui.platform.ComposeViewcan be added to any XML layout- The view element implements
setContent, allowing you to add Composable functionsbinding.composeView.setContent { ... }
- Allows you to use ML for navigation but do everything else using Compose
Recycler List Views
Generating views is expensive: list views only render the items that will be visible on the screen.
A recycler view is used to manage some backing data set and to re-use existing views as rows become hidden and visible.
data DataItem(val id: Int, val text: String)
class MainActivity: AppCompatActivity(), OnDataItemListener {
private val data = arrayOf<DataItem>(
DataItem(1, "Hello"),
DataItem(2, "World"),
DataItem(3, "Cat")
)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val dataAdapter = DataAdapter(data)
val recyclerView: RecyclerView = findViewById(R.id.recycler_view)
recyclerView.adapter = dataAdapter
}
fun onItemClick(position: Int) {
val intent = Intent(Intent.ACTION_WEB_SEARCH).apply {
putExtra(SearchManager.QUERY, data[position])
}
if (intent.resolveActivity(packageManager) != null) {
startActivity(intent)
}
}
}
class DataAdapter(private val data: Array<DataItem>,
private val listener: OnDataItemListener) :
RecyclerView.Adapter<DataAdapter.DataItemViewHolder>() {
// Make the class a subclass of RecyclerView, and call the superclass's
// initializer with the `itemView` as the argument.
// Also pass through the listener to each list item view holder
private class DataItemViewHolder(itemView: View,
private val listener: OnDataItemListener) :
RecyclerView.ViewHolder(itemView) {
val textView: TextView
init {
textView = itemView.findViewById("data_text")
}
override fun onClick(view: View?) {
listener.onItemClick(adapterPosition)
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DataItemViewHolder {
// parent context required as views need an owner
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.list_item, parent, attachToRoot: false)
// list_item.xml is a XML layout resource file for the list item
return DataItemViewHolder(view, listener);
}
// Set view item content depending on element index
override fun onBindViewHolder(viewHolder: DataItemViewHolder, position: Int) {
viewHolder.textView = data[position].text;
}
override fun getItemCount() = data.size
interface OnDataItemListener {
fun onItemClick(position: Int)
}
}
<!-- activity_main.xml -->
...
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
app:layoutManager=:LinearLayoutManager"
/>
<!-- LinearLayoutManager = list view. grid view managers also available -->
...
<!-- list_item.xml -->
...
<TextView
android:id="@+id/data_text"
android:text="Default Text"
/>
...