Android Development
Android Development
1. Introduction
Therefore, it is developed by Google and later the OHA (Open Handset Alliance) which means a consortium of
84 companies such as google, samsung, AKM, synaptics, KDDI, Garmin, Teleca, Ebay, Intel etc. It was
established on 5th November, 2007, led by Google. It is committed to advance open standards, provide services
and deploy handsets using the Android Plateform. Java language is mainly used to write the android code even
though other languages can be used.
The first beta version of the Android Software Development Kit (SDK) was released by Google in 2007 where as
the first commercial version, Android 1.0, was released in September 2008.
On June 27, 2012, at the Google I/O conference, Google announced the next Android version, 4.1 Jelly Bean.
Jelly Bean is an incremental update, with the primary aim of improving the user interface, both in terms of
functionality and performance.
1. It is open-source.
3. There are a lot of mobile applications that can be chosen by the consumer.
4. It provides many interesting features like weather details, opening screen, live RSS (Really Simple
Syndication) feeds etc.
It provides support for messaging services(SMS and MMS), web browser, storage (SQLite), connectivity (GSM,
CDMA, Blue Tooth, Wi-Fi etc.), media, handset layout etc.
Using a Linux kernel allows Android to take advantage of keys security features and allows device
manufacturers to develop hardware drivers for a well-known kernel.
A summary of some key core Android libraries available to the Android developer is as follows −
l android.app − Provides access to the application model and is the cornerstone of all Android
applications.
l android.content − Facilitates content access, publishing and messaging between applications and
application components.
l android.database − Used to access data published by content providers and includes SQLite database
management classes.
l android.os − Provides applications with access to standard operating system services including
messages, system services and inter-process communication.
Having covered the Java-based core libraries in the Android runtime, it is now time to turn our attention to the
C/C++ based libraries contained in this layer of the Android software stack.
The Dalvik VM makes use of Linux core features like memory management and multi-threading, which is
intrinsic in the Java language. The Dalvik VM enables every Android application to run in its own process, with
its own instance of the Dalvik virtual machine.
The Android runtime also provides a set of core libraries which enable Android application developers to write
Android applications using standard Java programming language.
For devices running Android version 5.0 (API level 21) or higher, each app runs in its own process and with its
own instance of the Android Runtime(ART). ART is written to run multiple virtual machines on low-memory
devices by executing DEX files, a bytecode format designed specially for Android that's optimized for minimal
memory footprint. Build toolchains, such as Jack, compile Java sources into DEX bytecode, which can run on
the Android platform.
Android also includes a set of core runtime libraries that provide most of the functionality of the Java
programming language, including some Java 8 language feature, that the Java API framework uses.
If you are developing an app that requires C or C++ code, you can use the Android NDK to access some of these
native platform libraries directly from your native code.
l A rich and extensible View System you can use to build an app’s UI, including lists, grids, text boxes,
buttons, and even an embeddable web browser
l A Resource Manager , providing access to non-code resources such as localized strings, graphics, and
layout files
l A Notfication Manager that enables all apps to display custom alerts in the status bar
l An Activity Manager that manages the lifecycle of apps and provides a common navigation back stack
l Content Provider that enable apps to access data from other apps, such as the Contacts app, or to share
their own data
Developers have full access to the same framework APIs that Android system apps use.
v. System Apps
Android comes with a set of core apps for email, SMS messaging, calendars, internet browsing, contacts, and
more. Apps included with the platform have no special status among the apps the user chooses to install. So a
third-party app can become the user's default web browser, SMS messenger, or even the default keyboard (some
exceptions apply, such as the system's Settings app).
The system apps function both as apps for users and to provide key capabilities that developers can access from
their own app. For example, if your app would like to deliver an SMS message, you don't need to build that
functionality yourself you can instead invoke whichever SMS app is already installed to deliver a message to the
recipient you specify.
The Application Framework layer provides many higher-level services to applications in the form of Java
classes. Application developers are allowed to make use of these services in their applications.
The Android framework includes the following key services −
l Activity Manager − Controls all aspects of the application lifecycle and activity stack.
l Content Providers − Allows applications to publish and share data with other applications.
l Resource Manager − Provides access to non-code embedded resources such as strings, color settings and
user interface layouts.
l Notifications Manager − Allows applications to display alerts and notifications to the user.
l View System − An extensible set of views used to create application user interfaces.
l Activities
l Services
l Broadcast Receivers
l Content Providers
1.4.1 . Activities
An activity is a class that represents a single screen. It is like a Frame in AWT or Swing in java. Since almost all
activities interact directly with the user, it is often helpful to think of an activity as the screen for a particular
action, such as logging in or taking a picture. Activity is a Java code that supports a screen or UI. Since mobile
phone screens are small, an activity typically takes up the whole screen. If you open multiple activities they are
stacked at top of each other . You can’t arrange activities side by side, like you can do with desktop windows.
Syntax
View: The view is everything you can see on the screen of the app — think of the individual UI elements like
buttons, labels, and text fields.
Intent
The main purpose of intent is to invoke individual components. Common uses include starting the service,
launching activities, displaying a list of contacts, dialing a phone number, or displaying a web page.
Intent is used to invoke components. It is mainly used to:
For example, you may write the following code to view the webpage.
Intent intent=new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse("http://www.google.com"));
startActivity(intent);
1.4.2 . Services
A service is a component which runs in the background without direct interaction with the user. As the service
has no user interface, it is not bound to the lifecycle of an activity. They handle background processing
associated with an application. There are two types of services local and remote. Local service is accessed from
within the application whereas remote service,accessed remotely from other applications running on the same
device.
Syntax
//block
Syntax
Syntax
1.4.5 . Fragment
Fragments are like parts of activity. An activity can display one or more fragments on the screen at the same
time.
AndroidManifest.xml
It contains informations about activities, content providers, permissions etc. It is like the web.xml file in Java EE.
1. In Android Studio, we need to create an Android Virtual Device (AVD) that the emulator can use to
install and run your app. To create a new AVD:-
2. Open the AVD Manager by clicking Tools > AVD Manager or Directly from an AVD manager.
To run an Android emulator that uses an AVD, double-click the AVD, or click Launch
l To stop the running emulator, right-click and select Stop, or click Menu ▼ and select Stop.
l If we want to clear the data from an emulator and return it to the initial state when it was first defined,
then right-click an AVD and select Wipe Data. Or click menu ▼ and select Wipe Data.
Chapter Two
2. Android Application
So let us proceed to write a simple Android Application which will print "Hello World!".
The first step is to create a simple Android Application using Android studio. When you click on Android studio
icon, it will show screen as shown below:
application development by calling start a new android studio project. A new installation frame should ask
Application name, package information and location of the project.
Fig 7: Create new project steps
The next level of installation should contain selecting the activity to mobile, it specifies the default layout for
Applications.
After entered application name, it going to be called select the form factors your application runs on, here need
to specify Minimum SDK, in this document, I have declared as API 23: Android 6.0(Mashmallow) and will run
approximately 84.6% of devices on this API.
At the final stage it going to be open development tool to write the application code.
This is a directory for drawable objects that are designed for high-density screens.
4 res/layout
This is a directory for files that define your app's user interface.
5 res/values
This is a directory for other various XML files that contain a collection of resources, such as
strings and colours definitions.
6 Build.gradle
Following section will give a brief overview of the important application files.
The main activity code is a Java file MainActivity.java. This is the actual application file which ultimately gets
converted to a Dalvik executable and runs your application. Following is the default code generated by the
application wizard for Hello World! Application.
package com.myandroid.helloworldapps;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate( savedInstanceState );
setContentView( R.layout.activity_main );
}}
Here, R.layout.activity_main refers to the activity_main.xml file located in the res/layout folder. The onCreate()
method is one of many methods that are figured when an activity is loaded.
Whatever component we want develop as a part of our application, we must declare all its components in a
manifest.xml which resides at the root of the application project directory. This file works as an interface between
Android OS and your application, so if we do not declare your component in this file, then it will not be
considered by the OS. For example, a default manifest file will look like as following file
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
Here <application>...</application> tags enclosed the components related to the application. Attribute
android:icon will point to the application icon available under res/drawable-hdpi. The application uses the image
named ic_launcher.png located in the drawable folders
The <activity> tag is used to specify an activity and android:name attribute specifies the fully qualified class
name of the Activity subclass and the android:label attributes specifies a string to use as the label for the activity.
You can specify multiple activities using <activity> tags.
The action for the intent filter is named android.intent.action.MAIN to indicate that this activity serves as the
entry point for the application. The category for the intent-filter is named android.intent.category.LAUNCHER
to indicate that the application can be launched from the device's launcher icon.
The @string refers to the strings.xml file explained below. Hence, @string/app_name refers to the app_name
string defined in the strings.xml file, which is "HelloWorld". Similar way, other strings get populated in the
application.
Following is the list of tags which we will use in our manifest file to specify different Android application
components −
The strings.xml file is located in the res/values folder and it contains all the text that our application uses. A
string is a simple resource that is referenced using the value provided in the name attribute (not the name of the
XML file). So, we can combine string resources with other simple resources in the one XML file, under one
<resources> element.
For example, the names of buttons, labels, default text, and similar types of strings go into this file. This file is
responsible for their textual content. For example, a default strings file will look like as following file:
<resources>
<string name="app_name">HelloWorld</string>
<string name="hello_world">Hello world!</string>
<string name="menu_settings">Settings</string>
<string name="title_activity_main">MainActivity</string>
</resources>
Elements:
<resources>
Required. This must be the root node.
No attributes.
<string>
A string, which can include styling tags. Beware that must escape apostrophes and quotation marks.
attributes:
name
String. A name for the string. This name is used as the resource ID.
String array
A string array is a simple resource that is referenced using the value provided in the name attribute (not the
name of the XML file). As such, you can combine string array resources with other simple resources in the one
XML file, under one <resources> element.
syntax:
<resources>
<string-array name="array_name">
<item>item_name</item>
</string-array>
</resources>
elements:
<resources>
Required. This must be the root node.
No attributes.
<string-array>
Defines an array of strings. Contains one or more <item> elements.
attributes:
name
String. A name for the array. This name is used as the resource ID to reference the array.
<item>
A string, which can include styling tags. The value can be a reference to another string resource. Must be a
child of a <string-array> element. Beware that you must escape apostrophes and quotation marks.
No attributes.
example:
XML file saved at res/values/strings.xml:
<resources>
<string name="app_name">COVD-19 Detecction Apps</string>
<string-array name="ethiopia_town">
<item>Addis Ababa</item>
<item>Harar</item>
<item>Bahirdar</item>
<item>Jimma</item>
<item>Mekele</item>
<item>Dire Dawa</item>
</string-array>
</resources>
The value always begins with a pound (#) character and then followed by the Alpha-Red-Green-Blue
information in one of the following formats:
l #RGB
l #ARGB
l #RRGGBB
l #AARRGGBB
Note: A color is a simple resource that is referenced using the value provided in the name attribute (not the name
of the XML file). As such, we can combine color resources with other simple resources in the one XML file,
under one <resources> element.
file location:
res/values/colors.xml
The filename is arbitrary. The <color> element's name will be used as the resource ID.
resource reference:
In Java: R.color.color_name
In XML: @[package:]color/color_name
syntax:
<color name="color_name">hexa_value</color>
<resources>
Elements:
<resources>
Required. This must be the root node.
No attributes.
<color>
A color expressed in hexadecimal, as described above.
attributes:
name
String. A name for the color. This will be used as the resource ID.
example:
XML file saved at res/values/colors.xml:
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
This is an example of simple RelativeLayout which we will study in a separate chapter. The TextView is an
Android control used to build the GUI and it have various attributes like android:layout_width,
android:layout_height etc which are being used to set its width and height etc.. The @string refers to the
strings.xml file located in the res/values folder. Hence, @string/hello_world refers to the hello string defined in
the strings.xml file, which is "Hello World!".
1. In the Project window, click the module folder and then select File > New > XML > Layout XML.
2. Enter a name for the layout file and enter "androidx.constraintlayout.widget.ConstraintLayout" for the
Root Tag.
3. Click Finish.
1. Drag a view from the Palette window into the editor. When we add a view in a ConstraintLayout, it
displays a bounding box with square resizing handles on each corner and circular constraint handles on
each side.
2. Click the view to select it.
3. Do one of the following:
l Click a constraint handle and drag it to an available anchor point. This point can be the edge of
another view, the edge of the layout, or a guideline. Notice that as we drag the constraint handle,
the Layout Editor shows potential connection anchors and blue overlays.
l Click one of the Create a connection buttons in the Layout section of the Attributes
window, as shown in the following figure:
Figure . The Layout section of the Attributes window lets we create connections.
When the constraint is created, the editor gives it a default margin to separate the two views.
l Every view must have at least two constraints: one horizontal and one vertical.
l We can create constraints only between a constraint handle and an anchor point that share the same plane.
So a vertical plane (the left and right sides) of a view can be constrained only to another vertical plane;
and baselines can constrain only to other baselines.
l Each constraint handle can be used for just one constraint, but you can create multiple constraints (from
different views) to the same anchor point.
Figure. A red constraint indicates that you can click to delete it.
l In the Layout section of the Attributes window, click on a constraint anchor, as shown in the following
figure.
When we want to add opposing constraints on a view, the constraint lines become squiggly like a spring to
indicate the opposing forces. The effect is most visible when the view size is set to "fixed" or "wrap content," in
which case the view is centered between the constraints. If we instead want the view to stretch its size to meet
the constraints, switch the size to "match constraints"; or if we want to keep the current size but move the view
so that it is not centered, adjust the constraint bias.
And also we can use constraints to achieve different types of layout behavior, as described in the following
sections.
Figure. A
horizontal and vertical constraint
1.1.4 . Alignment
Align the edge of a view to the same edge of another view. In below figure , the left side of B is aligned to the
left side of A. If you want to align the view centers, create a constraint on both sides. And also we can offset the
alignment by dragging the view inward from the constraint. Since, we can also select all the views you want to
align, and then click Align in the toolbar to select the alignment type.
Figure(the left)A horizontal alignment constraint (the right) An offset horizontal alignment constraint
Align the text baseline of a view to the text baseline of another view. In the
right figure, the first line of B is aligned with the text in A.
We can add a vertical or horizontal guideline to which can constrain views, and the guideline will be invisible to
app users. The position the guideline within the layout based on either dp units or percent, relative to the layout's
edge. To create a guideline, click Guidelines in the toolbar,
and then click either Add Vertical Guideline or Add
Horizontal Guideline.
Drag the dotted line to reposition it and click the circle at the edge of
the guideline to toggle the measurement mode.
Similar to a guideline, a barrier is an invisible line that you can constrain views to. Except a barrier does not
define its own position; instead, the barrier position moves based on the position of views contained within it.
This is useful when you want to constrain a view to the a set of views rather than to one specific view.
For example, in the above figure shows view C is constrained to the right side of a barrier. The barrier is set to
the "end" (or the right side in a left-to-right layout) of both view A and view B. So the barrier moves depending
on whether the right side of view A or view B is is farthest right.
3. Select the barrier from the Component Tree, open the Attributes window, and then set the
barrierDirection.
Now we can create a constraint from another view to the barrier. And also, we can constrain views that are
inside the barrier to the barrier. This way, we can ensure that all views in the barrier always align to each other,
even if we don't know which view will be the longest or tallest.
We can also include a guideline inside a barrier to ensure a "minimum" position for the barrier.
different content or screen sizes. To select a different sizing mode, click a view and open the Attributes
window on the right side of the editor.
Near the top of the Attributes window is the view inspector, which includes controls for several layout
attributes, as shown in figure 14 (this is available only for views in a constraint layout).
You can change the way the height and width are calculated by clicking the symbols indicated with callout 3 in
figure below. These symbols represent the size mode as follows (click the symbol to toggle between these
settings):
l Fixed: You specify a specific dimension in the text box below or by resizing the view in the
editor.
l Wrap Content: The view expands only as much as needed to fit its contents.
l Match Constraints: The view expands as much as possible to meet the constraints on each side
(after accounting for the view's margins). However, you can modify that behavior with the following
attributes and values (these attributes take effect only when you set the view width to match constraints):
l layout_constraintWidth_default
l spread: Expands the view as much as possible to meet the constraints on each side. This is
the default behavior.
l wrap: Expands the view only as much as needed to fit its contents, but still allows the
view to be smaller than that if the constraints require it. So the difference between this and
using Wrap Content (above), is that setting the width to Wrap Content forces the width
to always exactly match the content width; whereas using Match Constraints with
layout_constraintWidth_default set to wrap also allows the view to be smaller than the
content width.
l layout_constraintWidth_min
l layout_constraintWidth_max
When we want to set the view size to a ratio such as 16:9 if at least one of the view dimensions is set to "match
constraints" (0dp). To enable the ratio, click Toggle Aspect Ratio Constraint (callout 1 in figure above), and
then enter the width:height ratio in the input that appears.
If both the width and height are set to match constraints, we can click Toggle Aspect Ratio Constraint to select
which dimension is based on a ratio of the other. The view inspector indicates which is set as a ratio by
connecting the corresponding edges with a solid line.
1.3. Adjust the view margins
To ensure that all your views are evenly spaced, click Margin in the toolbar to select the default
margin for each view that you add to the layout. Any change you make to the default margin applies only to the
views you add from then on.
We can control the margin for each view in the Attributes window by clicking the number on the line that
represents each constraint (in figure 14, callout 4 shows the bottom margin is set to 16dp).
.Spread: The views are evenly distributed (after margins are accounted for). This is the default.
.Spread inside: The first and last view are affixed to the constraints on each end of the chain and the rest are
evenly distributed.
.Weighted: When the chain is set to either spread or spread inside, you can fill the remaining space by
setting one or more views to "match constraints" (0dp). By default, the space is evenly distributed
between each view that's set to "match constraints," but you can assign a weight of importance to each
view using the layout_constraintHorizontal_weight and
layout_constraintVertical_weight attributes. If you're familiar with layout_weight in a
linear layout, this works the same way. So the view with the highest weight value gets the most amount of
space; views that have the same weight get the same amount of space.
.Packed: The views are packed together (after margins are accounted for). You can then adjust the whole
chain's bias (left/right or up/down) by changing the chain's head view bias.
The chain's "head" view (the left-most view in a horizontal chain and the top-most view in a vertical chain)
defines the chain's style in XML. However, you can toggle between spread, spread inside, and packed by
selecting any view in the chain and then clicking the chain button that appears below the view.
Let's try to run our Hello World! application we just created. I assume you had created your AVD while doing
environment set-up. To run the app from Android studio, open one of your project's activity files and click Run
icon from the tool bar. Android studio installs the app on your AVD and starts it and if everything is fine with
your set-up and application, it will display following Emulator window
fig 8: Sample output on Android emulator
Congratulations!!! we have developed our first Android Application and now just keep following rest of the
tutorial step by step to become a great Android Developer. All the very best.
1. Activity is a java class that creates and default window on the screen where we can place different
components such as Button, EditText, TextView, Spinner etc. It is like the Frame of Java AWT. It
provides life cycle methods for activity such as onCreate, onStop, OnResume etc.
2. The onCreate method is called when Activity class is first created.
3. The setContentView(R.layout.activity_main) gives information about our layout resource.
Here, our layout resources are defined in activity_main.xml file.
File: activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
As you can see, a textview is created by the framework automatically. But the message for this string is defined
in the strings.xml file. The @string/hello_world provides information about the textview message. The value of
the attribute hello_world is defined in the strings.xml file.
File: strings.xml
<resources>
<string name="app_name">HelloWorld</string>
<string name="hello_world">Hello world!</string>
<string name="menu_settings">Settings</string>
<string name="title_activity_main">MainActivity</string>
</resources>
You can change the value of the hello_world attribute from this file.
Generated R.java file
It is the auto-generated file that contains IDs for all the resources of res directory. It is generated by aapt(Android
Asset Packaging Tool). Whenever you create any component on activity_main, a corresponding ID is created in
the R.java file which can be used in the Java Source file later.
APK File
An apk file is created by the framework automatically. If you want to run the android application on the mobile,
transfer and install it.
Resources
The AndroidManifest.xml file contains information of your package, including components of the application
such as activities, services, broadcast receivers, content providers etc.
l It is responsible to protect the application to access any protected parts by providing the permissions.
l It also declares the android api that the application is going to use.
l It lists the instrumentation classes. The instrumentation classes provides profiling and other
informations. These informations are removed just before the application is published etc.
This is the required xml file for all the android application and located inside the root directory. A simple
AndroidManifest.xml file looks like this:
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
</manifest>
The javac tool compiles the java source file into the class file.
The dx tool takes all the class files of your application and generates a single .dex file. It is a platform-specific
tool.
The Android Assets Packaging Tool (aapt) handles the packaging process.
In this example, we are going to explain how to hide the title bar and how to display content in full screen mode.
The setFlags() method of Window class is used to display content in full screen mode. You need to pass the
WindowManager.LayoutParams.FLAG_FULLSCREEN constant in the setFlags method.
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate( savedInstanceState );
setContentView( R.layout.activity_main );
this.getWindow().setFlags( WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
Syntax:
<activity android:name="package_name.our_Acctivity_name( e.g.MainActivity)"
android:screenOrientation="orientation_type([portrait,landscape,reversePortable...])">
</activity>
Example:
<activity android:name=".MainActivity"
android:screenOrientation="landscape">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
An activity is a single, focused thing that the user can do. Almost all activities interact with the user, so the
Activity class takes care of creating a window for you in which you can place your UI with
setContentView(View). While activities are often presented to the user as full-screen windows, they can
also be used in other ways: as floating windows (via a theme with R.attr.windowIsFloating set), Multi-
Window mode or embedded into other windows. There are two methods almost all subclasses of Activity will
implement:
l onCreate(Bundle) is where you initialize your activity. Most importantly, here you will usually call
setContentView(int) with a layout resource defining your UI, and using findViewById(int)
to retrieve the widgets in that UI that you need to interact with programmatically.
l onPause() is where you deal with the user pausing active interaction with the activity. Any changes
made by the user should at this point be committed (usually to the ContentProvider holding the
data). In this state the activity is still visible on screen.
To be of use with Context.startActivity(), all activity classes must have a corresponding
<activity> declaration in their package's AndroidManifest.xml.
An activity provides the window in which the app draws its UI. This window typically fills the screen, but may
be smaller than the screen and float on top of other windows. Generally, one activity implements one screen in an
app. For instance, one of an app’s activities may implement a Preferences screen, while another activity
implements a Select Photo screen.
Activities in the system are managed as activity stack. When a new activity is started, it is usually placed on the
top of the current stack and becomes the running activity. The previous activity always remains below it in the
stack, and will not come to the foreground again until the new activity exits. There can be one or multiple
activity stacks visible on screen.
l If an activity is in the foreground of the screen (at the highest position of the topmost stack), it is active or
running. This is usually the activity that the user is currently interacting with.
l If an activity has lost focus but is still presented to the user, it is visible. It is possible if a new non-full-
sized or transparent activity has focus on top of your activity, another activity has higher position in
multi-window mode, or the activity itself is not focusable in current windowing mode. Such activity is
completely alive (it maintains all state and member information and remains attached to the window
manager).
l If an activity is completely obscured by another activity, it is stopped or hidden. It still retains all state and
member information, however, it is no longer visible to the user so its window is hidden and it will often
be killed by the system when memory is needed elsewhere.
l The system can drop the activity from memory by either asking it to finish, or simply killing its process,
making it destroyed. When it is displayed again to the user, it must be completely restarted and restored to
its previous state.
The entire lifecycle of an activity is defined by the following Activity methods. All of these are hooks that you
can override to do appropriate work when the activity changes state. All activities will implement
onCreate(Bundle) to do their initial setup; many will also implement onPause() to commit changes to
data and prepare to pause interacting with the user, and onStop() to handle no longer being visible on screen.
You should always call up to your superclass when implementing these methods.
onCreate()
You must implement this callback, which fires when the system creates your activity. Your implementation
should initialize the essential components of your activity: For example, your app should create views and bind
data to lists here. Most importantly, this is where you must call setContentView() to define the layout for
the activity's user interface.
When onCreate() finishes, the next callback is always onStart().
onStart()
As onCreate() exits, the activity enters the Started state, and the activity becomes visible to the user. This
callback contains what amounts to the activity’s final preparations for coming to the foreground and becoming
interactive.
onResume()
The system invokes this callback just before the activity starts interacting with the user. At this point, the activity
is at the top of the activity stack, and captures all user input. Most of an app’s core functionality is implemented
in the onResume() method.
onPause()
The system calls onPause() when the activity loses focus and enters a Paused state. This state occurs when,
for example, the user taps the Back or Recents button. When the system calls onPuase() for your activity, it
technically means your activity is still partially visible, but most often is an indication that the user is leaving the
activity, and the activity will soon enter the Stopped or Resumed state.
An activity in the Paused state may continue to update the UI if the user is expecting the UI to update. Examples
of such an activity include one showing a navigation map screen or a media player playing. Even if such
activities lose focus, the user expects their UI to continue updating.
You should not use onPuase() to save application or user data, make network calls, or execute database
transactions. For information about saving data.
Once onPuase() finishes executing, the next callback is either onStop() or onResume(), depending on what
happens after the activity enters the Paused state.
onStop()
The system calls onStop() when the activity is no longer visible to the user. This may happen because the
activity is being destroyed, a new activity is starting, or an existing activity is entering a Resumed state and is
covering the stopped activity. In all of these cases, the stopped activity is no longer visible at all.
The next callback that the system calls is either onRestart(), if the activity is coming back to interact with
the user, or by onDestroy() if this activity is completely terminating.
onRestart()
The system invokes this callback when an activity in the Stopped state is about to restart. onRestart()
restores the state of the activity from the time that it was stopped.
This callback is always followed by onStart().
onDestroy()
The system invokes this callback before an activity is destroyed.
This callback is the final one that the activity receives. onDestroy() is usually implemented to ensure that all
of an activity’s resources are released when the activity, or the process containing it, is destroyed.
The following diagram shows the important state paths of an Activity. The square rectangles represent callback
methods you can implement to perform operations when the Activity moves between states. The colored ovals
are major states the Activity can be in.
fig 10: Android Activity Life-cycle
Following is the content of the modified main activity file MainActivity.java. This file includes each of the
fundamental life cycle methods. The Log.d() method has been used to generate log messages.
package com.myservice.activitylifecycle;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
public class MainActivity extends AppCompatActivity {
String text="Android Activity:";
There are three key loops you may be interested in monitoring within your activity:
l The entire lifetime of an activity happens between the first call to onCreate(Bundle) through to a
single final call to onDestroy(). An activity will do all setup of "global" state in onCreate(), and
release all remaining resources in onDestroy(). For example, if it has a thread running in the background
to download data from the network, it may create that thread in onCreate() and then stop the thread in
onDestroy().
l The visible lifetime of an activity happens between a call to onStart() until a corresponding call to
onStop(). During this time the user can see the activity on-screen, though it may not be in the
foreground and interacting with the user. Between these two methods you can maintain resources that are
needed to show the activity to the user. For example, we can register a BroadcastReceiver in
onStart() to monitor for changes that impact our UI, and unregister it in onStop() when the user no longer
sees what we are displaying. The onStart() and onStop() methods can be called multiple times, as the
activity becomes visible and hidden to the user.
l The foreground lifetime of an activity happens between a call to onResume() until a corresponding
call to onPause(). During this time the activity is in visible, active and interacting with the user. An
activity can frequently go between the resumed and paused states -- for example when the device goes to
sleep, when an activity result is delivered, when a new intent is delivered -- so the code in these methods
should be fairly lightweight.
Note that services, like other application objects, run in the main thread of their hosting process. This means that,
if your service is going to do any CPU intensive (such as MP3 playback) or blocking (such as networking)
operations, it should spawn its own thread in which to do that work. More information on this can be found in
Processes and Threads. The JobIntentService class is available as a standard implementation of Service
that has its own thread where it schedules its work to be done.
Most confusion about the Service class actually revolves around what it is not:
l A Service is not a separate process. The Service object itself does not imply it is running in its own
process; unless otherwise specified, it runs in the same process as the application it is part of.
l A Service is not a thread. It is not a means itself to do work off of the main thread (to avoid Application
Not Responding errors).
3.2.1 . Service Lifecycle
There are two reasons that a service can be run by the system. If someone calls Context.startService()
then the system will retrieve the service (creating it and calling its onCreate() method if needed) and then
call its onStartCommand(Intent, int, int) method with the arguments supplied by the client. The
service will at this point continue running until Context.stopService() or stopSelf() is called.
Note that multiple calls to Context.startService() do not nest (though they do result in multiple corresponding
calls to onStartCommand()), so no matter how many times it is started a service will be stopped once
Context.stopService() or stopSelf() is called; however, services can use their stopSelf(int) method to
ensure the service is not stopped until started intents have been processed.
There can be two forms of a service. The lifecycle of service can follow two different paths: started or bound.
1. Started
2. Bound
1) Started Service
A service is started when component (like activity) calls startService() method, now it runs in the background
indefinitely. It is stopped by stopService() method. The service can stop itself by calling the stopSelf() method.
2) Bound Service
A service is bound when another component (e.g. client) calls bindService() method. The client can unbind the
service by calling the unbindService() method. The service cannot be stopped until all clients unbind the
service.
Fig 11: Android service life-cycle
For started services, there are two additional major modes of operation they can decide to run in, depending on
the value they return from onStartCommand(): START_STICKY is used for services that are explicitly started
and stopped as needed, while START_NOT_STICKY or START_REDELIVER_INTENT are used for services
that should only remain running while processing any commands sent to them. See the linked documentation for
more detail on the semantics.
Clients can also use Context.bindService() to obtain a persistent connection to a service. This likewise
creates the service if it is not already running (calling onCreate() while doing so), but does not call
onStartCommand(). The client will receive the IBinder object that the service returns from its
onBind(Intent) method, allowing the client to then make calls back to the service. The service will remain
running as long as the connection is established (whether or not the client retains a reference on the service's
IBinder). Usually the IBinder returned is for a complex interface that has been written in aidl.
A service can be both started and have connections bound to it. In such a case, the system will keep the service
running as long as either it is started or there are one or more connections to it with the
Context.BIND_AUTO_CREATE flag. Once neither of these situations hold, the service's onDestroy()
method is called and the service is effectively terminated. All cleanup (stopping threads, unregistering receivers)
should be complete upon returning from onDestroy().
The following program shows how to use service and its life cycles
package com.myservice.serviceexample;
import android.app.Service;
import android.content.Intent;
import android.media.MediaPlayer;
import android.os.IBinder;
import android.widget.Toast;
onStartCommand()
The system calls this method when another component, such as an activity, requests that the service be started,
by calling startService(). If you implement this method, it is your responsibility to stop the service when its work
is done, by calling stopSelf() or stopService() methods.
onBind()
The system calls this method when another component wants to bind with the service by calling bindService(). If
you implement this method, you must provide an interface that clients use to communicate with the service, by
returning an IBinder object. You must always implement this method, but if you don't want to allow binding, then
you should return null.
onUnbind()
The system calls this method when all clients have disconnected from a particular interface published by the
service.
onRebind()
The system calls this method when new clients have connected to the service, after it had previously been
notified that all had disconnected in its onUnbind(Intent).
onRebind()
The system calls this method when new clients have connected to the service, after it had previously been
notified that all had disconnected in its onUnbind(Intent).
onCreate()
The system calls this method when the service is first created using onStartCommand() or onBind(). This call is
required to perform one-time set-up.
onDestroy()
The system calls this method when the service is no longer used and is being destroyed. Your service should
implement this to clean up any resources such as threads, registered listeners, receivers, etc.
Broadcast Receivers simply respond to broadcast messages from other applications or from the system itself.
These messages are sometime called events or intents. For example, applications can also initiate broadcasts to
let other applications know that some data has been downloaded to the device and is available for them to use, so
this is broadcast receiver who will intercept this communication and will initiate appropriate action.
There are following two important steps to make BroadcastReceiver works for the system broadcasted intents
A broadcast receiver is implemented as a subclass of BroadcastReceiver class and overriding the onReceive()
method where each message is received as a Intent object parameter.
package com.broadcast.broadcastreciver;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.widget.Toast;
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText( context,"test for brodcat reciver",Toast.LENGTH_LONG ).show();
}
}
An application listens for specific broadcast intents by registering a broadcast receiver in AndroidManifest.xml
file. Consider we are going to register MyReceiver for system generated event ACTION_BOOT_COMPLETED
which is fired by the system once the Android system has completed the boot process.
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<receiver
android:name=".MyReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED">
</action>
</intent-filter>
</receiver>
</application>
</manifest>
Now whenever our Android device gets booted, it will be intercepted by BroadcastReceiver MyReceiver and
implemented logic inside onReceive() will be executed.
.
Chapter Four
4. User Interface
The basic building block for user interface is a View object which is created from the View class and occupies a
rectangular area on the screen and is responsible for drawing and event handling. View is the base class for
widgets, which are used to create interactive UI components like buttons, text fields, etc.
A layout defines the structure for a user interface in your app, such as in an activity. All elements in the layout
are built using a hierarchy of view and viewGroup objects. A view usually draws something the user can see and
interact with. Whereas a viewGroup is an invisible container that defines the layout structure for view and other
viewGroup objects, as shown in figure 1.
The ViewGroup is a subclass of View and provides invisible container that hold other Views or other
ViewGroups and define their layout properties.
The View
objects
are
usually
called
"widgets"
and can be
one of
many
subclasses, such as Button or TextView. The ViewGroup objects are usually called "layouts" can be one of many
types that provide a different layout structure, such as LinearLayout or ConstraintLayout .
You can declare a layout in two ways:
l Declare UI elements in XML. Android provides a straightforward XML vocabulary that corresponds to
the View classes and subclasses, such as those for widgets and layouts.
You can also use Android Studio's Layout Editor to build your XML layout using a drag-and-drop
interface.
l Instantiate layout elements at runtime. Your app can create View and ViewGroup objects (and
manipulate their properties) programmatically.
Declaring your UI in XML allows you to separate the presentation of your app from the code that controls its
behavior. Using XML files also makes it easy to provide different layouts for different screen sizes and
orientations (discussed further in Supporting Different Screen Sizes).
The Android framework gives you the flexibility to use either or both of these methods to build your app's UI.
For example, you can declare your app's default layouts in XML, and then modify the layout at runtime.
Tip:To debug your layout at runtime, use the Layout Inspector tool.
Each layout file must contain exactly one root element, which must be a View or ViewGroup object. Once we've
defined the root element, we can add additional layout objects or widgets as child elements to gradually build a
View hierarchy that defines our layout.
For example, here's an XML layout that uses a horizontal ConstraintLayout to hold a TextView .
</androidx.constraintlayout.widget.ConstraintLayout>
After declared a layout in XML, save the file with the .xml extension, in Android project's res/layout/ directory,
so it will properly compile.
When you compile your app, each XML layout file is compiled into a View resource. You should load the layout
resource from your app code, in your Activity.onCreate() callback implementation. Do so by calling
setContentView(), passing it the reference to your layout resource in the form of: R.layout.layout_file_name. For
example, if our XML layout is saved as main_layout.xml, you would load it for your Activity like so:
The onCreate() callback method in our Activity is called by the Android framework when our Activity is
launched.
Attributes
Every View and ViewGroup object supports their own variety of XML attributes. Some attributes are specific to
a View object (for example, TextView supports the textSize attribute), but these attributes are also inherited by
any View objects that may extend this class. Some are common to all View objects, because they are inherited
from the root View class (like the id attribute). And, other attributes are considered "layout parameters," which
are attributes that describe certain layout orientations of the View object, as defined by that object's parent
ViewGroup object.
ID
Any View object may have an integer ID associated with it, to uniquely identify the View within the tree. When
the app is compiled, this ID is referenced as an integer, but the ID is typically assigned in the layout XML file as
a string, in the id attribute. This is an XML attribute common to all View objects (defined by the View class) and
you will use it very often. The syntax for an ID, inside an XML tag is:
android:id="@+id/myButton"
The at-symbol (@) at the beginning of the string indicates that the XML parser should parse and expand the rest
of the ID string and identify it as an ID resource. The plus-symbol (+) means that this is a new resource name
that must be created and added to our resources (in the R.java file). There are a number of other ID resources that
are offered by the Android framework. When referencing an Android resource ID, we do not need the plus-
symbol, but must add the android package namespace, like so:
android:id="@android:id/empty"
With the android package namespace in place, we're now referencing an ID from the android.R resources
class, rather than the local resources class.
In order to create views and reference them from the app, a common pattern is to:
<Button
android:id="@+id/showbtn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp"
android:textSize="20dp"
android:text="@string/btn_show"
.../>
Then create an instance of the view object and capture it from the layout (typically in the onCreate() method):
Button btn=(Button) findViewById( R.id.mybtnID );
Defining IDs for view objects is important when creating a RelativeLayout. In a relative layout, sibling views
can define their layout relative to another sibling view, which is referenced by the unique ID.
An ID need not be unique throughout the entire tree, but it should be unique within the part of the tree you are
searching (which may often be the entire tree, so it's best to be completely unique when possible).
Note: With Android Studio 3.6 and higher, the view binding feature can replace findViewById() calls and
provides compile-time type safety for code that interacts with views. Consider using view binding instead of
findViewById().
XML layout attributes named layout_something define layout parameters for the View that are appropriate for
the ViewGroup in which it resides.
Every ViewGroup class implements a nested class that extends ViewGroup.LayoutParams. This subclass
contains property types that define the size and position for each child view, as appropriate for the view group.
As you can see in figure 2, the parent view group defines layout parameters for each child view (including the
child view group).
Figure 2. Visualization of a view hierarchy with layout parameters associated with each view
At third level we have different layouts which are subclasses of ViewGroup class and a typical layout defines the
visual structure for an Android user interface and can be created either at run time using View/ViewGroup
objects or we can declare your layout using simple XML file main_layout.xml which is located in the res/layout
folder of our project.
Note that every LayoutParams subclass has its own syntax for setting values. Each child element must define
LayoutParams that are appropriate for its parent, though it may also define different LayoutParams for its own
children.
All view groups include a width and height (layout_width and layout_height), and each view is required to
define them. Many LayoutParams also include optional margins and borders.
You can specify width and height with exact measurements, though probably won't want to do this often. More
often, will use one of these constants to set the width or height:
l wrap_content tells our view to size itself to the dimensions required by its content.
l match_parent tells our view to become as big as its parent view group will allow.
In general, specifying a layout width and height using absolute units such as pixels is not recommended. Instead,
using relative measurements such as density-independent pixel units (dp), wrap_content, or match_parent, is a
better approach, because it helps ensure that the app will display properly across a variety of device screen sizes.
Layout Position
The geometry of a view is that of a rectangle. A view has a location, expressed as a pair of left and top
coordinates, and two dimensions, expressed as a width and a height. The unit for location and dimensions is the
pixel.
It is possible to retrieve the location of a view by invoking the methods getLeft() and getTop(). The former
returns the left, or X, coordinate of the rectangle representing the view. The latter returns the top, or Y, coordinate
of the rectangle representing the view. These methods both return the location of the view relative to its parent.
For instance, when getLeft() returns 20, that means the view is located 20 pixels to the right of the left edge of
its direct parent.
In addition, several convenience methods are offered to avoid unnecessary computations, namely getRight()
and getBottom(). These methods return the coordinates of the right and bottom edges of the rectangle
representing the view. For instance, calling getRight() is similar to the following computation: getLeft() +
getWidth().
The size of a view is expressed with a width and a height. A view actually possesses two pairs of width and
height values.
The first pair is known as measured width and measured height. These dimensions define how big a view wants
to be within its parent. The measured dimensions can be obtained by calling getMeasuredWidth() and
getMeasuredHeight().
The second pair is simply known as width and height, or sometimes drawing width and drawing height. These
dimensions define the actual size of the view on screen, at drawing time and after layout. These values may, but
do not have to, be different from the measured width and height. The width and height can be obtained by calling
getWidth() and getHeight().
To measure its dimensions, a view takes into account its padding. The padding is expressed in pixels for the left,
top, right and bottom parts of the view. Padding can be used to offset the content of the view by a specific
number of pixels. For instance, a left padding of 2 will push the view's content by 2 pixels to the right of the left
edge. Padding can be set using the setPadding(int, int, int, int) method and queried by calling
getPaddingLeft(), getPaddingTop(), getPaddingRight() and
getPaddingBottom().
Even though a view can define a padding, it does not provide any support for margins. However, view groups
provide such a support.
Note: Although we can nest one or more layouts within another layout to achieve the UI design, should strive to
keep your layout hierarchy as shallow as possible. The layout draws faster if it has fewer nested layouts (a wide
view hierarchy is better than a deep view hierarchy).
Fig 2
different
layout
managers
A) Rel
ati
ve
Layout
RelativeLayout is a view group that displays child views in relative positions. The position of each view can be
specified as relative to sibling elements (such as to the left-of or below another view) or in positions relative to
the parent RelativeLayout area (such as aligned to the bottom, left or center).
A RelativeLayout is a very powerful utility for designing a user interface because it can eliminate nested view
groups and keep our layout hierarchy flat, which improves performance. If we find ourself using several nested
LinearLayout groups, we may be able to replace them with a single RelativeLayout.
Positioning Views
RelativeLayout lets child views specify their position relative to the parent view or to each other (specified by
ID). So you can align two elements by right border, or make one below another, centered in the screen, centered
left, and so on. By default, all child views are drawn at the top-left of the layout, so we must define the position
of each view using the various layout properties available from RelativeLayout.LayoutParams.
android:layout_alignParentTop : If "true", makes the top edge of this view match the top edge of the parent.
android:layout_centerVertical : If "true", centers this child vertically within its parent.
android:layout_below : Positions the top edge of this view below the view specified with a resource ID.
android:layout_toRightOf : Positions the left edge of this view to the
right of the view specified with a resource ID.
The value for each layout property is either a boolean to enable a
layout position relative to the parent RelativeLayout or an ID that
references another view in the layout against which the view should
be positioned.
Examlpe:
</RelativeLayout>
B) Liner Layout
LinearLayout is a view group that aligns all children in a single direction, vertically or horizontally. You can
specify the layout direction with the android:orientation attribute.
All children of a LinearLayout are stacked one after the other, so a vertical list will only have one child per
row, no matter how wide they are, and a horizontal list will only be one row high (the height of the tallest child,
plus padding). A LinearLayout respects margins between children and the gravity (right, center, or left
alignment) of each child.
Example:
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Send to" />
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="write subject" />
<EditText
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:gravity="top"
android:hint="contents" />
<Button
android:layout_width="100dp"
android:layout_height="wrap_content"
android:layout_gravity="right"
android:text="Send Email" />
</LinearLayout>
C) Constraint Layout
There are currently various types of constraints that you can use:
l Relative positioning
l Margins
l Centering positioning
l Circular positioning
l Visibility behavior
l Dimension constraints
l Chains
Relative positioning
Relative positioning is one of the basic building blocks of creating layouts in ConstraintLayout. Those
constraints allow us to position a given widget relative to another one. We can constrain a widget on the
horizontal and vertical axis:
<Button
android:id="@+id/bbtnid"
android:text="Button B"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toLeftOf="@+id/abtnId" />
This tells the system that we want the left side of button B to be constrained to the right side of button A. Such a
position constraint means that the system will try to have both sides share the same location.
They all take a reference id to another widget, or the parent (which will reference the parent container, i.e. the
ConstraintLayout):
<Button
android:id="@+id/ButtonBId"
android:layout_height="wrap_content"
android:text="Button A"
...
app:layout_constraintLrft_toLeftOf="parent" />
Margins
Note that a margin can only be positive or equals to zero, and takes a Dimension.
When a position constraint target's visibility is View.GONE, you can also indicate a different margin value to be
used using the following attributes:
l layout_goneMarginStart
l layout_goneMarginEnd
l layout_goneMarginLeft
l layout_goneMarginTop
l layout_goneMarginRight
l layout_goneMarginBottom
Centering positioning and bias
A useful aspect of ConstraintLayout is in how it deals with "impossible" constrains. For example, if we
have something like:
<Button
android:id="@+id/button"
android:text="Button A"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
Unless the ConstraintLayout happens to have the exact same size as the Button, both constraints cannot
be satisfied at the same time (both sides cannot be where we want them to be).
<android.support.constraint.ConstraintLayout ...>
<Button android:id="@+id/button" ...
app:layout_constraintHorizontal_bias="0.3"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent/>
</>
Circular positioning
Now we can constrain a widget center relative to another widget center, at an angle and a distance. This allows
us to position a widget on a circle (see Fig. 6). The following attributes can be used:
l layout_constraintCircle : references another widget id
l layout_constraintCircleRadius : the distance to the other widget center
l layout_constraintCircleAngle : which angle the widget should be at (in degrees, from 0 to
360)
Visibility behavior
ConstraintLayout has a specific handling of widgets being marked as View.GONE. GONE widgets, as
usual, are not going to be displayed and are not part of the layout itself (i.e. their actual dimensions will not be
changed if marked as GONE).
But in terms of the layout computations, GONE widgets are still part of it, with an important distinction:
l For the layout pass, their dimension will be considered as zero (basically, they will be resolved to a point)
l If they have constraints to other widgets they will still be respected, but any margins will be as if equals
to zero
Note: The margin used will be the margin that B had defined when connecting to A (see Fig. 7 for an example).
In some cases, this might not be the margin you want (e.g. A had a 100dp margin to the side of its container, B
only a 16dp to A, marking A as gone, B will have a margin of 16dp to the container). For this reason, you can
specify an alternate margin value to be used when the connection is to a widget being marked as gone (see the
section above about the gone margin attributes).
Dimensions constraints
Minimum dimensions on ConstraintLayout
You can define minimum and maximum sizes for the ConstraintLayout itself:
Those minimum and maximum dimensions will be used by ConstraintLayout when its dimensions are set
to WRAP_CONTENT.
Widgets dimension constraints
The dimension of the widgets can be specified by setting the android:layout_width and
android:layout_height attributes in 3 different ways:
l Using a specific dimension (either a literal value such as 123dp or a Dimension reference)
l Using WRAP_CONTENT, which will ask the widget to compute its own size
l Using 0dp, which is the equivalent of "MATCH_CONSTRAINT"
If a dimension is set to WRAP_CONTENT, in versions before 1.1 they will be treated as a literal dimension --
meaning, constraints will not limit the resulting dimension. While in general this is enough (and faster), in some
situations, you might want to use WRAP_CONTENT, yet keep enforcing constraints to limit the resulting
dimension. In that case, you can add one of the corresponding attribute:
l app:layout_constrainedWidth=”true|false”
l app:layout_constrainedHeight=”true|false”
MATCH_CONSTRAINT dimensions
When a dimension is set to MATCH_CONSTRAINT, the default behavior is to have the resulting size take all the
available space. Several additional modifiers are available:
l layout_constraintWidth_min and layout_constraintHeight_min : will set the
minimum size for this dimension
l layout_constraintWidth_max and layout_constraintHeight_max : will set the
maximum size for this dimension
l layout_constraintWidth_percent and layout_constraintHeight_percent : will set
the size of this dimension as a percentage of the parent
Min and Max
The value indicated for min and max can be either a dimension in Dp, or "wrap", which will use the same value
as what WRAP_CONTENT would do.
Percent dimension
To use percent, we need to set the following:
l The dimension should be set to MATCH_CONSTRAINT (0dp)
l The default should be set to percent app:layout_constraintWidth_default="percent" or
app:layout_constraintHeight_default="percent"
l Then set the layout_constraintWidth_percent or
layout_constraintHeight_percent attributes to a value between 0 and 1
Ratio
We can also define one dimension of a widget as a ratio of the other one. In order to do that, we need to have at
least one constrained dimension be set to 0dp (i.e., MATCH_CONSTRAINT), and set the attribute
layout_constraintDimensionRatio to a given ratio. For example:
<Button android:layout_width="wrap_content"
android:layout_height="0dp"
app:layout_constraintDimensionRatio="1:1" />
This will set the height of the button to be the same as its width.
The ratio can be expressed either as:
l a float value, representing a ratio between width and height
l a ratio in the form "width:height"
We can also use ratio if both dimensions are set to MATCH_CONSTRAINT (0dp). In this case the system sets the
largest dimensions that satisfies all constraints and maintains the aspect ratio specified. To constrain one specific
side based on the dimensions of another, you can pre append W," or H, to constrain the width or height
respectively. For example, If one dimension is constrained by two targets (e.g. width is 0dp and centered on
parent) you can indicate which side should be constrained, by adding the letter W (for constraining the width) or
H (for constraining the height) in front of the ratio, separated by a comma:
<Button android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintDimensionRatio="H,16:9"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
This will set the height of the button following a 16:9 ratio, while the width of the button will match the
constraints to parent.
Chains
Chains provide group-like behavior in a single axis (horizontally or vertically). The other axis can be constrained
independently.
Creating a chain
A set of widgets are considered a chain if they are linked together via a bi-directional connection (see Fig. 9,
showing a minimal chain, with two widgets).
Fig. 9 - Chain
Chain heads
Chains are controlled by attributes set on the first element of the chain (the "head" of the chain):
For example, on a horizontal chain, if one element defines a right margin of 10dp and the next element defines a
left margin of 5dp, the resulting margin between those two elements is 15dp.
An item plus its margins are considered together when calculating leftover space used by chains to position
items. The leftover space does not contain the margins.
4.2.3 . Building Layouts with an Adapter
When the content for your layout is dynamic or not pre-determined, you can use a layout that subclasses
AdapterView to populate the layout with views at runtime. A subclass of the AdapterView class uses an Adapter
to bind data to its layout. The Adapter behaves as a middleman between the data source and the AdapterView
layout—the Adapter retrieves the data (from a source such as an array or a database query) and converts each
entry into a view that can be added into the AdapterView layout.
1.
We can populate an AdapterView such as ListView or GridView by binding the AdapterView instance to an
Adapter, which retrieves data from an external source and creates a View that represents each data entry.
Android provides several subclasses of Adapter that are useful for retrieving different kinds of data and building
views for an AdapterView. The two most common adapters are:
ArrayAdapter
Use this adapter when your data source is an array. By default, ArrayAdapter creates a view for each array item
by calling toString() on each item and placing the contents in a TextView.
For example, if you have an array of strings you want to display in a ListView, initialize a new ArrayAdapter
using a constructor to specify the layout for each string and the string array:
To customize the appearance of each item you can override the toString() method for the objects in your array.
Or, to create a view for each item that's something other than a TextView (for example, if you want an
ImageView for each array item), extend the ArrayAdapter class and override getView() to return the type of view
you want for each item.
SimpleCursorAdapter
Use this adapter when your data comes from a Cursor. When using SimpleCursorAdapter, you must specify a
layout to use for each row in the Cursor and which columns in the Cursor should be inserted into which views of
the layout. For example, if you want to create a list of people's names and phone numbers, you can perform a
query that returns a Cursor containing a row for each person and columns for the names and numbers. You then
create a string array specifying which columns from the Cursor you want in the layout for each result and an
integer array specifying the corresponding views that each column should be placed:
When you instantiate the SimpleCursorAdapter, pass the layout to use for each result, the Cursor containing the
results, and these two arrays:
If, during the course of your app's life, you change the underlying data that is read by your adapter, you should
call notifyDataSetChanged(). This will notify the attached view that the data has been changed and it should
refresh itself.
You can respond to click events on each item in an AdapterView by implementing the
AdapterView.OnItemClickListener interface. For example:
Android provides the following features to help you build material design apps:
To take advantage of the material features such as styling for standard UI widgets, and to streamline our app's
style definition, apply a material-based theme to your app.
To provide your users a familiar experience, use material's most common UX patterns:
l Promote your UI's main action with a Floating Action Button (FAB).
l Show your brand, navigation, search, and other actions with the App Bar.
l Show and hide your app's navigation with the Navigation Drawer.
l Use one of many other material components for your app layout and navigation, such as collapsing
toolbars, tabs, a bottom nav bar, and more. To see them all, check out the Material Components for
Android catalog
And whenever possible, use predefined material icons. For example, the navigation "menu" button for your
navigation drawer should use the standard "hamburger" icon. You can also import SVG icons from the material
icon library with Android Studio's Vector Asset Studio.
In addition to the X and Y properties, views in Android have a Z property. This new property represents the
elevation of a view, which determines:
l The size of the shadow: views with higher Z values cast bigger shadows.
l The drawing order: views with higher Z values appear on top of other views.
Elevation is often applied when your layout includes a card-based layout, which helps you display important
pieces of information inside cards that provide a material look. You can use the CardView widget to create
cards with a default elevation.
4.3.3 . Animations
The new animation APIs let we create custom animations for touch feedback in UI controls, changes in view
state, and activity transitions.
These APIs let you:
l Respond to touch events in your views with touch feedback animations.
l Animate changes in one or more view properties with view state change animations.
Touch feedback animations are built into several standard views, such as buttons. The new APIs let you
customize these animations and add them to your custom views.
A theme is a type of style that's applied to an entire app, activity, or view hierarchy not just an individual view.
When we apply our style as a theme, every view in the app or activity applies each style attribute that it supports.
Themes can also apply styles to non-view elements, such as the status bar and window background.
Styles and themes are declared in a style resource file in res/values/, usually named styles.xml.
Each attribute specified in the style is applied to that view if the view accepts it. The view simply ignores any
attributes that it does not accept.
Note: Only the element to which you add the style attribute receives those style attributes—any child views do
not apply the styles. If a child views to inherit styles, instead apply the style with the android:theme
attribute.
However, instead of applying a style to individual views, usually apply styles as a theme for the entire app,
activity, or collection of views.
When creating your own styles, you should always extend an existing style from the framework or support
library so that you maintain compatibility with platform UI styles. To extend a style, specify the style to extend
with the parent attribute. Then override the inherited style attributes and add new ones.
For example, inherit the Android platform's default text appearance and modify it as follows:
However, should be always inherit our core app styles from the Android Support Library. The styles in the
support library provide compatibility with Android 4.0 (API level 14) and higher by optimizing each style for the
UI attributes available in each version. The support library styles often have a name similar to the style from the
platform, but with AppCompat included.
And also, inherit styles (except those from the platform) by extending a style's name with a dot notation, instead
of using the parent attribute. That is, prefix the name of a style with the name of the style to inherit, separated by
a period. Usually do this only when extending our own styles, not styles from other libraries. For example, the
following style inherits all styles from the RedText style above and then increases the text size:
Note: If we use the dot notation to extend a style, and also include the parent attribute, then the parent styles
override any styles inheritted through the dot notation.
To find which attributes that declare with an <item> tag, refer to the "XML attributes" table in the various class
references. All views support XML attributes from the base View class, and many views add their own special
attributes. For example, the TextView XML attributes includes the android:inputType attribute that you can apply
to a text view that receives input, such as an EditText widget.
For example, here's how to apply the Android Support Library's material design "dark" theme to the whole app:
And here's how to apply the "light" theme to just one activity:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.course.mycourseone">
<application
...
android:theme="@style/Theme.AppCompat.Light">
...
</application>
</manifest>
Now every view in the app or activity applies the styles defined in the given theme. If a view supports only some
of the attributes declared in the style, then it applies only those attributes and ignores the ones it does not support.
Beginning with Android 5.0 (API level 21) and Android Support Library v22.1, we can also specify the
android:theme attribute to a view in the layout file. This modifies the theme for that view and any child
views, which is useful for altering theme color palettes in a specific portion of the interface. The best way to do
so is to extend these styles from the support library and override some of the attributes.
… />
Android provides a variety of ways to set attributes throughout your Android app. For example, you can set
attributes directly in a layout, you can apply a style to a view, you can apply a theme to a layout, and you can
even set attributes programmatically.
If we have specified the same attributes in multiple places, the list below determines which attributes are
ultimately applied. The list is ordered from highest precedence to lowest:
If we’re trying to style our app and not seeing the results expect, it's likely that other styling is overriding
changes. For example, if we apply a theme to our app, along with a style to an individual View, the style
attributes would override any matching theme attributes for that View. Note, however, that any theme attributes
that aren't overridden by the style are still used.
4.4.5 . TextAppearance
One limitation with styles is that apply only one style to a View. In a TextView, however, specify a
TextAppearance attribute which functions similarly to a style, as shown in the following example:
<TextView
android:id="@+id/showtxt"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="@style/TextAppearance.AppCompat.Body2"
android:text="This text is applied style"
.../>
TextAppearance allows to define text-specific styling while leaving a View’s style available for other uses.
Note, however, that if we define any text attributes directly on the View or in a style, those values would override
the TextAppearance values.
TextAppearance supports a subset of styling attributes that TextView offers. Some common TextView attributes
not included are lineHeight[Multiplier|Extra], lines, breakStrategy, and
hyphenationFrequency. TextAppearance works at the character level and not the paragraph level, so
attributes that affect the entire layout are not supported.
</style>
Notice that the style values are actually references to other Color Resource defined in the project's
res/values/colors.xml file. So that's the file we should edit to change the colors. But before start
changing these colors, preview the colors with the Material Color Tool. This tool helps us pick colors from the
material palette and preview how they'll look in an app.
The layout design can implementable based on them based colours, for example as following design is designed
based on them colour(blue).
And then we can override whatever other styles we want. For example, we can change the activity background
color as follows:
<style name="CustomTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize our theme here. -->
<item name="android:windowBackground">@color/colorAccent</item>
…
</style>
Most attributes are applied to specific types of views, and some apply to all views. However, some theme
attributes listed at R.styleable.Theme apply to the activity window, not the views in the layout. For example,
windowBackground changes the window background and windowEnterTransition defines a transition animation
to use when the activity starts.
The Android Support Library also provides other attributes we can use to customize theme extended from
Theme.AppCompat (such as the colorPrimary attribute shown above).
Note: Attribute names from the support library do not use the android: prefix. That's used only for attributes
from the Android framework.
There are also different themes available from the support library that we might want to extend instead of the
ones shown above. The best place to see the available themes is the library's themes.xml file.
If a new version of Android adds theme attributes that you want to use, you can add them to your theme while
still being compatible with old versions. All you need is another styles.xml file saved in a values directory that
includes the resource version qualifier.
For example:
res/values/styles.xml # themes for all versions
res/values-v21/styles.xml # themes for API level 21+ only
Because the styles in the values/styles.xml file are available for all versions, your themes in
values-v21/styles.xml can inherit them. As such, you can avoid duplicating styles by beginning with a "base"
theme and then extending it in your version-specific styles.
For example, to declare window transitions for Android 5.0 (API level 21) and higher, you need to use some new
attributes. So your base theme in res/values/styles.xml could look like this:
Now you can apply AppTheme in your manifest file and the system selects the styles available for each system
version.
<Button
android:id="@+id/showbtn”
android:textStyle="bold"
style="@style/Widget.AppCompat.Button.Borderless.Colored"
../>
And if you want to apply this style to all buttons, you can declare it in your theme's buttonStyle as follows:
To discover all of the alternative widget styles available from the support library, look at the R.style reference
for fields that begin with Widget. (Ignore the styles that begin with Base_Widget.) Remember to replace all
underscores with periods when using the style name in your resources.
color.xml
<resources>
<color name="colorPrimary">#6200EE</color>
<color name="colorPrimaryDark">#3700B3</color>
<color name="colorAccent">#03DAC5</color>
<color name="first">#FF5722</color>
<color name="second">#64DD17</color>
<color name="third">#0D47A1</color>
</resources>
style.xml
<resources>
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>
<style name="CustomThem" parent="Theme.AppCompat.DayNight.DarkActionBar">
<item name="colorPrimary">@color/first</item>
<item name="colorPrimaryDark">@color/second</item>
<item name="colorAccent">@color/third</item>
<item name="android:navigationBarColor">@color/colorAccent</item>
</style>
<style name="StyleButton">
<item name="android:textSize">24dp</item>
<item name="android:background">@color/colorAccent</item>
<item name="android:textColor">#FFF</item>
<item name="android:padding">12dp</item>
<item name="android:textStyle">bold</item>
</style>
</resources>
mainfast.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.course.samplecode">
<application
...
android:icon="@mipmap/ic_launcher"
android:theme="@style/CustomThem">
<activity android:name=".sample"></activity>
...
</manifest>
4.5 . Add a Floating Action Button
A floating action button (FAB) is a circular button that triggers the primary action in our app's UI. This page
shows you how to add the FAB to your layout, customize some of its appearance, and respond to button taps.
We can configure other FAB properties using either XML attributes or corresponding methods, such as the
following:
l The size of the FAB, using the app:fabSize attribute or the setSize() method.
l The ripple color of the FAB, using the app:rippleColor attribute or the setRippleColor() method.
l The FAB icon, using the android:src attribute or the setImageDrawable() method.
. Chapter Five
5. Android Widgets
5.1 . Introduction
Widgets are an essential aspect of home screen customization. We can imagine them as "at-a-glance" views of an
app's most important data and functionality that is accessible right from the user's home screen. Users can move
widgets across their home screen panels, and, if supported, resize them to tailor the amount of information within
a widget to their preference.
Information widgets typically display a few crucial information elements that are important to a user and track
how that information changes over time. Good examples for information widgets are weather widgets, clock
widgets or sports score trackers. Touching information widgets typically launches the associated app and opens a
detail view of the widget information.
As the name implies, collection widgets specialize in displaying multitude elements of the same type, such as a
collection of pictures from a gallery app, a collection of articles from a news app or a collection of
emails/messages from a communication app. Collection widgets typically focus on two use cases: browsing the
collection, and opening an element of the collection to its detail view for consumption. Collection widgets can
scroll vertically.
The main purpose of a control widget is to display often used functions that the user can trigger right from the
home screen without having to open the app first. Think of them as remote controls for an app. A typical example
of control widgets are music app widgets that allow the user to play, pause or skip music tracks from outside the
actual music app.
Interacting with control widgets may or may not progress to an associated detail view depending on if the control
widget's function generated a data set, such as in the case of a search widget.
While all widgets tend to gravitate towards one of the three types described above, many widgets in reality are
hybrids that combine elements of different types.
For the purpose of widget planning, center our widget around one of the base types and add elements of other
types if needed.
There are given a lot of android widgets with simplified examples such as Button,EditText,
AutoCompleteTextView,ToggleButton,DatePicker,TimePicker,ProgressBar etc.
5.3 . Android Button
A button consists of text or an icon (or both text and an icon) that communicates what action occurs when the
user touches it.
Depending on whether we want a button with text, an icon, or both, we can create the button in our layout in
three ways:
<Button
android:id="@+id/showbtn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_margin="8dp"
android:background="#00BCD4"
android:text="My Button"
android:textStyle="bold"
android:textColor="#FFFFFF"
android:hint="This button is login"
../>
We can also declare the click event handler programmatically rather than in an XML layout. This might be
necessary if we instantiate the Button at runtime or to declare the click behavior in a Fragment subclass.
To declare the event handler programmatically, create an View.OnClickListener object and assign it to the button
by calling setOnClickListener(View.OnClickListener).
For example:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate( savedInstanceState );
setContentView( R.layout.activity_main );
btnshow=(Button) findViewById( R.id.showbtn );
txtshow =(TextView) findViewById( R.id.showtxt );
txtedit=(EditText) findViewById( R.id.edittxt );
btnshow.setOnClickListener( new View.OnClickListener() {
@Override
public void onClick(View view) {
}
} );
The appearance of button (background image and font) may vary from one device to another, because devices
by different manufacturers often have different default styles for input controls.
We can control exactly how our controls are styled using a theme that we apply to our entire application. For
instance, to ensure that all devices running Android 8.0 and higher use the AppThem theme in the app, declare
android:theme="@style/AppTheme" in the manifest's <application> element.
To customize individual buttons with a different background, specify the android:background attribute with a
drawable or color resource. Alternatively, we can apply a style for the button, which works in a manner similar to
HTML styles to define multiple style properties such as the background, font, size, and others.
<Button
....
android:textStyle="bold"
android:textColor="#FFFFFF"
android:background="#00BCD4"
.../>
One design that can be useful is a "borderless" button. Borderless buttons resemble basic buttons except that they
have no borders or background but still change appearance during different states, such as when clicked.
To create a borderless button, apply the borderlessButtonStyle style to the button. For example:
<Button
...
android:textStyle="bold"
style="@style/Widget.AppCompat.Button.Borderless.Colored"
android:textColor="#FFFFFF"
../>
Instead of supplying a simple bitmap or color, however, our background should be a state list resource that
changes appearance depending on the button's current state. We can define the state list in an XML file that
defines three different images or colors to use for the different button states.
1. Create three bitmaps for the button background that represent the default, pressed, and focused button
states.
To ensure that the images fit buttons of various sizes, create the bitmaps as Nine-patch bitmaps.
2. Place the bitmaps into the res/drawable/ directory of our project. Be sure each bitmap is named properly
to reflect the button state that they each represent, such as. button_default.9.png and
button_pressed.9.png.
3. Create a new XML file in the res/drawable/ directory (name it something like
button_custom.xml). Insert the following XML:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/53-reminder"
android:state_pressed="true" />
<item android:drawable="@drawable/button_focused"
android:state_focused="true" />
<item android:drawable="@drawable/button_default" />
</selector>
l This defines a single drawable resource, which will change its image based on the current state of
the button.
l The first <item> defines the bitmap to use when the button is pressed (activated).
l The second <item> defines the bitmap to use when the button is focused (when the button is
highlighted using the trackball or directional pad).
l The third <item> defines the bitmap to use when the button is in the default state (it's neither
pressed nor focused).
Note: The order of the <item> elements is important. When this drawable is referenced, the <item> elements
are traversed in-order to determine which one is appropriate for the current button state. Because the default
bitmap is last, it is only applied when the conditions android:state_pressed and
android:state_focused have both evaluated as false.
This XML file now represents a single drawable resource and when referenced by a Button for its background,
the image displayed will change based on these three states.
l Then simply apply the drawable XML file as the button background:
<Button
android:id="@+id/showbtn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:textSize="20dp"
android:text="@string/btn_show"
android:textColor="#FFFFFF"
android:background="@drawable/custom_button"
../>
5.4 . Checkboxes
Checkboxes allow the user to select one or more options from a set. Typically, you should present each checkbox
option in a vertical list.
To create each checkbox option, create a CheckBox in your layout. Because a set of checkbox options allows the
user to select multiple items, each checkbox is managed separately and you must register a click listener for each
one.
l CheckBox
To define the click event handler for a checkbox, add the android:onClick attribute to the <CheckBox> element
in your XML layout. The value for this attribute must be the name of the method you want to call in response to
a click event. The Activity hosting the layout must then implement the corresponding method.
Within the Activity that hosts this layout, the following method handles the click event for both checkboxes:
The method you declare in the android:onClick attribute must have a signature exactly as shown above.
Specifically, the method must:
l Be public
l Return void
l Define a View as its only parameter (this will be the View that was clicked)
Tip: If you need to change the checkbox state yourself, use the setChecked(boolean) or toggle() method.
To create each radio button option, create a RadioButton in your layout. However, because radio buttons are
mutually exclusive, you must group them together inside a RadioGroup. By grouping them together, the system
ensures that only one radio button can be selected at a time.
Key classes are the following:
l RadioButton
l RadioGroup
Note: The RadioGroup is a subclass of LinearLayout that has a vertical orientation by default.
Within the Activity that hosts this layout, the following method handles the click event for both radio
buttons:
public void onRadioButtonClicked(View view) {
// Is the button now checked?
boolean checked = ((RadioButton) view).isChecked();
// Check which radio button was clicked
switch(view.getId()) {
case R.id.radio_pirates:
if (checked)
// Pirates are the best
break;
case R.id.radio_ninjas:
if (checked)
// Ninjas rule
break;
}
}
The method you declare in the android:onClick attribute must have a signature exactly as shown above.
Specifically, the method must:
l Be public
l Return void
l Define a View as its only parameter (this will be the View that was clicked)
Tip: If you need to change the radio button state yourself, use the setChecked(boolean) or toggle() method.
We can add a basic toggle button to your layout with the ToggleButton object. Android 4.0 (API level 14)
introduces another kind of toggle button called a switch that provides a slider control, which you can add with a
Switch object. SwitchCompat is a version of the Switch widget which runs on devices back to API 7.
If you need to change a button's state yourself, you can use the CompoundButton.setChecked() or
CompoundButton.toggle() method.
l ToggleButton
l Switch
l SwitchCompat
l CompoundButton
To detect when the user activates the button or switch, create an CompoundButton.OnCheckedChangeListener
object and assign it to the button by calling setOnCheckedChangeListener(). For example:
5.7 . Spinners
Spinners provide a quick way to select one value from a set. In the default state, a spinner shows its currently
selected value. Touching the spinner displays a dropdown menu with all other available values, from which the
user can select a new one.
You can add a spinner to your layout with the Spinner object. You should usually do so in your XML layout
with a <Spinner> element. For example:
<Spinner
android:id="@+id/planets_spinner"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
To populate the spinner with a list of choices, you then need to specify a SpinnerAdapter in your
Activity or Fragment source code.
The choices you provide for the spinner can come from any source, but must be provided through an
SpinnerAdapter, such as an ArrayAdapter if the choices are available in an array or a CursorAdapter if the
choices are available from a database query.
For instance, if the available choices for your spinner are pre-determined, you can provide them with a string
array defined in a string resource file:
With an array such as this one, we can use the following code in your Activity or Fragment to supply the
spinner with the array using an instance of ArrayAdapter:
The createFromResource() method allows you to create an ArrayAdapter from the string array. The third
argument for this method is a layout resource that defines how the selected choice appears in the spinner control.
The simple_spinner_item layout is provided by the platform and is the default layout you should use unless you'd
like to define your own layout for the spinner's appearance.
You should then call setDropDownViewResource(int) to specify the layout the adapter should use to display the
list of spinner choices (simple_spinner_dropdown_item is another standard layout defined by the platform).
When the user selects an item from the drop-down, the Spinner object receives an on-item-selected event.
To define the selection event handler for a spinner, implement the AdapterView.OnItemSelectedListener
interface and the corresponding onItemSelected() callback method. For example, here's an implementation of the
interface in an Activity:
5.8 . Pickers
Android provides controls for the user to pick a time or pick a date as ready-to-use dialogs. Each picker provides
controls for selecting each part of the time (hour, minute, AM/PM) or date (month, day, year). Using these
pickers helps ensure that your users can pick a time or date that is valid, formatted correctly, and adjusted to the
user's locale.
We recommend that we use DialogFragment to host each time or date picker. The DialogFragment manages the
dialog lifecycle for you and allows to display the pickers in different layout configurations, such as in a basic
dialog on handsets or as an embedded part of the layout on large screens.
Although DialogFragment was first added to the platform in Android 3.0 (API level 11), if our app supports
versions of Android older than 3.0 even as low as Android 1.6 we can use the DialogFragment class that's
available in the support library for backward compatibility.
Note: The code samples below show how to create dialogs for a time picker and date picker using the support
library APIs for DialogFragment. If our app's minSdkVersion is 11 or higher, we can instead use the platform
version of DialogFragment.
l DatePickerDialog
l TimePickerDialog
To display a TimePickerDialog using DialogFragment, you need to define a fragment class that extends
DialogFragment and return a TimePickerDialog from the fragment's onCreateDialog() method.
Note: If your app supports versions of Android older than 3.0, be sure you've set up your Android project with
the support library as described in Setting Up a Project to Use a Library.
Here's an example:
Now all you need is an event that adds an instance of this fragment to your activity.
Once you've defined a DialogFragment like the one shown above, you can display the time picker by creating an
instance of the DialogFragment and calling show().
For example, here's a button that, when clicked, calls a method to show the dialog:
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/pick_time"
android:onClick="showTimePickerDialog" />
When the user clicks this button, the system calls the following method:
public void showTimePickerDialog(View v) {
DialogFragment newFragment = new TimePickerFragment();
newFragment.show(getSupportFragmentManager(), "timePicker");
}
This method calls show() on a new instance of the DialogFragment defined above. The show() method requires
an instance of FragmentManager and a unique tag name for the fragment.
Caution: If your app supports versions of Android lower than 3.0, be sure that you call
getSupportFragmentManager() to acquire an instance of FragmentManager. Also make sure that your activity
that displays the time picker extends FragmentActivity instead of the standard Activity class.
Creating a DatePickerDialog is just like creating a TimePickerDialog. The only difference is the dialog you
create for the fragment.
To display a DatePickerDialog using DialogFragment, you need to define a fragment class that extends
DialogFragment and return a DatePickerDialog from the fragment's onCreateDialog() method.
Here's an example:
See the DatePickerDialog class for information about the constructor arguments. Now all you need is an event
that adds an instance of this fragment to your activity.
Once you've defined a DialogFragment like the one shown above, you can display the date picker by creating an
instance of the DialogFragment and calling show().
For example, here's a button that, when clicked, calls a method to show the dialog:
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/pick_date"
android:onClick="showDatePickerDialog" />
When the user clicks this button, the system calls the following method:
This method calls show() on a new instance of the DialogFragment defined above. The show() method requires
an instance of FragmentManager and a unique tag name for the fragment.
Android 8.0 introduces the Autofill Framework, which allows users to save data that can be later used to fill out
forms in different apps. Pickers can be useful in autofill scenarios by providing a UI that lets users change the
value of a field that stores date or time data. For example, in a credit card form, a date picker would allow users
to enter or change the expiration date of their credit card.
Because pickers are dialogs, they aren't displayed in an activity along with other fields. To display the picker data
when the picker isn't visible, you can use another view, such as an EditText, which can display the value when
the picker isn't visible.
An EditText object natively expects autofill data of type AUTOFILL_TYPE_TEXT. In contrast, autofill services
should save the data as AUTOFILL_TYPE_DATE to be able to create an appropriate representation of it. To
solve the inconsistency in types, it's recommended that you create a custom view that inherits from EditText and
implements the methods required to correctly handle values of type AUTOFILL_TYPE_DATE.
You should take the following steps to create a subclass of EditText that can handle values of type
AUTOFILL_TYPE_DATE:
5.9 . Tooltips
A tooltip is a small descriptive message that appears near a view when users long press the view or hover their
mouse over it. This is useful when your app uses an icon to represent an action or piece of information to save
space in the layout. This page shows you how to add these tooltips on Android 8.0 (API level 26) and higher.
Some scenarios, such as those in productivity apps, require a descriptive method of communicating ideas and
actions. You can use tooltips to display a descriptive message, as shown in figure 1.
Figure 1. Tooltip displayed in an Android app.
Some standard widgets display tooltips based on the content of the title or content description properties. Starting
in Android 8.0, you can specify the text displayed in the tooltip regardless of the value of other properties.
You can specify the tooltip text in a View by calling the setTooltipText() method. You can set the tooltipText
property using the corresponding XML attribute or API.
To specify the tooltip text in your XML files, set the android:tooltipText attribute, as shown in the following
example:
<android.support.design.widget.FloatingActionButton
android:id="@+id/fab"
android:tooltipText="Send an email" />
To specify the tooltip text in your code, use the setTooltipText(CharSequence) method, as shown in the following
example:
The API also includes a getTooltipText() method that you can use to retrieve the value of the tooltipText
property.
Android displays the value of the tooltipText property when users hover their mouse over the view or long press
the view.
5.10 . Toasts
A toast provides simple feedback about an operation in a small popup. It only fills the amount of space required
for the message and the current activity remains visible and interactive. Toasts automatically disappear after a
timeout.
For example, clicking Send on an email triggers a "Sending message..." toast, as shown in the following screen
capture:
Toasts are not clickable. If user response to a status message is required, consider instead using a Notification.
First, instantiate a Toast object with one of the makeText() methods. This method takes three parameters: the
application Context, the text message, and the duration for the toast. It returns a properly initialized Toast object.
You can display the toast notification with show(), as shown in the following example:
This example demonstrates everything you need for most toast notifications. You should rarely need anything
else. You may, however, want to position the toast differently or even use your own layout instead of a simple
text message. The following sections describe how you can do these things.
You can also chain your methods and avoid holding on to the Toast object, like this:
A standard toast notification appears near the bottom of the screen, centered horizontally. You can change this
position with the setGravity(int, int, int) method. This accepts three parameters: a Gravity constant, an x-position
offset, and a y-position offset.
For example, if you decide that the toast should appear in the top-left corner, you can set the gravity like this:
toast.setGravity(Gravity.TOP|Gravity.LEFT, 0, 0);
If you want to nudge the position to the right, increase the value of the second parameter. To nudge it down,
increase the value of the last parameter.
If a simple text message isn't enough, you can create a customized layout for your toast notification. To create a
custom layout, define a View layout, in XML or in your application code, and pass the root View object to the
setView(View) method.
The following snippet contains a customized layout for a toast notification (saved as layout/custom_toast.xml):
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/custom_toast_container"
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:padding="8dp"
android:background="#DAAA"
>
<ImageView android:src="@drawable/droid"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="8dp"
/>
<TextView android:id="@+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#FFF"
/>
</LinearLayout>
Notice that the ID of the LinearLayout element is "custom_toast_container". You must use this ID and the ID of
the XML layout file "custom_toast" to inflate the layout, as shown here:
First, retrieve the LayoutInflater with getLayoutInflater() (or getSystemService()), and then inflate the layout
from XML using inflate(int, ViewGroup). The first parameter is the layout resource ID and the second is the root
View. You can use this inflated layout to find more View objects in the layout, so now capture and define the
content for the ImageView and TextView elements. Finally, create a new Toast with Toast(Context) and set some
properties of the toast, such as the gravity and duration. Then call setView(View) and pass it the inflated layout.
You can now display the toast with your custom layout by calling show().
Note: Do not use the public constructor for a Toast unless you are going to define the layout with setView(View).
If you do not have a custom layout to use, you must use makeText(Context, int, int) to create the Toast.
Chapter Six
6. Android Projects
6.1 . Meter to Inches
The first project may implements change meters to inches unites that accepts any values from the user. First
create a new project from android studio names MeterToInche as project name inside a package's.
The first things I am going to do is to add a new image to our resource files. So, we are going to go ahead and
open the resource here, and the right-click drawable and then select show as file which allow us directly access
directories then drag from our image directories and drop in drawable file directory(or we can copy and past the
images into our drawable folder in android studio.
After load all necessary images into our working drawable directory then the first things that we have do is
adding ImageView into our layout. And also, make sure all constraint both left,right, top and bottom.
Then
save and
run in
order to
see on
our
emulator
and the
output
will
seems
like the
following pictures.
<ImageView
android:id="@+id/imageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.612"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.016"
app:srcCompat="@drawable/meters_inches" />
Now, we going to add plain text that gives name and put in the center. And also, be empty and add hints to it.
Here, make sure that access number and number of decimal only rather string by set the type number and
numberDecimal to the layout.
<EditText
android:id="@+id/meterEditTextId"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp"
android:ems="10"
android:hint="Enter meters values"
android:inputType="number|numberDecimal"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.578"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/imageView"
app:layout_constraintVertical_bias="0.039" />
Then add button to our layout and make sure nicely centered and change text
from button to converted text.
<Button
android:id="@+id/convertedId"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:background="@color/colorAccent"
android:text="Converted"
android:textColor="#FFF"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.498"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/meterEditTextId"
app:layout_constraintVertical_bias="0.108" />
Lastly , add text view that where going to show the converted values. Here we have initially invisible this
components.
<TextView
android:id="@+id/resultId"
android:layout_width="wrap_content"
android:layout_height="44dp"
android:layout_marginBottom="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.498"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/convertedId"
app:layout_constraintVertical_bias="0.258" />
We have finished the layout, now we are going to implements the java class. To implements the converter we
have to figure out how many inches are there in a meter
package com.course.metertoinche;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate( savedInstanceState );
setContentView( R.layout.activity_main );
}
}
Then inside this file defined all instances like Button,TextFields and EditText instance inside the class.
Then instantiate all instances inside onCreate() mmethods in java class as follows
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate( savedInstanceState );
setContentView( R.layout.activity_main );
enterMeter=(EditText) findViewById( R.id.meterEditTextId );
convertButton=(Button) findViewById( R.id.convertedId );
showResultTxt=(TextView)findViewById( R.id.resultId );
Then we have add onClick Action Listners, the idea here when we have entering a number in EditText then we’ll
able to convert that number by clicking the button and shows inside another TextViews.
And also, we can actually use a string format function that will help us round our decimal point instead of having
a bunch of them we can just round up to two decimal points.
The string format function is String.format( "%.2f",convertdValues). Here, the first parameter is we are going to
convert this into two decimal points. And the next parameter is we have to pass is the actual argument or the
result.
In this application, going to develop an application change different colors when we click the button and we call
the button ‘try me!’. So, go head and create a new project in android studio. In order to develop the apps we have
add the images that we need in our button as background into drawable folders inside android studio.
Firstly, we care about background which means we need to figure out how access our background because in our
project change the background color randomly. So, to access the background firstly we have to set an ID to the
view. Here, we going to set windowsViewID as an id.
<Button
android:id="@+id/tryMeButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/round_b"
android:text="@string/try_me"
android:textColor="#FFF"
android:textSize="18sp"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
Now, we have finished the layout for our apps. The next step is implements the internal details in java program.
So, we going to MianActivity.java file. Inside java class firstly we want to declare the View and Button instances
as follows:
Then, instantiate all instances inside onCreate() methods in the class as follows:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate( savedInstanceState );
setContentView( R.layout.activity_main );
windowsView=findViewById( R.id.windowsViewID );
tryMeButton=(Button)findViewById( R.id.tryMeButton );
}
In in the first Instantiate no need to cast because windowsView instance is Instantiate View and
everything considers as View in the Layout. But the next Button class should be converted into Button for
instantiate from View initially.
After Instantiate the next step is, implements onclick listener, here we are going to implements our logic that will be called
once we tap on the button as follows:
}
} );
First let us try by Log.d in dubeging. So, there is a class Log class that allow us to see what our apps doing on
consule. The user canot able to see this log but the developers need to be able to see the debug or see things or
test things out in the applications when we are building them. We are going to head here and say Log.d where
Log is a class and d means debug and passing two parameter as follows:
Now, we going to save and run to check our applications is succesfully build or not. When the following message
seen in the console when we click the button our apps working well.
But, our aim is changing background colors dynamically. So, we want to randomly generate a number that would
correspond to a certain color because in order to change the background color we have used setBackground
methods and this method accepts Integer values. Hence , user array to store many items that have the same type.
Here, we have declare integer array that holds Integer values and we need name it colors inside java class.
Let’s go ahead and create the actual array so we are going to say colors is equal to New int[] it is very interesting
notation for instantiate a new array of Integer and also inserting items into an array. Hence, all items are colors.
colors=new int[]
{Color.GRAY,Color.GREEN,Color.BLUE,Color.BLACK,Color.WHITE,Color.YELLOW
Color.CYAN,Color.MAGENTA,Color.TRANSPARENT,Color.LTGRAY};
After here, they need figure out away to create random numbers so there is a class called Random class that will
help as to randomly choose colors from an array colors. The class created inside onclick() methods because it
happens when we taped the button.
And now, we are going to go head and create the actual random number at a given time. So, we need to create
integer variable that holds random numbers at a time. Hence, to assign a value to a variable we use random
objects random and invoke into nextInt() methods to start random numbers. In the method pass the length of
colors array because we creating random numbers the range is between 0 and 100 or 0 to 300 or 0 to 5. In our
case we want to make sure each time that we tap the button we get a number that is between 0 and length of
colors array and never go beyond the length of array.
int colorsArrayLength=colors.length;
Random random=new Random( );
int colorsIndex=random.nextInt(colorsArrayLength);
Finally, to access each colors from colors array through the index number and set to the background in order to
change.
windowsView.setBackgroundColor(colors[colorsIndex] );
package com.course.changerandomcolor;
import android.graphics.Color;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import java.util.Random;
public class MainActivity extends AppCompatActivity {
private View windowsView;
private Button tryMeButton;
private int[] colors;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate( savedInstanceState );
setContentView( R.layout.activity_main );
colors=new int[]{Color.GRAY,Color.GREEN,Color.BLUE,Color.BLACK, Color.WHITE, +
Color.YELLOW,Color.CYAN,Color.MAGENTA,Color.TRANSPARENT,Color.LTGRAY};
windowsView=findViewById( R.id.windowsViewID );
tryMeButton=(Button)findViewById( R.id.tryMeButton );
tryMeButton.setOnClickListener( new View.OnClickListener() {
@Override
public void onClick(View view) {
int colorsArrayLength=colors.length;
Random random=new Random( );
int colorsIndex=random.nextInt(colorsArrayLength);
windowsView.setBackgroundColor(colors[colorsIndex] );
}
} );
}}
Widgets in general view like Button,Text view ,Radio button and so on. The next widget we are doing an apps
going to be Radio Button. Now, create a new project and call it RadioButtonApps in Android Studio. So, once
we have open the activity xml to desing the apps and get Radio and RadioGroup from the plate. Radio button's
is just button that allows the user turn it on/off and usually use in RadioGroup because the idea is allow the user
select only one option from a certain group. Since, RadioGroup that will host the Radio buttons up very easily.
So , firstly add Radio button group from the plate to our layout before Radio Button and make sure it in the
middle.
<RadioGroup
android:id="@+id/radioGroupId"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.498">
Now, once we have RadioGroup we can add Radio button into our Radio Groups. Hence, puts all radio buttons
inside our Radio group that have the same regularly a group. Note, our Android studio here just give all radio
buttons random names can change that no problem. So, now change Radio buttons name and gives different ID
in Attributes or in text tag in xml. For the name of each Radio button let’s say the first Yes, the next we are going
say may be and the lat say No as option. Notice that radio button allow the selection of one option.
<RadioButton
android:id="@+id/noId"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="No" />
<RadioButton
android:id="@+id/maybeId"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="May Be" />
Now, we have radio button(yes, maybe and no) options lets us put the textField
which is display which button is click. So, add TextView from plate or write
the code in xml layout file at the top of our radio group. In fact, change a few things to make it a little bit bigger
and change the font. Since, let’s set 24sp Textsize, attending? as text name and give an id to this component.
Now, what are going to do our main activity the java class. Firstly, we want to declare all instances by private
access modifier and first we are going to connect radioGroup and then for radio buttons. Here, the radio group
are parent container for our Radio buttons.
private RadioGroup radioGroup;
private RadioButton radioButton;
private TextView attendTextView;
Now, we have Radio group which contains all of our radio button what do we need to do now is to connect this
radio group to an event listener because the idea is to have user click on the buttons. So what we say invoking to
setOnChekedChangeListner() methods and pass new radio group that actually read group that own check to
listner. Hence, the method they are using here instantiate on checked listener and inside here we have to override
the on checked passes in the group as follows.
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate( savedInstanceState );
setContentView( R.layout.activity_main );
radioGroup=(RadioGroup)findViewById( R.id.radioGroupId );
}
} );
Now, just check the ID here contains the ID of each buttons or each radio buttons so that we can handle
everything inside of our own check to change method. Here,we can access to the group and also we have access
to the IDs of each items inside of our Group. So, inside onCheckedChanged() methods we are going to instantiate
the radio button and since we know that we have the IDs inside the methods being passed and find one ID that
would just take care of one button which is not what we want. So, we are going to dynamically pass this
checked ID which this checkedID contain the IDs of each of the selected or clicked radio button.
Now, since we have more thane one radio buttons we need to find a way to go through and check which button
has been selected. And the easiest wey is use switch statement that allows us switch through our choice and the
switch statement used IDs of each radio button. And inside the block use case which going to done we have here
radio that get IDs are getting of each button that its being clicked or tapped and then we were checked. And
lastly set to Text View which button is selected.
radioButton=(RadioButton)findViewById( checkedID );
switch (radioButton.getId()){
case R.id.yesID:
attendTextView.append( "Yes!!" );
break;
case R.id.maybeId:
if (radioButton.isChecked()) {
attendTextView.append( "May be!!" );
}
break;
case R.id.noId:
attendTextView.setText( " Nope" );
break;
default:
attendTextView.setText( "plaese select button!!" );
}
package com.course.radiobuttonapps;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.Switch;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
private RadioGroup radioGroup;
private RadioButton radioButton;
private TextView attendTextView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate( savedInstanceState );
setContentView( R.layout.activity_main );
radioGroup=(RadioGroup)findViewById( R.id.radioGroupId );
attendTextView=(TextView)findViewById( R.id.attendtextViewId );
radioGroup.setOnCheckedChangeListener( new
RadioGroup.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(RadioGroup radioGroup, int checkedID) {
radioButton=(RadioButton)findViewById( checkedID );
switch (radioButton.getId()){
case R.id.yesID:
attendTextView.setText( "Attending? Yes!!" );
break;
case R.id.maybeId:
if (radioButton.isChecked()) {
attendTextView.append( "Attending? May be!!" );
}
break;
case R.id.noId:
attendTextView.setText( " Attending? Nope" );
break;
default:
attendTextView.setText( "plaese select button!!" );
}
}
} );
}
}
6.4 . Android SeekBar
Now, we going to discussed about another widget called seekbar a snack bar which means when drag our finger
gets some values. Now, we going to create an applications will essentially allow users to tell us what is their
plane scale of 1 to 10. So firstly, we going to add seekbar from plate in android studio IDE and make sure put in
center. And also, we want to change its property and turns out to seekbar has maximum progress until that we
can set. Here, first change the maximum by tap maximum from property or attribute and set as we want but in
our case we set 10 that our max progress level( android:max="10"). And next, lets a little bit bigger so what we
going to do into the layout and set "fill_parent" in android:layout_width want to emphasize the seek bar as
follows:
android:layout_width="fill_parent"
Then add two TextView which going to gives some information and put a level when the user select their pain
level from the seek bar. And also, what going to happen is as we move left to right or the vise verse we will
updating the text in the text field. In the second TextField change the text to empty and litle bit bigger like 20sp
text size.
<TextView
android:id="@+id/painlevelID"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="20sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/seekBar" />
Next, we are going to be implement our main activity java class. Firstly, we are going to connect our widgets.
private SeekBar seekBar;
private TextView resultPainlevel;
seekBar=(SeekBar)findViewById( R.id.seekBarID );
resultPainlevel=(TextView)findViewById( R.id.painlevelID );
Then the next thing we need to do attached the seekbar to an event listener because we want to make sure that we
can track and catch all of those event listener as they drags the seek bar left to right as follows:
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
}} );
In the above code the first method is onProgressChanged() which passes our seek bar, progress and fromuser
parameters/arguments. And also both onStartTrackingTouch() and onStopTrackingTouch() takes Seekbar
arguments. So, all of these methods they catch the events at different phase as the users go through the process of
dragging the seek bar.
As example, we going to use logs what we see in which what’s happening. So, here write Log class with
debugging methods and pass tag and message string values as we want that describes our works. And copy to all
methods inside the seek bar listener.
And run the program going to see a certain events in seek listener when our program is build successfully the
following output is display on the console when we drag seek bar. When we touched earlier it says on start
tracking and on stop go back and show everything what progess go on
D/SeekBar: on progeress seek bar
Now, we are going to show our result from seek bar to text view the
progress that has been made. So, we are going to now replace log debugging by the actual implementation.
Since, set the text that gets from seek bar progress what doing here seek bar is being passed on our
onProgressChange in the first parameter so able to get this object seek bar that instantiate before and getProgess
return a number from seek bar we are drag the progress bar. And also, the next method getMax() returns the
maximum numbers that we sets to our seek bar
Finally, let’s run the apps and make sure that in the beginning our level say zero out of 10 so the users at least
have a way of saying oh this is where our pain level is going to show they just user interface. Here, we going to
set a text to our result text view out of Listener events which will see pain level 0 out of 10 initially and the
moment we go a head start moving in seek bar it changes as follows:
package com.course.seekbar;
import androidx.appcompat.app.AppCompatActivity;
import android.graphics.Color;
import android.os.Bundle;
import android.util.Log;
import android.widget.SeekBar;
import android.widget.TextView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate( savedInstanceState );
setContentView( R.layout.activity_main );
seekBar=(SeekBar)findViewById( R.id.seekBarID );
resultPainlevel=(TextView)findViewById( R.id.painlevelID );
resultPainlevel.setText( "Pain level:"+seekBar.getProgress()+"/"+seekBar.getMax() );
seekBar.setOnSeekBarChangeListener( new SeekBar.OnSeekBarChangeListener() {
@Override
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
Log.d("Seek Bar","on stark tracking");
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
if(seekBar.getProgress()>=6){
resultPainlevel.setTextColor( Color.RED );
}else{
resultPainlevel.setTextColor( Color.BLACK );
}
}
} );
}}
6.5 . Android
Toggle Button
The Toggle button allows the user to change setting between two state on and off. Here, let’s going to create an
apps for toggle button so first goes to plate in the layout and find Toggle Button then add to screen design layout.
And also, make sure put in the middle and change the id and names in the property for better design.
<ToggleButton
android:id="@+id/toggleButtonID"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="ToggleButton"
android:textColor="@color/textColor"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
Now, we are going to add a text view which used to shows which state is selected by the users which means is on
or off taped by the user.
<TextView
android:id="@+id/showId"
android:text="@string/show_togle"
android:textStyle="bold"
app:layout_constraintTop_toBottomOf="@+id/toggleButtonID"
app:layout_constraintVertical_bias="0.266"
android:visibility="invisible"
... />
Now everything are in place so we going to implements the main activity java class. So, start with create all
instances inside the java class and instantiate them inside onCreate() methods as follows:
Then first set the visibility for text view initially invisible and change through the toggle button what they wants.
Because we wants to make sure that its when the application opens that first time its going to be invisible. Then,
we going to connect our toggle button to any event listener.
Now, inside the listener add if—else because we know the state its being sent.
if (isChecked){
//the toggle is enabled
}else{
//the toggle is disabled
}
So, what we want to do when its cheked or not? Well we want to show the text view to our user right now that
have some text and initially not showing because the visibility it is invisible. Here, our idea is how to make
visible when the user taps on state in the toggle button or disabled the text view when the toggle button become
off state. Hence, use showshideTxt.setVisibility( View.VISIBLE ); statements because its a view so we have pass
View class and Visible objects to show a text view is called showshideTxt.
if (isChecked){
showshideTxt.setVisibility( View.VISIBLE );
}else{
showshideTxt.setVisibility( View.INVISIBLE );
}
package com.course.toggelbutton;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.CompoundButton;
import android.widget.TextView;
import android.widget.ToggleButton;
super.onCreate( savedInstanceState );
setContentView( R.layout.activity_main );
toggleButton=(ToggleButton)findViewById( R.id.toggleButtonID );
showshideTxt=(TextView)findViewById( R.id.showId );
toggleButton.setOnCheckedChangeListener( new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton compoundButton, boolean isChecked) {
if (isChecked){
//the toggle is enabled
showshideTxt.setVisibility( View.VISIBLE );
}else{
//the toggle is disabled
showshideTxt.setVisibility( View.INVISIBLE );
}
}
} );
<CheckBox
android:id="@+id/momID"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/mom"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.498"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.283" />
<CheckBox
android:id="@+id/fatherID"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/father"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.519"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/momID"
app:layout_constraintVertical_bias="0.068" />
<CheckBox
android:id="@+id/brosisID"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/brother_sister"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.594"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/fatherID"
app:layout_constraintVertical_bias="0.106" />
And add another Button and Text view to show whatever users have selected or haven’t selected at least the state
of our checkbox. So, let’s put the button and text view on the bottom of all checkboxs and let’s give showBtnID
and resultID respectively as its id and change the name as follows the following xml layout.
<Button
android:id="@+id/showBtnID"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="32dp"
android:background="#00BCD4"
android:textColor="#FFF"
android:textSize="20sp"
android:text="@string/showBtn"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.489"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/brosisID" />
<TextView
android:id="@+id/resultID"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="TextView"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.498"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/showBtnID"
app:layout_constraintVertical_bias="0.347" />
Now, our design was set as well so let’s going to back main activity java class for implements when the user
check or not check states. Here, firstly we have to connect all instances and instantiate them in the class and
onCreate() methods respectively.
momcheckbox=(CheckBox)findViewById( R.id.momID );
...
Next things, we need to do now is connect show button to an event listener. Because a set of checkbox options
allows the user to select multiple items, each checkbox is managed separately and we must register a click
listener for each one.
Then, the idea here is make sure when they select mom,father or brother/sister form the options or all of them
and show the states of each checkbox on our text view. Here, checkbox allows to state check or unchecked so
how do we then get those state the checkbox object has all of those properties that we can tap into it. Since, what
we going to do is we have wonderful class is called StringBuilder which construct a string put a string in a buffer
many strings and then set into text view.
And then, by using the StringBuilder object is called stringBuilder adding all states of each checkbox to our string
builder by using append() methods and passing each checkbox text and states as a string as follows:
So, now we have a string builder built all of the status of all of our checkboxes and we can show all of these
status to our text view(resulttTxt) as follows:
resulttTxt.setText( stringBuilder )
Now, we have finished everything and the the full java code looks and what will be the output in android
emulator as follows:
package com.course.checkboxapps;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
private CheckBox momcheckbox;
private CheckBox fathecheckbox;
private CheckBox brosischeckbox;
private Button showBtn;
private TextView resulttTxt;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate( savedInstanceState );
setContentView( R.layout.activity_main );
momcheckbox=(CheckBox)findViewById( R.id.momID );
fathecheckbox=(CheckBox)findViewById( R.id.fatherID );
brosischeckbox=(CheckBox)findViewById( R.id.brosisID );
resulttTxt=(TextView)findViewById( R.id.resultID );
showBtn=(Button)findViewById( R.id.showBtnID );
showBtn.setOnClickListener( new View.OnClickListener() {
@Override
public void onClick(View view) {
StringBuilder stringBuilder=new StringBuilder( );
stringBuilder.append( momcheckbox.getText().toString()+" status is:"+momcheckbox.isChecked()+"\
n" );
stringBuilder.append( fathecheckbox.getText().toString()+" status is:"+fathecheckbox.isChecked()+"\
n" );
stringBuilder.append( brosischeckbox.getText().toString()+" status is:"+brosischeckbox.isChecked()
+"\n" );
resulttTxt.setText( stringBuilder );
}
} );
}
}
6.7 . Android Alert Dialog
The dialog is a small window that prompts the user to make a decision or enter additional information. So one of
the thing that we need to realize here is that dialog does not necessary feel the screen and normally used events
that required to take an action before they can proceed. Since, let’s going to do simple apps that shows dialog. So
the first things going to add button and change some property/attributes like id, size and text name. And the xml
layout as follows:
<Button
android:id="@+id/showDialogID"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#03A9F4"
android:text="@string/dialog_btn"
android:textColor="#FFF"
android:textSize="24sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
After create our button the next thing is going to create the alert dialog class and builder because the builder class
will have all the functionalities that we need to create alert dialog and we going to call this alerDialog.
Then, we instantiate or build up alert dialog when the user clicked or tapped the button. So create event listener
to create the actual Dialog here and instantiate the alartBuilder objects inside the listener events as follows:
alertBuilder=new AlertDialog.Builder(MainActivity.this);
Here, we instantiate the Alert Builder we going to be pass the context which is our main activity because we need
displayed our alert dialog in this context means going to be a part of MainActivity so we’ll have this context will
have a home.
The next things we need to do once when we have instantiated alart dialog we can start our alert dialog so we can
things up. The first things set up the title by setTitle() methods and pass title string. When we have string
resource in string.xml we going to access the string values from string resources is as follows:
alartDialog.setTitle( R.string.alert_title ); or
alartDialog.setTitle( getResources().getString( R.string.alert_title ) );
When we want to add an icons to our alert dialog in its title or message we can use as follows:
alertDialog.setIcon( android.R.drawable.alert_light_frame );
After setting the title for alert dialog the second step is setting the message to it as follows:
After setting tiles and message of our alert dialog the next step we are going to do is set cancelable which does
the system that the users must select an option before they can cancel out of that window. So, what we do was
say alert dialog that said cancelable and we have to pass in something true/false. When we set false the user will
not be able to cancel the dialog. Which means they have to choose yes or no in order to remove the dialog from
our application.
alertDialog.setCancelable( false );
The next step after set the cancelable states we going to set the buttons. In order to set buttons we going to set
positive and negative buttons use setPositiveButtons() and setNegativeButtons() methods respectively. And also,
pass in the first argument, a String which is a button name and in the second argument pass event listener when
the user’s click the button what will happen as second argument. Here, where we will put whatever needs to
happen when they yes for positive button as follows:
Next, set negative buttons which is No. Here, we going to cancel our dialog only rather than our application
when the user clicked on No buttons in the alert dialog.
Now, we constructing our alert dialog by setting titles, message inside the dialog and all buttons that appears in
our alert dialog. So, the next step is creating the actual dialog because remember our alert dialog is builder type
that builder will build the dialog.
AlertDialog dialog=alertDialog.create();
Now, we have actually created the dialog which we’ll be using show on the screen by using show() methods of
created dialog.
AlertDialog dialog=alertDialog.create();
dialog.show();
package com.course.alertdialogs;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import android.content.DialogInterface;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
public class MainActivity extends AppCompatActivity {
private AlertDialog.Builder alertDialog;
private Button showAlert;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate( savedInstanceState );
setContentView( R.layout.activity_main );
showAlert=(Button)findViewById( R.id.showalertBtnID );
showAlert.setOnClickListener( new View.OnClickListener() {
@Override
public void onClick(View view) {
//show the actual dialog(alert dialog
alertDialog=new AlertDialog.Builder(MainActivity.this);
//setting up-set title
// alartDialog.setTitle( R.string.alert_title );
alertDialog.setTitle( getResources().getString( R.string.alert_title ) );
alertDialog.setIcon( android.R.drawable.ic_btn_speak_now);
//setting the message
alertDialog.setMessage( getResources().getString( R.string.alert_message ) );
//set cancelable
alertDialog.setCancelable( false );
//set button
alertDialog.setPositiveButton( getResources().getString( R.string.alert_yes ), new
DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int which) {
//Exit our window activity
MainActivity.this.finish();
}
} );
//set negative button
alertDialog.setNegativeButton( getResources().getString( R.string.alert_no ), new
DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
dialogInterface.cancel();
}
} );
//creating the actual dialog
AlertDialog dialog=alertDialog.create();
dialog.show();
}
} );
}
}
And also after adding Text view we going to add seek bar for set the tip percentages and buttons in order to
calculate a given tip after takes the amount and tip percentage and also add text view to show us the result that
calculated tip amount with a given percentage. Then let us look xml layouts and seen in our device looks as
follows:
<EditText
android:id="@+id/tipAmountId"
android:layout_width="277dp"
android:layout_height="39dp"
android:ems="10"
android:hint="@string/tipamount_hint"
android:inputType="number|numberDecimal"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.37"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.168" />
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/tip_percentage"
android:textSize="20sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.18"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tipAmountId"
app:layout_constraintVertical_bias="0.038" />
<SeekBar
android:id="@+id/percentSeekBarId"
android:layout_width="277dp"
android:layout_height="wrap_content"
android:max="35"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.373"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView"
app:layout_constraintVertical_bias="0.051" />
<Button
android:id="@+id/calculatBtnId"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#009688"
android:text="@string/cal_button"
android:textColor="#FFFFFF"
android:textSize="20sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/seekBarTextViewId"
app:layout_constraintVertical_bias="0.383" />
<TextView
android:id="@+id/resultTipId"
android:layout_width="113dp"
android:layout_height="36dp"
android:textSize="24sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.442"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/calculatBtnId"
app:layout_constraintVertical_bias="0.132" />
<TextView
android:id="@+id/seekBarTextViewId"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="60dp"
android:textColor="#3F51B5"
android:textSize="18sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.04"
app:layout_constraintStart_toEndOf="@+id/percentSeekBarId"
app:layout_constraintTop_toBottomOf="@+id/textView"
app:layout_constraintVertical_bias="0.042" />
</androidx.constraintlayout.widget.ConstraintLayout>
Now, we have constructed user interface pretty much done so that we have that setup we are going to be write a
java code in main activity. And we want to implement onclick listener on MainActivity class as follows:
@Override
public void onClick(View view) {
And also, we have create all instance variable such as Edit text, seek bar, button and text view inside the class as
follows:
seekBartextView=(TextView)findViewById( R.id.textViewseekbarId );
}
Now, let us setup our button to action listener events which registering our calculate button that it has an event
listener attached in this context.
calculateTip.setOnClickListener( this );
Also, we have setup seek bar listener for get the percentage values when we slide left to right the slider or seek
bar and display on Text view is called seekBarText view instance.
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
} );
Here, we have different methods when we implements seek bar on change listener. Hence, the first method is
called onProgressChange() method with have three arguments i.e Seek Bar object, integer progress and
fromUser Boolean values. Since, it shows exactly how things are happening right so we can go and fetch the
progress number from the slider. And the next method is called onStartTrackingTouch() that have seek bar
object where can fetch when the tracking touch begins and the last method called onStopTrackingTouch() where
we get when the user has to left/release the finger off our application screen it is the best place to get the final
value that the users have selected. Then implement inside the method, first lets us show percentage when the
users slide on seek bar inside onProgressChange() method as follows:
Now, we’re able to see percentage and can get seek bar progress values. Since, let’s store the seek bar values to a
variable in order to use the values later. So, first declare the variable as instance variable called
seekbarPercentage with integer type to store the percentage that the user are selecting from sliding on seek bar.
Also, assign a value to declared instance variable inside onStopTrackingTouch() methods because this is the final
place that when the users have stopped tracking or the system has stopped tracking the touch which means that
we’ll get the correct percentage. The implementation of the method as follows:
And also, create another instance variable which is going to be hold the amount that the users have enterned on
in our edit text and let’s called enteredBill with float type. Also, create a method is called calculate() to perform
a given expression that calculates the tip amount with a given percentage.
}
Now, everything are working well but let’s add one thing that will add the tip amount to the bill amount in order
to calculate how much their total amount bill that payed to someone that gives services. So what we can do, sum
up the entered values from edit text and the calculated tip bills together and shows in totalBill text view.
Then invoke the methods inside on click listener because this method perform when the users clicked or taped on
calculate button.
Wow congratulation now we have simple and working android apps and the full java class and last layout seems
like as follows:
package com.course.tipcalculator;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.SeekBar;
import android.widget.TextView;
import android.widget.Toast;
calculateTip.setOnClickListener( this );
. Chapter
Seven
7. Android Activities
1.1. What is Activity
In case of Android Views, Layouts and View Groups are used to design the user interface, which is the physical
appearance of our App that the user interact with. But what is the mind or soul or conscious of our App? Yes, it is
the Activity. Activity is nothing but a java class in Android which has some pre-defined functions which are
triggered at different App states, which we can override to perform anything we want. Activity class provides us
with empty functions allowing us to be the controller of everything.
Activity In Android:
l Android system initiates its program with in an activities starting with a call onCreate() call-back method.
l There is a sequence of call-back methods that start up an activity.
l There can be more then 1 activity in application.
l Any one of them will be defined as main activity.
An activity is a window representation or a single screen which means when we create a project we have main
activity java class and we have xml layout that user interface the user can interact with. Let use see our main
activity class.
Here, our MainActivity is a derived class that extends from AppCompatActivity which is a base activity that our
application is compatible with different device activities. Activity can be started by sending an intent to android
system which in turn uses manifest files to start right activity from right app.
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate( savedInstanceState );
setContentView( R.layout.activity_main );
Here, after extends an Activity we have onCreate() override methods which is very important method and we
are override onCreate() methods that comes Activity base class for instantiate or set up our activity properly.
And also, we passing Bundle savedInstanceState which what happening here is that this Bundle class hold all of
the states, all of the methods, all of the instances or all of the properties of the particular activity screen which
call saveInstancestate then we have call to our super class on the base class that we derived from and pass our
instance state to the base class.
super.onCreate( savedInstanceState );
So, all of the instance states of our activity are being saved here from main activity which is parent activity. And
then, we have another method is called setContentView which is doing just going back to the layout resources like
our screen xml visual representation layout.
Let’s create each activity states in java program and implements Toast text message which just a small message
that create on our application that just popups and fade out on our android screen. So, use Toast class and invoke
makeText() methods, here pass context, message text and duration as first, second and third parameters
respectively then use show() method that needs to show on our screen. Let’s see the full different state as
follows:
import android.os.Bundle;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate( savedInstanceState );
setContentView( R.layout.activity_main );
Toast.makeText(this,"Oncreate() invoked" ,Toast.LENGTH_LONG).show();
}
@Override
protected void onStart() {
super.onStart();
Toast.makeText(this,"Onstart() invoked" ,Toast.LENGTH_LONG).show();
}
@Override
protected void onResume() {
super.onResume();
Toast.makeText(this,"onResum() invoked" ,Toast.LENGTH_LONG).show();
}
@Override
protected void onPause() {
super.onPause();
Toast.makeText(this,"Onpause() invoked" ,Toast.LENGTH_LONG).show();
}
@Override
protected void onRestart() {
super.onRestart();
Toast.makeText(this,"OnRestart() invoked" ,Toast.LENGTH_LONG).show();
}
@Override
protected void onStop() {
super.onStop();
Toast.makeText(this,"onstop() invoked" ,Toast.LENGTH_LONG).show();
}
@Override
protected void onDestroy() {
super.onDestroy();
Toast.makeText(this,"onDestroy() invoked" ,Toast.LENGTH_LONG).show();
}
}
Initially, on create method called that why we setup everything here then automatically goes to onStart() methods
everything is ready. And then onResume() method invoked and our app alive on our screen. As the user begins to
leave the activity, the system calls methods to dismantle the activity. In some cases, this dismantlement is only
partial; the activity still resides in memory (such as when the user switches to another app), and can still come
back to the foreground. If the user returns to that activity, the activity resumes from where the user left off. The
system’s likelihood of killing a given process along with the activities in it depends on the state of the activity at
the time.
And then, we going to implement the intent object which means intent in an android is an action what we need to
be and what is it that the application wants to do. So, our intent here the action that we need is to go from first
activity to second activity. That means we have to instantiate that the class and set everything up for us to
actually go to the next activity that we want go to. Let’s create and instantiate an Intent as follows:
Intent myIntent=new Intent(MainActivity.this,SecondActivity.class );
So, we are creating Intent object and passing two parameters to the constructor, the first parameter is the name of
the activity that we are currently located that is MainActivity and the second is what will be go to which activity
is called SecondActivity class. Finaly, we have to start the activity by passing our intent object is called myIntent
as follows:
Intent myIntent=new Intent(MainActivity.this,SecondActivity.class );
startActivity(myIntent);
or
We can used anonymous Intent object that navgates between activities and pass in startActivity method as
follows.
startActivity( new Intent( MainActivity.this, SecondActivity.class ) );
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate( savedInstanceState );
setContentView( R.layout.activity_main );
gotoNext=(Button) findViewById( R.id.showActivityID );
gotoNext.setOnClickListener( new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent myIntent=new Intent(MainActivity.this,SecondActivity.class );
startActivity( myIntent );
}
} );
}
Now, we can navigate from the first activity to another second activity.
So, we keep put all attached messages we want to be attached before that intent is sent which means it will
collect all of the message that we attached that we have to put into our intent and will be available in the second
intent at which point we can then parse or get all of that information.
Now, we going to implements on the second activity, we able to extract those messages that we have sent. The
idea here, display the message that we were receiving from our intent in text view automatically. So, we have
first ready our text view for receiving string that sending from our first activity. Then, use Bundle class that
collects all messages that send from first activity as follows:
Bundle extra=getIntent().getExtras();
Here,we are saying that we have the intent we need to go and get the extras that puts the message by putExtras()
methods in the first activity. And then check the whether our extra intent is empty or not and also assign all
recived intent values into a variable. And inorder to retrive the intantes we have to knows about the types of
values/message that sent from other activity which means when the message String type we can get using
getString() method or its integer using getInt() and so on as follows:
if (extras!=null){
String name=extras.getString( "name" );
String sex=extras.getString( "sex" );
int age=extras.getInt( "age" );
} .
Finally, after received all intent message now we can set to our text view in order to display or we can used for
other purpose as we want.
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate( savedInstanceState );
setContentView( R.layout.activity_main );
gotoNext=(Button) findViewById( R.id.showActivityID );
gotoNext.setOnClickListener( new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent=new Intent(MainActivity.this,SecondActivity.class );
intent.putExtra( "name","Aster Awoke" );
intent.putExtra("sex","Female" );
intent.putExtra( "age",50 );
startActivity( intent );
//startActivity( new Intent( MainActivity.this,SecondActivity.class ) );
}
} );
}
}
And, SecondActivity.java or the second activity which is received messages from other intents
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate( savedInstanceState );
setContentView( R.layout.activity_second);
Bundle extras=getIntent().getExtras();
showIntent=(TextView)findViewById( R.id.showIntentID );
if (extras!=null){
String name=extras.getString( "name" );
String sex=extras.getString( "sex" );
int age=extras.getInt( "age" );
showIntent.setText( "Name:"+name+" \n Sex:"+sex+"\n Age:"+String.valueOf( age) );
}else{
showIntent.setText( "we can't sent any values " );
}
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult( requestCode, resultCode, data );
}
Here, the first parameter is called requestcode integer type is any code or integer number that we can create and
will specify which activity or which message that we are trying to catch and display. And the second parameter
the resultCode is going to be code or it’s allow us to know what kind of result are we where expecting. The third
parameter is called Intent data is the main one that the intent data of request code. Let’s declare and assign for
request code as follows:
Hence, got to second ctivity and we going to add button and name as “back to ” taped or clicked to send
massages to back first activity. The idea is clicked on the button sent attributes to the next activity called first
activity. Now we are going to set up all things in the second activities so we are going to create a private button
field and names gobackTo. Finally setup the buttons action listener as follows:
}
} );
The idea here is when we tapped or clicked on the button in second activity we want to sent messages back to
first activity from the current called second activity. And then, we going to implement the intent object. So, our
intent here the action that we need is to go from second activity back to first activity.
public void onClick(View view) {
Intent returnIntent=getIntent();
returnIntent.putExtra( "returnData","From second activity" );
setResult( RESULT_OK, returnIntent);
finish();
}
Here, there is setResult() methods that we need is the one that has the result code that we can used to the intent
data we will have to pass. So, in the method, the first parameter RESULT_OK is just an internal code that will tell
the system that this result is all good and we can send this and it will be received on the next or previous activity.
And the next parameter is the intent data.
Now, we going back to first activity to be able to retrieve the data that passed from the second activity.
Remember, we are not retrive on onCreate() method rather we retrieve on onActivityresult() methods that why
we have expecting result from our previous or from second activity. We need to make a few change inside of our
onClick() when our first activity here when we start activity because here the magic happen. Here, the
startActivity it starts when we click the button and then we go to the second activity and from there we can go
and retrieve all messages. But we passed the intent just saying start activity and make sure the activity we need to
tell the system to get ready for what comes next which we want to start activity for result. Because, is we just say
startActivity and we will go to that activity go back nothing will be registered in the android system that this
activity is starting for results meaning that we’re starting to hopefully receive result back from the second
activity. Hence, change something that used before as replaced the following:
startActivity( new Intent( MainActivity.this,SecondActivity.class ) );
by
startActivityForResult( intent,REQUEST_CODE );
So that, we know which activity message that we are getting from the is of 2 which gives in request_code. Now
we are saying this activity first activity is registered to for result so it will always expect to get a result.
Then implements onActivityResult() as follows:
if (requestCode==REQUEST_CODE){
if (resultCode==RESULT_OK){
The first condition is check about the request code which means if the request code is being passed coming back
equal to our request code that we have specified. And the second condition is check if result code is OK which
means exactly what we are passing in the second activity.
Now everything are ok then retrieve our message. Here, the data that we see being passed from our own activity
result which will have all of intent information that we are getting from the second activity get that getStringExtra
and passed returnData which is the key that declared in second activity. Finaly, let’s display on Toast.
Now, we will be showing the results from second activity message in first activity(in main activity) and able to
exchanging information from first activity and second activity.
The full code, first activity
public class MainActivity extends AppCompatActivity {
private Button gotoNext;
private final int REQUEST_CODE=2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate( savedInstanceState );
setContentView( R.layout.activity_main );
gotoNext=(Button) findViewById( R.id.showActivityID );
gotoNext.setOnClickListener( new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent=new Intent(MainActivity.this,SecondActivity.class );
intent.putExtra( "name","Aster Awoke" );
intent.putExtra("sex","Female" );
intent.putExtra( "age",50 );
// startActivity( new Intent( MainActivity.this,SecondActivity.class ) );
startActivityForResult( intent,REQUEST_CODE );
}
} ); }
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult( requestCode, resultCode, data );
if (requestCode==REQUEST_CODE) {
if (resultCode == RESULT_OK) {
String result = data.getStringExtra( "returnData" );
Toast.makeText( this, "our result" + result, Toast.LENGTH_LONG ).show();
}
}
}
And SecondActivity.java
public class SecondActivity extends AppCompatActivity {
private TextView showIntent;
private Button goBackTo;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate( savedInstanceState );
setContentView( R.layout.activity_second);
Bundle extras=getIntent().getExtras();
showIntent=(TextView)findViewById( R.id.showIntentID );
goBackTo=(Button)findViewById( R.id.backtobtnID );
if (extras!=null){
String name=extras.getString( "name" );
String sex=extras.getString( "sex" );
int age=extras.getInt( "age" );
showIntent.setText( "Name:"+name+" \n Sex:"+sex+"\n Age:"+String.valueOf( age) );
}else{
showIntent.setText( "we can't sent any values " );
}
goBackTo.setOnClickListener( new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent returnIntent=getIntent();
returnIntent.putExtra( "returnData","From second activity" );
setResult( RESULT_OK, returnIntent);
finish();
}
} );
}
}
1.1. Pet Bio apps
In this application, we
will able to develop a
bio for our pats to be
pass and to be a dog
and a cat. So the idea is to have two pictures on the first screen with a dog and a cat. And if we click on the
pictures we’ll take to the second screen where it will show a picture of that cat or dog and also a little bit
description or Bible about that pictures(cats/dog). This apps is enhanced the idea of transition data from one
activity to others. So let’s design the layout that the user directly interact. Here,let’s change the background to
make beautiful design. And then, add images to our layout and we have already added to images cat and dog
images inside our drawable. Inorder to insert image in our apps we have used image viewer from plate in android
studio so we going to add two image view at top and buttom of our layout for adding cat and dog images to our
apps.
When we build our interface, then we go back to
our main and setup our views as follows:
...
catView=(ImageView)findViewById( R.id.catImageViewId );
dogView=(ImageView)findViewById( R.id.dogImageViewId );
Then attach action listener to our images that is cat image and dog image. Because, buttons are a types of views
that means it inherits from view and view is just a screen where we can tap or click so we can add action listener
to any views. Now, we going to add toast inorder to check which images are clicked and display on small
popups.
@Override
public void onClick(View view) {
Toast.makeText( MainActivity.this,"cat image touched",Toast.LENGTH_LONG ).show();
} } );
dogView.setOnClickListener( new View.OnClickListener() {
@Override
public void onClick(View view) {
Toast.makeText( MainActivity.this,"Dog image touched",Toast.LENGTH_LONG ).show();
}
} );
But the above code is redundant because when we have more than one views to attach action listener it would be
really nice to just have only on onClick() method that will attach to all of others views that well setting action
listener. Hence, how to do is first implements View.OnClickListener interface to our class and when we need
implements certain methods like onClick() methods that comes with OnClickListener.
@Override
public void onClick(View view) {
Now, we have to register all cat view and dog view and pass our context like Mainactivity.this as follows:
catView.setOnClickListener( this );
dogView.setOnClickListener( this );
Here, the view are activated or registered to our class or the click listener event and we have pass the activity or
the context means now we can control whatever happens to the views inside of onClick() methods.
Now inside, onclick() methods, we need to find which button is tapped or clicked by the users which means
differentiate which button is clicked and we can used switch statement. In this statement use View.getId() as
conditions which means any view that get id and in case we have use R.id.viewId for access each views as
follows:
@Override
public void onClick(View view) {
switch (view.getId()){
case R.id.catImageViewId:
Toast.makeText( MainActivity.this,"Cat image is touched",Toast.LENGTH_LONG ).show();
break;
case R.id.dogImageViewId:
Toast.makeText( MainActivity.this,"Dog image is touched",Toast.LENGTH_LONG ).show();
break;
}
}
Then, implement the actual logic when we clicked the images got to the second activity that displayed the images
and some Bio about a specific image what we tapped or clicked. Since, we have to animals cat and dogs we
should create two activities for each animals but we don’t need create two activities because that would be a wast
of our resources. So, what do we need to do to think more dynamically just create one activity that will decide
whether we’ve clicked on cat or dog and then displayed the appropriate messages or information about we
tapped.
So now create another activity and going to be BioActivity as name. And in this new activity add image view at
top and in the bottom add some text that displays our Bio information about our animals. Then, initially let’s add
darker transparent as background for our images and change id petBioImageId. And also, we have add two text
view at the bottom of our activity in order to display name and Bio information respectively about animals.
petImage=(ImageView)findViewById( R.id.petBioImageId );
petName=(TextView)findViewById( R.id.nameId );
petBio=(TextView)findViewById( R.id.bioInfoId );
Now, we have finished on the second activity called BioActivity so we came back to Main activity we need to
create an intent so that it takes us to our second activity which is the BioActivity. So, first we are going to do
Intent in each case in switch statement. Then, add something like image, name and bio informations what we
tapped in putExtra() method.
switch (view.getId()){
case R.id.catImageViewId:
Intent catIntent=new Intent( MainActivity.this,BioActivity.class );
catIntent.putExtra( "name","Kenubish" );
catIntent.putExtra( "bio","Kenubish cat is very interesting cat..." );
startActivity( catIntent );
break;
case R.id.dogImageViewId:
Intent dogIntent=new Intent( MainActivity.this,BioActivity.class );
dogIntent.putExtra( "name","Boby" );
dogIntent.putExtra( "bio","Boby dog is very interesting dog ..." );
startActivity( dogIntent );
break;
}
However, we still need to be able to fetch all information like name and bio information in our BioActivity. So,
we going to create a Bundle instance which holds the information that comes from main activity and for retrive
we have used getIntent() followd getExtras() methods. Then, use conditional statements for checking if our intent
have some information or haven't. Our bundle objects called extras and instantiate it before use the object.
..
extras=getIntent().getExtras();
if (extras!=null){
String type=extras.getString( "type" );
String name=extras.getString( "name" );
String bio=extras.getString( "bio" );
setup( type,name,bio );
}
Hence, let’s create a method called setup() that have two arguments name and bio string type and invoke inside if
condition in the above block code. The implementation of the method, make sure that our image view has the
correct image and also our text view has the correct name and bio for a specific animals.
Now, use some condition for checking which animal or which item or the given information is for cat or dog that
sent from main activity.
}
}
Then, inside each condition in the above block code sets the appropriate image in the image view which means
when the user click on cat then the system able to set cat image to image view else set dog image to it as we
tapped. So, for doing this we going to use the following method.
petImage.setImageDrawable( );
The full java code for MainActivity and BioActivity as follows: the first one is MainActivity.java
And BioActivity.java
.Chapter Eight
2. Recycler View
2.1. Introduction
In this chapter we will discus about new concept is called recycler view concepts which will be able to display a
lot of information on a single screen. And the information that display on screen has to be organized in a row or
as in a list form get in android.
The RecyclerView widget is a more advanced and flexible version of ListView. In the RecyclerView model,
several different components work together to display our data. The overall container for our user interface is a
RecyclerView object that we add to our layout. The RecyclerView fills itself with views provided by a layout
manager that we going to provide. We can use one of standard layout managers (such as LinearLayoutManager
or GridLayoutManager), or implement our own.
The views in the list are represented by view holder objects. These objects are instances of a class we define by
extending RecyclerView.ViewHolder. Each view holder is in charge of displaying a single item with a view. For
example, if our list shows music collection, each view holder might represent a single album. The RecyclerView
creates only as many view holders as are needed to display the on-screen portion of the dynamic content, plus a
few extra. As the user scrolls through the list, the RecyclerView takes the off-screen views and rebinds them to
the data which is scrolling onto the screen.
This RecyclerView model does a lot of optimization work so we don't have to:
l When the list is first populated, it creates and binds some view holders on either side of the list. For
example, if the view is displaying list positions 0 through 9, the RecyclerView creates and binds those
view holders, and might also create and bind the view holder for position 10. That way, if the user scrolls
the list, the next element is ready to display.
l As the user scrolls the list, the RecyclerView creates new view holders as necessary. It also saves the
view holders which have scrolled off-screen, so they can be reused. If the user switches the direction they
were scrolling, the view holders which were scrolled off the screen can be brought right back. On the
other hand, if the user keeps scrolling in the same direction, the view holders which have been off-screen
the longest can be re-bound to new data. The view holder does not need to be created or have its view
inflated; instead, the app just updates the view's contents to match the new item it was bound to.
l When the displayed items change, you can notify the adapter by calling an appropriate
RecyclerView.Adapter.notify…() method. The adapter's built-in code then rebinds just the affected items.
dependencies {
implementation 'com.android.support:recyclerview-v7:28.0.0'
}
Note: With Android Studio 3.6 and higher, the view binding feature can replace findViewById() calls and
provides compile-time type safety for code that interacts with views. Consider using view binding instead of
findViewById().
Now, all in recycler view to work we need to feed it data but before given data we need to make sure that we
have an adapter because the recycler view by itself is useless. The recycler view is just a tamplate that we can
use to connect it to an adapter. So, adapter is the interface between the data which would be the information we
want show on screen. Hence, we have three pieces that are very important for all of our works to create recycler
view apps which are recycler view, data and adapter.
Now, the next thing is we need to do is to customize each list view or each row that we want to show. So to do
that create a separate xml file and we’re going to call list_row that will be then inflated to create an actual view.
Here, let’s add a card view to our Linear layout because it just a card looking item that will hold data inside of
our list view or in our case recycler view. So, in order to add card view the first thing is search card view in the
plate and add to our list_row xml layout. And also we going to add Relative layout inside card view. So, we can
see the component tree as follows :
So, inside our linear layout here we’re going to add some text views.
<Te xtView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
/>
The last one in the above block called android:layout_weight="1" well tell the android system how to
distribute the allocated view for the text. The full xml code for list_row layout looks like as follows:
Then, add an adapter because it is the bridge between the recycler view and the data that we are going to be
feeding into our recycler view. For implementing this we need to use a programming pattern called MVC(Model
View Controller). The idea is when we building an application in fact the whole android paradigms is built up on
MVC concepts. Hence, we have our views the user can see or interact and we have our model what the data part
of things and then we have the controller give the classes that actually control what is supposed to be done. So,
for put all java code in the same java file is bad habits when we develop advanced applications.
So, what we going to do is create another package's inside our apps and name this Adapter where all of our
Adapter are going to go here. And also, we going to create another packages called Util for Utility class and add
Model package for model class.
Now, we’re going to do create an adapter because will allow us to connect the view with list of rows which will
be displaying the data in our case Title and description. We can next step is adding a list adapter.
To feed all our data to the list, we must extend the RecyclerView.Adapter class. This object creates views
for items, and replaces the content of some of the views with new data items when the original item is no longer
visible.
Now, create an adapter class called MyAdapter and extends RecyclerView.Adapter<>. Here the angle bracket
what we are going to pass when we going to say MyAdapter that ViewHolder.
Here, our class MyAdapter takes all the functionalities because it extends or inherit from Recycler view adapter.
So, there are classes and methods that waits us to work with from our recycler view and then we’re going to pass
myAdapter data view holder which is our myAdapter view holder constructor which create next. So, here we
have implements the method as follows:
package Adapter;
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
return null;
}
@Override
public void onBindViewHolder(@NonNull MyAdapter.ViewHolder holder, int position) {
}
@Override
public int getItemCount() {
return 0;
}
The layout manager calls the adapter's onCreateViewHolder() method. That method needs to construct a
RecyclerView.ViewHolder and set the view it uses to display its contents. The type of the ViewHolder
must match the type declared in the Adapter class signature. Typically, it would set the view by inflating an XML
layout file. Because the view holder is not yet assigned to any particular data, the method does not actually set
the view's contents.
The layout manager then binds the view holder to its data. It does this by calling the adapter's
onBindViewHolder() method, and passing the view holder's position in the RecyclerView. The
onBindViewHolder() method needs to fetch the appropriate data, and use it to fill in the view holder's
layout. For example, if the RecyclerView is displaying a list of names, the method might find the appropriate
name in the list, and fill in the view holder's widget.
So,we are going to create Context and ListItem instances then create a constructor which we’re going to pass
Context and list of items that we want to feed to our recycler view. And also, we need to do here is setup our
context and a list item.
Then, going to implements inside our Model class that reperesents our list items. So,here create a class called
ListItem inside our model package/folder.
package Model;
public class ListItem {
}
Inside here, add instance variable and constructor because we need to get the list item is representing each item
that we are going to show in recycler view essentially the title and description.
package Model;
public class ListItem {
private String name;
private String description;
public ListItem( String name,String description){
this.name=name;
this.description=description;
Now, go back to our adapter class called MyAdapter and import our model class as import Model.ListItem; in
MyAdapter.java. And also create the inner class that will implements ViewHolder.
Here,what this ViewHolder class will holds all of the items that will have in our list_row so we’re going to able
fetch that the text view name and description and that’s where we are going to be able to do and sent all of those
views that they are able to be shown. Since, we have extends RecyclerView.ViewHolder class to inherit the
properties of it because it has to go and get all of the functionalities of the actual RecyclerView base class.
public class ViewHolder extends RecyclerView.ViewHolder{
public ViewHolder(@NonNull View itemView) {
super( itemView );
}
}
The layout manager calls the adapter's onCreateViewHolder() method. That method needs to construct a
RecyclerView.ViewHolder and set the view it uses to display its contents. The type of the ViewHolder
must match the type declared in the Adapter class signature. Typically, it would set the view by inflating an XML
layout file. Because the view holder is not yet assigned to any particular data, the method does not actually set
the view's contents. So inside this method we going to implements as follows:
Here, we have the view called list_row which holds the entire view and return new object of viewholder type
which contain all of that we need. Since, we have a view is being passed on through our ViewHolder.
Then, we need to bind the ViewHolder with our Adapter so that then we can show inside of our Recycler View
but before we’re going to do inside let’s fix our ViewHolder which create a few fields like TextField.
public class ViewHolder extends RecyclerView.ViewHolder{
public TextView name;
public TextView description;
public ViewHolder(@NonNull View itemView) {
super( itemView );
name=(TextView)itemView.findViewById( R.id.title );
description=(TextView)itemView.findViewById( R.id.description );
}
}
Here, in the statement “name=(TextView)itemView.findViewById( R.id.title )”, we have used itemView for
instantiate each text view fields because we have to attach the view that we are working.
The last method in Adapter class called getItemCount() we need to return the size of our list view or list item. So
what we’re going to do here get the size of list item because in order to get the count of our list items.
public int getItemCount() {
return listitems.size();
}
The layout manager then binds the view holder to its data. It does this by calling the adapter's
onBindViewHolder() method, and passing the view holder's position in the RecyclerView. The
onBindViewHolder() method needs to fetch the appropriate data, and use it to fill in the view holder's
layout. For example, if the RecyclerView is displaying a list of names and description the method might find
the appropriate name and description in the list, and fill in the view holder's widget.
If the list needs an update, call a notification method on the RecyclerView.Adapter object, such as
notifyItemChanged(). The layout manager then rebinds any affected view holders, allowing their data to
be updated.
Finally, go to our main activity and create instantiate our adapter and start putting together our recycler view.
Here, we said recyclerView.setHasFixedSize( true ), because we want to every item has a fixed size. And also, set
layout manager to our recycler view in our case pass Linear layout objects in Layout manager.
listItems=new ArrayList<>( );
for (int i=0;i<9;i++){
ListItem item=new ListItem(
"Item"+(i+1),
"Description" );
listItems.add( item );
Here, the last statement in the above code is set an adapter to our recycler view which has all of the code, all of
the data and all of visuals and everything combind. And let’s see what seems in a device.But it have space
between the list items in the left images it probably, on list_row so change a few things like
layout_height="wrap_content" instead of layout_height="match_parent". The right picture is after change
layout height in relative layout inside list_row xml and it is the correct.
cardview_compat_inset_shadow". No
.
The only left is implement in Util package, the idea here is that we’re able to separate everything in MVC
programming pattern. It used for when we want to add another text view in the layout how to add easily without
affected others. Let’s add another TextView on list_row layout and add this text view to recycler view. Hence,
firstly go to the model and add new String instance and generate getter and setter for this instance called rating
instance.
...
And also, go to ViewHolder in our Adapter and add our new field called rating
holder.rating.setText( item.getRateing() );
...
public TextView rating;
public ViewHolder(@NonNull View itemView) {
super( itemView );
...
rating=(TextView)itemView.findViewById( R.id.ratingID );
}
Then, we have to add the new fields in MainActivity and pass to ItemList
constructor.
Now, let’s attach an event listener for an list items but how to add an event listener to our recycler view? Yes,
first go to our adapter and implements OnClickListner on our inner ViewHolder class and then implement
onClick() abstract method inside our class. And also, we have to registered an item view because when the user
tapped on the view triggers to something which have three text views(title,description and rating).
...
}
Now, we have able to got to our onClick() method to setting things up. Here, if we tapped or clicked on either of
those items of the rows we need to make sure the position on which item has been clicked. So, we have to get the
correct position of an item or get position of row clicked by getAdapterPosion() methods.
Now we can get the list item which row is tapped or clicked.
int position=getAdapterPosition();
ListItem item=listitems.get( position );
Here, the item object holds an item from listitems at a specific position so we know exactly which item we’re
clicked or tapped as well. Hence,we’re going to add Toast for show the effects when we tapped to a certain row
in recycler view.
We’ll do when we clicked or tapped a specific row then able to show some information about what we clicked or
tapped in another activity. So, what we need to do is first create a new activity called DetailsActivity which will
receive the item from the previous activity. And also, in the layout we have put different fields depends on how
many fields have a single row that will tape.
context.startActivity( intent );
}
But here, for start an activity we have used context because we implements inside Adapters so here haven’t any
activity that is why used context before the method is called startActivity(intent) because context does it start
activity.
Then, we go back to detail activity for retrieve the information that passed from recycler view in order to
displayed on detailed activity. So first create all instances that will used in our works and then instantiate all of
created instances.
...
name=(TextView)findViewById( R.id.detailNameId );
description=(TextView)findViewById( R.id.detialDescriptionId );
rating=(TextView)findViewById( R.id.detailRatingId );
Then, we have retrieve information by Bundle objects which will holds all item information that put in our
intent.
extras=getIntent().getExtras();
if (extras!=null){
name.setText( extras.getString( "name" ) );
description.setText( extras.getString( "description" ) );
rating.setText( extras.getString( "rating" ) );
}
ListItem.java
package Model;
public class ListItem {
private String name;
private String description;
private String rating;
public ListItem( String name,String description,String rating){
this.name=name;
this.description=description;
this.rating=rating;
}
public void setName(String name) {
this.name = name;
}
public void setDescription(String description) {
this.description = description;
}
public void setRating(String rating) {
this.rating = rating;
}
public String getName() {
return name;
}
public String getDescription() {
return description;
}
public String getRating() {
return rating;
}
}
MyAdapter.java
package Adapter;
import android.content.Context;
import android.content.Intent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.course.recyclerviewapps.DetailActivity;
import com.course.recyclerviewapps.MainActivity;
import com.course.recyclerviewapps.R;
import org.w3c.dom.Text;
import java.util.List;
import Model.ListItem;
@Override
public void onBindViewHolder(@NonNull MyAdapter.ViewHolder holder, int position) {
ListItem item=listitems.get( position );
holder.name.setText(item.getName());
holder.description.setText( item.getDescription());
holder.rating.setText( item.getRating() );
}
@Override
public int getItemCount() {
return listitems.size();
}
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{
public TextView name;
public TextView description;
public TextView rating;
public ViewHolder(@NonNull View itemView) {
super( itemView );
itemView.setOnClickListener( this );
name=(TextView)itemView.findViewById( R.id.title );
description=(TextView)itemView.findViewById( R.id.description );
rating=(TextView)itemView.findViewById( R.id.ratingID );
}
@Override
public void onClick(View view) {
int position=getAdapterPosition();
ListItem item=listitems.get( position );
context.startActivity( intent );
}
}
}
MainActivity.java
package com.course.recyclerviewapps;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import android.os.Bundle;
import android.widget.Adapter;
import java.util.ArrayList;
import java.util.List;
import Model.ListItem;
import Adapter.MyAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate( savedInstanceState );
setContentView( R.layout.activity_main );
recyclerView=(RecyclerView)findViewById( R.id.recyclerViewID );
recyclerView.setHasFixedSize( true );
recyclerView.setLayoutManager( new LinearLayoutManager( this ));
listItems=new ArrayList<>( );
for (int i=0;i<9;i++){
ListItem item=new ListItem(
"Item"+(i+1),
"Description", "Excelent!" );
listItems.add( item );
}
adapter=new MyAdapter( this,listItems );
recyclerView.setAdapter( adapter );
}
}
package com.course.recyclerviewapps;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate( savedInstanceState );
setContentView( R.layout.activity_detail );
name=(TextView)findViewById( R.id.detailNameId );
description=(TextView)findViewById( R.id.detialDescriptionId );
rating=(TextView)findViewById( R.id.detailRatingId );
extras=getIntent().getExtras();
if (extras!=null){
name.setText( extras.getString( "name" ) );
description.setText( extras.getString( "description" ) );
rating.setText( extras.getString( "rating" ) );
}
}
}
. Chapter Nine
.Android Media player
1.1. Media player Overview
The Android multimedia framework includes support for playing variety of common media types, so that we can
easily integrate audio, video and images into our applications. We can play audio or video from media files
stored in your application's resources (raw resources), from standalone files in the file system, or from a data
stream arriving over a network connection, all using MediaPlayer APIs.
Note: You can play back the audio data only to the standard output device. Currently, that is the mobile device
speaker or a Bluetooth headset. You cannot play sound files in the conversation audio during a call.
MediaPlayer
This class is the primary API for playing sound and video.
AudioManager
This class manages audio sources and audio output on a device.
l Internet Permission - If you are using MediaPlayer to stream network-based content, your application
must request network access.
Wake Lock Permission - If your player application needs to keep the screen from dimming or the processor
from sleeping, or uses the MediaPlayer.setScreenOnWhilePlaying() or MediaPlayer.setWakeMode() methods,
you must request this permission.
l Local resources
l Internal URIs, such as one you might obtain from a Content Resolver
l External URLs (streaming)
Here is an example of how to play audio that's available as a local raw resource (saved in your application's
res/raw/ directory):
Java
In this case, a "raw" resource is a file that the system does not try to parse in any particular way. However, the
content of this resource should not be raw audio. It should be a properly encoded and formatted media file in one
of the supported formats.
And here is how you might play from a URI available locally in the system (that you obtained through Playing
from a remote URL via HTTP streaming looks like this:
Java
Note: If you're passing a URL to stream an online media file, the file must be capable of progressive download.
Caution: You must either catch or pass IllegalArgumentException and IOException when using
setDataSource(), because the file you are referencing might not exist.
Apart from the start and pause method, there are other methods provided by this class for better dealing with
audio/video files. These methods are listed below:
Sr.N
Method & description
o
isPlaying()
1
This method just returns true/false indicating the song is playing or not
seekTo(position)
2
This method takes an integer, and move song to that particular position millisecond
getCurrentPosition()
3
This method returns the current position of song in milliseconds
getDuration()
4
This method returns the total time duration of song in milliseconds
reset()
5
This method resets the media player
release()
6
This method releases any resource attached with MediaPlayer object
setDataSource(FileDescriptor fd)
8
This method sets the data source of audio/video file
selectTrack(int index)
9 This method takes an integer, and select the track from the list on that particular
index
getTrackInfo()
10
This method returns an array of track information
1.1. Create a simple apps that playing mp3
The android application will playing mp3 music. So, the first thing is create a new project and we going to call
MediaPlayerApps as a name. Then we are going to create a new folder called raw inside res/ in android studio
which we need for put our media files like mp3 file and add sample media mp3 files(e5.mp3) to our new created
directory/folder called raw.
The ides here, is create a simple apps that has a button and when we tapped a button the sound/music will play or
pause when it play before. So, now go to design mode the add a button and change some property.
<Button
android:id="@+id/playButtonId"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#86B0EF"
android:text="@string/play_button"
android:textColor="#BF360C"
android:textSize="24sp"
android:padding="10dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
Now, we go back main activity java class and create an instances of media player and button the instantiate them.
In order start playing media such as mp3,video files we need to have a class called MediaPlayer that’s why we
want to create an instance and instantiate the class MediaPlayer.
private MediaPlayer mediaPlayer;
private Button playButton;
...
mediaPlayer=new MediaPlayer();
mediaPlayer=MediaPlayer.create( getApplicationContext(),R.raw.e5 );
playButton=(Button)findViewById( R.id.playButtonId );
Here, we have create mediaPlayer instance/object of MediaPlayer class and instantiate inside onCreate()
methods. Then, create media play which takes the context or the path where our file is found/put. The below
block code, our instance/object holds our context and our media files(e5.mp3) files that found in raw folder and
accessed through R or resource.
mediaPlayer=MediaPlayer.create( getApplicationContext(),R.raw.e5 );
Then, when we implements the apps to trigger that tapped by button we have add action listener to our button.
And in our action events implement that will do to start playing our mp3 music/sound. So, first we going to
check if the mp3 player is already playing or not by using the method is called isPlating() it return Boolean.
} }} );
Hence, when the music is already play and we tapped/clicked the button the music will be pause unless the music
start playing. So, we need to create to methods called pauseMusic() and startMusic() for implements start and
pause state.
Since, when we tapped or clicked on the button and the music is already on playing state or it’s playing then to
invoke or call into pauseMusic() method which pause the music and change the button text to play for allowing
other start/play again unless to invoke to startMusic() method which means in the current the music is not
playing so we tapped the button start to play and change the button pause.
Now we going to setup a setOnCompilationListener because our media player has the capability for us to set a
listener which that listen to what’s going on with our media player. It allows us to do certain things that we may
want to do as the application or as the music or mp3 plays.
//
}
} );
Here, onCompletion() methods we passed mediaplayer objects of MediaPlayer class which we can access. Then,
inside the method we going to get the duration of our music or sound through getDuration() methods and assign
to duration integer type. Inside the method also, we going to show toast message just as a moment finished the
music or our mp3. But all time are measured in millisecond in android application or programming. So, in order
get a second unit we have to divide by 1000.
mediaPlayer.setOnCompletionListener( new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mediaPlayer) {
int duration=mediaPlayer.getDuration();
String mDuration=String.valueOf( duration/1000 );
Toast.makeText( MainActivity.this,"Duration:"+mDuration,Toast.LENGTH_LONG ).show();
}
} );
However, our media player apps takes memory so after finished must be getting back a memory system. Since,
we going to override a method called destroy() method which used for clean up after finished or stopped our
player once the application is about to destroy we are going to back to the activity lifecycle.
@Override
protected void onDestroy() {
if (mediaPlayer!=null && mediaPlayer.isPlaying()){
mediaPlayer.stop();
mediaPlayer.release();
mediaPlayer=null;
}
super.onDestroy();
}
Here, if the object is still alive and if media player is still playing then we are going to say media player stop then
our media player release the resource back to our android for kill media player object on destroy. Because
imagine run the application the moment we get out of this application and we tap on the back button and the
application goes through all the life cycle and get on destroy it’s not destroy which means we’re not releasing all
of the memory back that we took out of our system. And also, each time we open we’re creating a new media
player which means we’ll get to a point where all have a lot of media player now in memory and that’s not good.
Now, we’re going to add seek bar to our media player apps allow to seek to a certain part of music. So, we’re
going to have seek bar if users start to slide left to right or right to left and the music while playing it will start
playing from where they have seek to gain.
Then, we’re go back to main activity class file then create an instance and instatiate the Seekbar class and also
we’re set the maximum property of our seek bar that get from the duration of our mp3 music. Then set
OnSeekBarChangeListener which allow us to have a way in which we can actually track the progress change, on
strat tracking touch and on stop tracking change .
….
seekBar=(SeekBar)findViewById( R.id.mSeekbarId );
seekBar.setOnSeekBarChangeListener( new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
} );
Now, we’re doing on onProgressChanged first check if our touch is coming from the user or not and then set our
media player in seek to the position when we have tapped or slide in our seek bar. when the user taps or start
sliding and let it go.
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
if (fromUser) {
mediaPlayer.seekTo( progress );
}
}
Congratulations, we are finished simple media player apps now let’s see our layout and the Main Activity class
as follows:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<Button
android:id="@+id/playButtonId"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#86B0EF"
android:text="@string/play_button"
android:textColor="#BF360C"
android:textSize="24sp"
android:padding="10dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<SeekBar
android:id="@+id/mSeekbarId"
android:layout_width="300dp"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
app:layout_constraintBottom_toTopOf="@+id/playButtonId"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.495"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.682" />
</androidx.constraintlayout.widget.ConstraintLayout>
import android.media.MediaPlayer;
import android.widget.Button;
import android.widget.SeekBar;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate( savedInstanceState );
setContentView( R.layout.activity_main );
mediaPlayer=new MediaPlayer();
mediaPlayer=MediaPlayer.create( getApplicationContext(),R.raw.e5 );
mediaPlayer.setOnCompletionListener( new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mediaPlayer) {
int duration=mediaPlayer.getDuration();
String mDuration=String.valueOf( duration );
Toast.makeText( MainActivity.this,"duration:"+mDuration,Toast.LENGTH_LONG ).show();
}
} );
playButton=(Button)findViewById( R.id.playButtonId );
playButton.setOnClickListener( new View.OnClickListener() {
@Override
public void onClick(View view) {
if (mediaPlayer.isPlaying()){
pauseMusic();
}else {
startMusic();
}
}
} );
seekBar=(SeekBar)findViewById( R.id.mSeekbarId );
seekBar.setMax( mediaPlayer.getDuration() );
seekBar.setOnSeekBarChangeListener( new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
if (fromUser) {
mediaPlayer.seekTo( progress );
}
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) { }
@Override
public void onStopTrackingTouch(SeekBar seekBar) { }
} );
}
public void startMusic(){
if (mediaPlayer!=null){
mediaPlayer.start();
playButton.setText( "Pause" );
}
}
public void pauseMusic(){
if (mediaPlayer!=null){
mediaPlayer.pause();
playButton.setText( "Play" );
}
}
@Override
protected void onDestroy() {
if (mediaPlayer!=null && mediaPlayer.isPlaying()){
mediaPlayer.stop();
mediaPlayer.release();
mediaPlayer=null;
}
super.onDestroy();
}
}
So, we going to create new project and setting the layouts which means adding image view to our layout and also
add seek bar, text view and buttons. The first thing in the layout, we’re going to add image view on the top of our
view and change some property of image view as we want like change the actual width and name change
background, change the rectangle to oval shapes and so on. Let’s change the image view to oval shape, first
create drawable resource file inside drawable called oval and then have to specify what kind of shape(line, oval
or rectangle).
Here, that we’re creating a shape that will be inflated or used as a background inside of our image view. And the
solid tag in the above xml file means when we want the whole image to be solid. And go back to activity layout
add the following:
<ImageView
android:id="@+id/imageView"
---
app:srcCompat="@drawable/oval" />
app:srcCompat="@drawable/audiospeaker"
Now, we’re going to add a little line below our speaker images. Since, we create another xml file called divider
inside our drawable and set rectangle shape with a certain color. Our divider.xml as follows:
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle" >
<solid android:color="@color/colorPrimary"/>
</shape>
Now, in our main activity add image view which we made a divider for different section to be smart music box,
<ImageView
android:id="@+id/imageView2"
...
app:layout_constraintTop_toBottomOf="@+id/imageView"
app:srcCompat="@drawable/divider" />
Then we going to add text view for show us the title and author of music or give to us some additional
information about the music that we want to play. So, we need two text view for title and author of the music.
Then, we’re going to add seek bar for allow sliding from left to right or right to left the music duration with two
text view that will be shows the actual music duration and the deducted duration in playing music.
<SeekBar
android:id="@+id/seekBarId"
style="@android:style/Widget.DeviceDefault.SeekBar"
...
app:layout_constraintTop_toBottomOf="@+id/authorId"
app:layout_constraintVertical_bias="0.183" />
<TextView
android:id="@+id/leftDurationId"
...
app:layout_constraintEnd_toStartOf="@+id/seekBarId"
app:layout_constraintTop_toBottomOf="@+id/authorId"
app:layout_constraintVertical_bias="0.191" />
<TextView
android:id="@+id/rightDurationId"
...
app:layout_constraintStart_toEndOf="@+id/seekBarId"
app:layout_constraintTop_toBottomOf="@+id/authorId"
app:layout_constraintVertical_bias="0.191" />
Then, now we’re going to add next, play/pause and previous button in the bottom of our layout. So, first add
Table raw view for holding the three buttons. And make sure wrap content both the width and height And then
we’re add all three buttons to our table rows.
<TableRow
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="15dp"
android:layout_marginTop="40dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.495"
app:layout_constraintStart_toStartOf="parent"
android:background="@color/media_background"
app:layout_constraintTop_toBottomOf="@+id/seekBarId">
<Button
android:id="@+id/previusBtnId"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@android:drawable/ic_media_previous"/>
<Button
android:id="@+id/playBtnId"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@android:drawable/ic_media_play"/>
<Button
android:id="@+id/nextBtnId"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@android:drawable/ic_media_next"/>
</TableRow>
Then, we want to create a method called setupUI() for setting all user interface specifically on this methods to
make easy and more understandable then invoke inside our onCreate() methods. And also, we are instantiate our
media player which ready for play music in this method.
public void setupUI(){
mediaPlayer=new MediaPlayer();
mediaPlayer=MediaPlayer.create( getApplicationContext(),R.raw.libe_sew_yiwedal );
artistImage=(ImageView)findViewById( R.id.imageView );
seekBar=(SeekBar)findViewById( R.id.seekBarId );
leftTime=(TextView)findViewById( R.id.leftDurationId );
rightTime=(TextView)findViewById( R.id.rightDurationId );
prevMusic=(Button)findViewById( R.id.previusBtnId );
playMusic=(Button)findViewById( R.id.playBtnId );
nextMusic=(Button)findViewById( R.id.nextBtnId );
Now, we are going to implements action listener in our class for doing some triggered that allows us to tapped or
clicked for perform another task.
@Override
public void onClick(View view) {
Let’s registered our buttons for action listener and use switch statement to use different on clicked event. And
also we have passed all of the view which set up on click listener in switch statement. Then use case for
implement related to different action events on the views.
prevMusic.setOnClickListener( this );
playMusic.setOnClickListener( this);
nextMusic.setOnClickListener( this );
switch (view.getId()){
case R.id.previusBtnId:
break;
case R.id.playBtnId:
break;
case R.id.nextBtnId:
break;
}
Now, we are going to create two method called startMusic() and pauseMusic() methods which used for starting
the media player or music and pause the music respectively. Since,inside the method first check if our media
player is null or not if not null then perform an activity which means start or pause the music.
So now, we will able to check if the media player is playing which is the music already play then invoke to
pauseStrat() when the user tapes the play button else the music is not play state then invoke to startMusic() in
order to start the music.
case R.id.playBtnId:
if (mediaPlayer.isPlaying()){
pauseMusic();
}else {
startMusic();
}
break;
Here, we have done to playing music and also able to pause the music when it already plating. But now we going
to do sliding the seek bar for sliding the music as we want. Now, we have Seek bar objects so set media player
duration to seek bar maximum then set on change listener to the object inside onCrreate() methods.
seekBar.setMax( mediaPlayer.getDuration() );
onStopTrackingTouch() of seek bar listener.So firstly implemtents on progress change which if the progress comes
from the user then seek our media player.
Then, set the left and right time when the music playing or the seek bar is sliding from left to right or right to left
inside of our own progress change method. Since, set text to left text view and pass instantiate a new class called
simple date format because we want to make sure that our time is in minute and second. Now, we can get the
current position of our media player and duration of our music/mp3 and set to an integer type variable.
int currentPosition=mediaPlayer.getCurrentPosition();
int duration=mediaPlayer.getDuration();
But, the current position and duration of our media player haven’t in the correct format as we want which means
they returns in milliseconds instead minute second format(mm:ss). So there is class called SimpleDateFormat
and contractor of it that allow to accept a string pattern that specify minutes and second. However, this pattern
performs API 24 and higher android API.
int currentPosition=mediaPlayer.getCurrentPosition();
int duration=mediaPlayer.getDuration();
SimpleDateFormat simpleDateFormat=new SimpleDateFormat( "mm:ss" );
leftTime.setText( simpleDateFormat.format( new Date( currentPosition ) ) );
So, what’s happening here converting the current position and duration time into date format in order to set the
left and right time text view. Which means what ever is coming in millisecond it’s going to be translated to date
format.
Finally, we going to add another functionality which is we want to able to as the music we slide/move the seek
bar along. So, we can actually emulate the real music player. Hence, what we going to do is create a new method
called updateThead() and inside it we going to implement the thread which wants automatically sliding the seek
bar through each seconds or move left to right during the music is playing.
Hence, the tread means when an application component starts and the application does not have no any other
component running the android system start a new Linux process for that application with a single thread of
execution. So, by default all components of the same application run in the same process and thread which we
call in main thread. Now if an application component starts and there already exist the process where an
application because another component from the application exist then the component is started with that process
and uses the same thread of execution. Hence, we can arrange for different components in our location to run in
separate process and we can create adirional threads for any process.
Therefore, the android system allow us to create different threads or different lanes in which we can create
different processes to run on them. So, now we’re going to create a new thread to update our seek bar as well as
other things inside our application.
thread.start();
}
Now, write our code inside override run() methods and add try..catch because of we have to able to catch any
error happens. Here,inside of try..catch we want to make sure that all of this is run while a certain conditions is
being. Hence, media player is not null and it’s still playing so we’re going to put a while loop. And then inside it
we’re going to implement to sleep for 50 milliseconds so the thread that sleep and takes a run-able action using
sleep() and runOnUiThread() respectively.
try {
while (mediaPlayer!=null && mediaPlayer.isPlaying()) {
Thread.sleep( 50 );
runOnUiThread( new Runnable() {
@Override
public void run() {
//our action
}
} );
}
}catch (Exception e){
e.printStackTrace();
}
Now, we’re going to update our left text field, right text field and our seek bar automatically. So, as music plays
we going to see our seek bar moving along synchronized with our left time and right time which the rest of the
time to play. So, we’re going to create some variables that holds the current media player position and the
duration of our media player and then set seek bar maximum to the new duration and set the progress of our seek
bar to new position. Finally, set the new current position of media player to left time and the new duration to our
right time text fields then invoke the updateThread() method when we want to play the music called in our
startMusic() method.
if (newMax-newPosition<100){
playMusic.setBackgroundResource( android.R.drawable.ic_media_play );
}
}
} );
Now, we going to implement when the user tapped or clicked previous or next button then what happen. So we
going to create two methods called backMusic() and nextMusic() for previous and next button respectively.
Then, invoking inside previous button tapped and next button tapped in on click listener events.
main activity.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<ImageView
android:id="@+id/imageView"
android:layout_width="242dp"
android:layout_height="199dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.139"
app:srcCompat="@drawable/audiospeaker" />
<ImageView
android:id="@+id/imageView2"
android:layout_width="363dp"
android:layout_height="10dp"
android:layout_marginTop="10dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/imageView"
app:layout_constraintVertical_bias="0.0"
app:srcCompat="@drawable/divider" />
<TextView
android:id="@+id/titleMusicId"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/title"
android:textColor="#E65100"
android:textSize="24sp"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/imageView2"
app:layout_constraintVertical_bias="0.138" />
<TextView
android:id="@+id/authorId"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/author"
android:textColor="#0D47A1"
android:textSize="18sp"
android:textStyle="italic"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/titleMusicId"
app:layout_constraintVertical_bias="0.034" />
<SeekBar
android:id="@+id/seekBarId"
style="@android:style/Widget.DeviceDefault.SeekBar"
android:layout_width="284dp"
android:layout_height="16dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.496"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/authorId"
app:layout_constraintVertical_bias="0.183" />
<TextView
android:id="@+id/leftDurationId"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="0:00"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/seekBarId"
app:layout_constraintTop_toBottomOf="@+id/authorId"
app:layout_constraintVertical_bias="0.191" />
<TextView
android:id="@+id/rightDurationId"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="10:12"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@+id/seekBarId"
app:layout_constraintTop_toBottomOf="@+id/authorId"
app:layout_constraintVertical_bias="0.191" />
<TableRow
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="15dp"
android:layout_marginTop="40dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.495"
app:layout_constraintStart_toStartOf="parent"
android:background="@color/media_background"
app:layout_constraintTop_toBottomOf="@+id/seekBarId">
<Button
android:id="@+id/previusBtnId"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@android:drawable/ic_media_previous"/>
<Button
android:id="@+id/playBtnId"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@android:drawable/ic_media_play"/>
<Button
android:id="@+id/nextBtnId"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@android:drawable/ic_media_next"/>
</TableRow>
</androidx.constraintlayout.widget.ConstraintLayout>
MainActivity.java
package com.course.musicboxapps;
import androidx.appcompat.app.AppCompatActivity;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.SeekBar;
import android.widget.TextView;
import java.io.InterruptedIOException;
import java.text.SimpleDateFormat;
import java.util.Date;
@Override
public void onStartTrackingTouch(SeekBar seekBar) { }
@Override
public void onStopTrackingTouch(SeekBar seekBar) { }
} );
}
public void setupUI(){
mediaPlayer=new MediaPlayer();
mediaPlayer=MediaPlayer.create( getApplicationContext(),R.raw.libe_sew_yiwedal );
artistImage=(ImageView)findViewById( R.id.imageView );
seekBar=(SeekBar)findViewById( R.id.seekBarId );
leftTime=(TextView)findViewById( R.id.leftDurationId );
rightTime=(TextView)findViewById( R.id.rightDurationId );
prevMusic=(Button)findViewById( R.id.previusBtnId );
playMusic=(Button)findViewById( R.id.playBtnId );
nextMusic=(Button)findViewById( R.id.nextBtnId );
prevMusic.setOnClickListener( this );
playMusic.setOnClickListener( this);
nextMusic.setOnClickListener( this );
}
@Override
public void onClick(View view) {
switch (view.getId()){
case R.id.previusBtnId:
backMusic();
break;
case R.id.playBtnId:
if (mediaPlayer.isPlaying()){
pauseMusic();
}else {
startMusic();
}
break;
case R.id.nextBtnId:
nextMusic();
break;
}
}
public void startMusic(){
if (mediaPlayer!=null){
mediaPlayer.start();
updateThread();
playMusic.setBackgroundResource( android.R.drawable.ic_media_pause );
}
}
public void pauseMusic(){
if (mediaPlayer!=null){
mediaPlayer.pause();
playMusic.setBackgroundResource( android.R.drawable.ic_media_play );
}
}
public void updateThread(){
thread=new Thread( ){
@Override
public void run() {
super.run();
try {
while (mediaPlayer!=null && mediaPlayer.isPlaying()) {
Thread.sleep( 50 );
runOnUiThread( new Runnable() {
@Override
public void run() {
int newPosition=mediaPlayer.getCurrentPosition();
int newMax=mediaPlayer.getDuration();
seekBar.setMax( newMax );
seekBar.setProgress( newPosition );
leftTime.setText( new java.text.SimpleDateFormat("mm:ss").format( new Date( newPosition )
) );
rightTime.setText( new SimpleDateFormat("mm:ss").format( new Date( newMax-
newPosition ) ) );
if (newMax-newPosition<100){
playMusic.setBackgroundResource( android.R.drawable.ic_media_play );
}
}
} );
}
}catch (Exception e){
e.printStackTrace();
}
}
};
thread.start();
}
1.1. Introduction
Mobile apps need to store and retrieve small amount of data in files. On the Android platform, these files can be
saved either on the device’s internal memory or on an external storage media, such as a Secure Device (SD) card.
By default, all files stored in the internal storage are private to the app whereas files stored in the external storage
are accessible to all apps/users.
Android enables users to customize apps according to their needs. For example, a user can choose the
background image for an app. This background remains unchanged while the app is running. However when the
app is closed, the background changes back to the previous image. Shared preferences help us make persistent
changes in apps. Preferences allow users to personalize and specify settings for apps, such as font size, colors,
and background images.
The Android platform provides two types of preferences: activity-level preferences and app-level preferences.
Activity-level preferences are associated with a specific activity and only one preference for an activity is
allowed. App-level preferences are associated with an entire app. An app can have any number of app-level
preferences. These preferences are visible in the app’s context and are stored using primitive data types in the
form of key-value pairs. This data persists across user sessions even if the app is killed.
Android provides the android.content.SharedPreferences interface, which provides a framework that allows
us to save and retrieve persistent key-value pairs of built-in data types. The data types supported by the
SharedPreferences interface are:
l boolean
l float
l int
l long
l String
l getSharedPreferences(): This method is used for app-level preferences. It is used if your app uses multiple
preferences files identified by their name. This method takes two parameters, the first parameter refers to
the file name and the second parameter refers to the operating mode. The operating mode,
MODE_PRIVATE, MODE_WORLD_READABLE, or MODE_WORLD_WRITEABLE, specifies the
access permission for the preferences. For example:
l getPreferences(): This method is used for activity-level preferences. It is used when only one preferences
file is required for your activity. Because this would be the only preferences file for the activity, you do
not supply a file name. If the specified shared preferences file exists, then it is opened; otherwise, it is
created. For example:
Now, we store data or information even if when we get out of our application the data or information are
persisted or saved when we come back to our android apps. There are different level for saved data/information
in android application. The first one is shared preference class which allow us to save/store primitive data key-
value pairs. There is also internal storage to store more private data on the device memory. The other one is
external storage to store public data on the shared external storage like SD card. And also we have more
organized way of storing data called SQLite database storage for more structured data in private storing data in a
database. And there is other options to store data on the web with own network server which can create a server
somewhere on the internet and then we can connect with our application. So, every time user’s can store
data/information externally through network connection.
So we going to start with shared preference class which allow us to save a small bits of data that we can use in
our application even after we have go out of our application next time when we come back the information is
saved and we can in record be used. For example when we want to develop a game application and save the last
highest score of users they’re playing/pause the game saved on their memory location.
Since, firstly let’s start in main activity which we going to save/store the data in our shared preference that
accepts from edit text when the we tapped the button then we will get out of the application come back and also
will show that information in the plain text that we saved before. So, we have create edit text, button and text
view in the proper design as we want.
Then, let’s create an instance of Edit text, button and textfield then instantiate them. So,the idea to use shared
preference class to save whatever we are getting from our edit text. Here, create Shared preference instance
called myPrefs and a static variable with string type that is the name of our preference file because a shared
preference followed its created internally android created an xml file which stores all the information for us.
Here, the static variable called myPrefsfile we gives xml file name which store/saved all our information and we
can retrieve as we want.
Now, we’re going to instantiate our preference with getSharedPreference() methods and passed the first
parameter is string value which is the name of the xml file that we are creating, the next parameter is the mode in
our case pass zero(0) because we want this to be accessible.
public void onClick(View view) {
myPrefs=getSharedPreferences( PREFS_NAME,0 );
}
Then, we need to create an editor that will allow us to start adding items into our preference class object. So what
we do is as follows:
public void onClick(View view) {
myPrefs=getSharedPreferences( PREFS_NAME,0 );
SharedPreferences.Editor editor=myPrefs.edit();
}
Here, the object editor is our interface will allow us then start adding items into our shared preference. And then
we can put any types of key-value pairs to our share preference. Then, what we do is commit our editor by
invoking commit() method which save/store all things we have added to our editor.
Now, how to get the data/information back. So, we’re going to create an instance Shared Preference class. Then
check if our preference object's contain the key values we have putted/stored in shared preference and when our
object contains any key values then get a values from shared preference that stored before and we can update our
text fields in order to show in user interface.
The full code of the small apps main acitivty xml or the user interfaces
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<EditText
android:id="@+id/enterText"
android:layout_width="313dp"
android:layout_height="65dp"
android:ems="10"
android:hint="@string/enter_text"
android:inputType="textPersonName"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.175" />
<Button
android:id="@+id/saveBtnId"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#01579B"
android:paddingHorizontal="16dp"
android:paddingVertical="16dp"
android:text="@string/btn_name"
android:textColor="#FFF"
android:textSize="24sp"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.498"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/enterText"
app:layout_constraintVertical_bias="0.127" />
<TextView
android:id="@+id/resultTxtId"
android:layout_width="331dp"
android:layout_height="28dp"
android:textSize="18sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/saveBtnId"
app:layout_constraintVertical_bias="0.523" />
</androidx.constraintlayout.widget.ConstraintLayout>
import androidx.appcompat.app.AppCompatActivity;
import android.content.SharedPreferences;
import android.os.Bundle;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate( savedInstanceState );
setContentView( R.layout.activity_main );
enterMessage=(EditText)findViewById( R.id.enterText );
saveButton=(Button)findViewById( R.id.saveBtnId );
resultText=(TextView)findViewById( R.id.resultTxtId );
<EditText
android:id="@+id/enterTextId"
android:layout_width="371dp"
android:layout_height="219dp"
android:gravity="start|top"
android:hint="@string/enter_text"
android:inputType="textMultiLine"
android:textAlignment="center"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.13" />
<Button
android:id="@+id/saveBtnId"
android:text="@string/save_btn"
… />
When we have finished our user interface then next we’re going to create two methods called writeToFile() and
readFromfile() for write the text from our multi-line text field and read the data that already saved respectively.
In order to implement writeToFile() method we used the OutputStream class because all the data we feed into
our stream writer it comes in as a bits or stream of bits get small piece of information. So, there is a process that
we need to follow in order to transform the bits into string or the string into bits. And also, when we do in file we
have to add exception handling.
Here, we have passed the string which is the file name that we want to write into and the second parameter is the
mode which is specify sharing the data that we are writing with other application then we can open the file and
write into opened file. And finally, we have to close because stream of bits a lot of memory being used so for
release back after finished our work we have to closed our opened file.
The next method we need work on is readFromFile() which is we need to be able to read a text from a file that
saved before and return a string. Inside this method, first create a string variable called result that holds a text
from a file that we read. So, we need to create an input stream allow us to go to fetch our texts from the file but
but the text put in bit format so we’ll be able to take that in create a string which will then return as a result and
display on the screen.
public String readFromFile(){
String result="";
try{
InputStream inputStream = openFileInput( "myfile.txt" );
if (inputStream!=null){
InputStreamReader inputStreamReader=new InputStreamReader( inputStream );
BufferedReader bufferedReader=new BufferedReader( inputStreamReader )
String tempText="";
StringBuilder stringBuilder=new StringBuilder( );
while ((tempText=bufferedReader.readLine())!=null){
stringBuilder.append( tempText );
}
inputStream.close();
result=stringBuilder.toString();
}
}catch (Exception e){
e.printStackTrace();
}
return result;
}
Here, the inputstream reader reads the information from the file because still its in a stream of bits. And the
second one called BufferReader which is a class that will create a bucket/buffer where we’re going to put all of
the bit and then we’ll be able to read those bit in a better and faster way. Then, we’re going to instantiate another
class called StringBuilder because now we are ready to start getting text from BuffereReader which takes in our
input stream reader. And then we able to get the actual string and build all of those bits into characters,
characters into string so that we can shows it to our users.
Then invoke the our methods which means, when we tapped the save buttons then our apps check if the text null
or not, when our edit text have some text value then save to our text file. So, we have action listener and
invoking writeToFile() method here.
And also invoking readFromFile() methods which invoke when our application loads so we have invoke the
method inside onCreate() methods.
if (readFromFile()!=null){
enterText.setText( readFromFile() );
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate( savedInstanceState );
setContentView( R.layout.activity_main );
enterText=(EditText)findViewById( R.id.enterTextId );
saveBtn=(Button)findViewById( R.id.saveBtnId );
saveBtn.setOnClickListener( new View.OnClickListener() {
@Override
public void onClick(View view) {
if (!enterText.getText().equals( "" )){
String ourText=enterText.getText().toString();
writeToFile( ourText );
}else{
Toast.makeText( MainActivity.this,"Please ",Toast.LENGTH_LONG ).show();
}
}
} );
try {
if (readFromFile()!=null){
enterText.setText( readFromFile() );
}
}catch (Exception e){
e.printStackTrace();
}
}
public void writeToFile(String text){
try{
OutputStreamWriter outputStreamWriter=new OutputStreamWriter(openFileOutput( "myfile.txt"
,Context.MODE_PRIVATE) );
outputStreamWriter.write( text );
Toast.makeText( MainActivity.this,"The text write succesfully on the file",Toast.LENGTH_LONG ).show();
outputStreamWriter.close();
}catch (Exception e) {
e.printStackTrace();
}
}
public String readFromFile(){
String result="";
try{
InputStream inputStream = openFileInput( "myfile.txt" );
if (inputStream!=null){
InputStreamReader inputStreamReader=new InputStreamReader( inputStream );
BufferedReader bufferedReader=new BufferedReader( inputStreamReader )
String tempText="";
StringBuilder stringBuilder=new StringBuilder( );
while ((tempText=bufferedReader.readLine())!=null){
stringBuilder.append( tempText );
}
inputStream.close();
result=stringBuilder.toString();
}
}catch (Exception e){
e.printStackTrace();
}
return result;
}
}
1.1. Stored data in SQLite
Saving/store data to a database is ideal for repeating or structured data, such as contact information. The APIs
we’ll need to use a database on Android are available in the android.database.sqlite package.
Caution: Although these APIs are powerful, they are fairly low-level and require a great deal of time and effort
to use:
l There is no compile-time verification of raw SQL queries. As our data graph changes, we need to update
the affected SQL queries manually. This process can be time consuming and error prone.
l We need to use lots of boilerplate code to convert between SQL queries and data objects.
For these reasons, highly recommended using the Room Persistence Library as an abstraction layer for
accessing information in our app's SQLite databases.
A contract class is a container for constants that define names for URIs, tables, and columns. The contract class
allows to use the same constants across all the other classes in the same package. This lets we change a column
name in one place and have it propagate throughout our code.
A good way to organize a contract class is to put definitions that are global to our whole database in the root
level of the class. Then create an inner class for each table. Each inner class enumerates the corresponding table's
columns.
Note: By implementing the BaseColumns interface, your inner class can inherit a primary key field called
_ID that some Android classes such as CursorAdapter expect it to have. It's not required, but this can help
your database work harmoniously with the Android framework.
Now let’s create Data package, Model package and Util package inside our application. We are going to create a
model class called Contact under our Model . And also inside the class create different instance variable such as
id integer type, name and phoneNumber String type for stored contact information of a person. Then create
different constructor which means first create empty constructor or default constructor, the second constructor
have three parameter id, name and phone and the third constructor have two name and phone number
parameterize constructor. But each three constructor have different signature normally in programming this types
of paradigms called Constructor overloading. Then , after creating our constructor the next thing we’re going to
create setter and getter for our instance variable. Our Contact class inside Model package's looks as follows:
package Model;
public class Contact {
public Contact() {
}
public Contact(String name, String phoneNumber) {
this.name = name;
this.phoneNumber = phoneNumber;
}
public Contact(int id, String name, String phoneNumber) {
this.id = id;
this.name = name;
this.phoneNumber = phoneNumber;
}
public int getId() {
return id;
}
The next thing, we’re going to do is create a database handler class. So, create a database there are a lot of
properties/methods that we can attached to it which are create, write to the database and update or delete. But
also have other classes that we need to instantiate. Hence, create a new class called DatabaseHandler inside Data
package which will handle all of the functionalities that will need to our database. Since, this database handler
has to extends SQLite open handler class which comes with Android. Then create constructor and implements
certain methods of our base class in derived class.
package Data;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
public class DatabaseHandler extends SQLiteOpenHelper {
public DatabaseHandler(@Nullable Context context, @Nullable String name, @Nullable
SQLiteDatabase.CursorFactory factory, int version) {
super( context, name, factory, version );
}
@Override
public void onCreate(SQLiteDatabase sqLiteDatabase) {
}
@Override
public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) { }
}
Now, we’re going to create Util class which has static instances inside our Utils package and we want to add
some static variables that will reference them on our database handler. Hence, first sets all database version,
database name and table name then sets our columns name in our table. Hence, all instance should be public
because we want to access those instance in another class.
package Utils;
public class Util {
Then, now we’re going to start create our database and start adding an item to our tables. So, go to database
handler class which extends SQLite open helper and it override onCreate() and onUpgrade() methods. And also
we have a constructor when we extend or inherit from our SQLite open helper class interface class which will
allow us to construct our database handler. Our constructor looks as follow:
public DatabaseHandler(Context context) {
super( context, Util.DATABASE_NAME, null,Util.DATABASE_VERSION );
}
The next thing we need to create the actual tables inside onCreate() methods which we are passing our database
object so we can access it. But database has its own scripting languages that different from other language like
java in order to delete or update or writing to it. Since, write the actual sql query command to create a table and
assign to a string. Then , we need to do creating the table through execSQL().
The next method that override called onUpgrade() when we want to upgrade our database. For example we we
want to delete a tables from the database we have implements inside this method. Since, we’re going to
drop/delete the table if the table already create and then create again by invoking onCreate() method after deleted
the tables.
@Override
public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {
sqLiteDatabase.execSQL( "DROP TABLE IF EXISTS "+Util.TABLE_NAME );
onCreate(sqLiteDatabase);
}
Now, we’re going to create a certain CRUD(create, update and delete) basic operations which means they are
the basic operations that we need to operate on our database and table. So first we’re going to do add contact to
the table. So, create addContact() method, inside here first we need to instantiate a database which make sure our
database has all the functionalities and all the properties needed to write to our database. Next, we’re going to
use content values which is another data abstraction that we can use like Hash map so we can add key-value
pairs object. Then inserting all of items in table and finally close our database.
db.insert(Util.TABLE_NAME,null,value );
db.close();
}
Now, we want to get the values from the database. So here we’re going to create a method called getContct()
with the Contact type. Now, we make sure that we are able to get the readable database by instantiate SQLite
database SQLiteDatabase db=this.getReadableDatabase(). Next, we create cursor which is an object/class that
allows us to go through or iterate through our database and get data information. Then create Contact objects
then we are passing what ever the cursor starting from index at 0 up to the last columns when the cursor are not
null. Then we can return our contact objects that we need to retrieve contact from tables.
public Contact getContact(int id){
SQLiteDatabase db=this.getReadableDatabase();
Cursor cursor= db.query( Util.TABLE_NAME,new String[]
{Util.KEY_ID,Util.KEY_NAME,Util.KEY_PHONE},Util.KEY_ID+"=?", new String[]
{String.valueOf( id )},null,null,null,null);
if (cursor!=null)
cursor.moveToFirst();
Contact contact=new Contact (Integer.parseInt(cursor.getString(0) ),cursor.getString(1),cursor.getString(2) );
return contact;
}
So now we are constructing a query that will allow us to get all of information presetting to an item with id that
we are passing in our contact printer. In the statement of query() methods the first parameter is pass the table
context and the second paramater pass array of String which we are going to be retrieving, and in the third
parameter is the key or the condition because we want get one contact as a certain condition. Then passing array
string which is our id for a condition and the rest of the parameter set null.
Next, we are going to create get all contacts by the list of contact. So, we’re going to create a List of contact
method called getAllContact() method which gets all contacts from the table/database. Then inside the method
instantiate the database then create an array list of contacts contain contact/contact object then next to is select
all contacts which means write select sql query command. Then we can use cursor object to get all contact
information from the given table. Next to execute our query we need to loop through our contacts so we have a
cursor which has all of our contacts which our cursor object has entire items that will keep going until the last
item is found. Then inside our loops create a Contact object then set the values to our setter methods which found
in our Contact class inorder to add contact list. Finally, return our contact list in the function.
public List<Contact> getAllContact() {
SQLiteDatabase db = this.getReadableDatabase();
List<Contact> contactList = new ArrayList<>();
String selectAll = "select * from " + Util.TABLE_NAME;
Cursor cursor = db.rawQuery( selectAll, null );
if (cursor.moveToFirst()) {
do {
Contact contact = new Contact();
contact.setId( Integer.parseInt( cursor.getString( 0 ) ) );
contact.setName( cursor.getString( 1 ) );
contact.setPhoneNumber( cursor.getString( 2 ) );
//then add our contact object to array list
contactList.add( contact );
} while (cursor.moveToNext());
}
return contactList;
}
Then, next we’re going to continue to add more operation on Database handler class like updating and deleting
some records on the database/table. So, let’s create a method called updateContact() and it inside our database
handler class for implementing our update statements and it returns integer values. So, first we need instantiate
our database then create a Contentvalues object and update any values in a certain condition.
//update an item from contacts
public int updateContact(Contact contact){
SQLiteDatabase db=this.getWritableDatabase();
ContentValues value=new ContentValues( );
value.put( Util.KEY_NAME,contact.getName() );
value.put( Util.KEY_PHONE,contact.getPhoneNumber() );
int result=db.update( Util.TABLE_NAME,value,Util.KEY_ID+" =?",new String[]
{String.valueOf( contact.getId() )} );
return result;
}
Now, let’s as perform a delete operation which deletes a specific rows in contact tables. So, first create
deleteContact() methods inside Database handler class and we need to pass an contact object. Then inside here
instantiate the database then invoke delete() method using SsQLite database class objects that instantiated and we
passed our table name and the condition which means at which position we want to delete a row of
table/database.
public int deleteUpdate(Contact contact){
SQLiteDatabase db =this.getWritableDatabase();
int result=db.delete( Util.TABLE_NAME,Util.KEY_ID+" =?", new String[]{String.valueOf( contact.getId() )} );
return result;
}
Then the last one we want to get the number of contacts that we have in our contact lists. So, first we’re going to
create getCount() methods in Database handler class which give us number of items we have in our database.
public int getContactsCount(){
SQLiteDatabase db=this.getReadableDatabase();
String query= "SELECT * FROM "+Util.TABLE_NAME;
Cursor cursor=db.rawQuery( query,null );
cursor.close();
int result=cursor.getCount();
return result;
}
Now, we are going back to main activity class and inside of our Main activity we want to instantiate our
DatabaseHandler class that we created and passing context(this). Then insert contacts by invoking to
addContact() methods by database handler object and passed an items as we want.
protected void onCreate(Bundle savedInstanceState) {
super.onCreate( savedInstanceState );
setContentView( R.layout.activity_main );
DatabaseHandler db = new DatabaseHandler( this );
Log.d( "Insert", "Data inserting..." );
db.addContact( new Contact( "Yohana", "+251912160207" ) );
db.addContact( new Contact( "Tigest", "0912925867" ) );
db.addContact( new Contact( "Kelemu", "0912160106" ) );
}
Next to it we’re going to retrieve a person contact such as a specific person name and phone number. We used
getContact() method and passed an id of a record and assigned to an object of Contact class that holds all
information of a specific person’s contact.
Log.d( "one contact","select one contact" );
Contact contact=db.getContact( 1 );
Now we want to retrieve all our contacts by invoking getAllContacts() using Database handler class object then
set to contact lists. Here, we’re going to create Contact list object and holds all information that returns from
getAllContact() method then iterate through each contact list for retrieve all the records in a given table/database
that inserted before.
Log.d( "Reading", "Reading all contacts..." );
List<Contact> contactList = db.getAllContact();
for (Contact c : contactList) {
String log = "ID:" + c.getId() + " Name:" + c.getName() + " Phone Number:" + c.getPhoneNumber();
Log.d( "All our contacts:", log );
}
Now, we going to update a contacts which means let’s us update a specific person name or phone number or both
name and phone number. So, first create a Contact objects and invoking to getUpdate() Database holder class and
passing an object of contact class and it returns an integer values so we want to assign to integer variable. Then
set a new values that we want to change in a table/database.
//update some information
Log.d( "Updating","Update a specic person contacts..." );
Contact oneContact=db.getContact( 3 );
oneContact.setName( "Abocher" );
oneContact.setPhoneNumber( "0920514538" );
int result=db.updateContact( oneContact );
Log.d( "updated","our updated"+String.valueOf( result )+" Name:"+oneContact.getName()+" Phone
Number:"+oneContact.getPhoneNumber() );
Now, delete a specific rows in a table which means let’s delete one contact that we won’t in our contact lists.
Finally, let’s us get the number of contacts that we have in our contact lists as follows:
//get the number of contacts that we have
int numContact=db.getContactsCount();
Log.d( "Number count","Number of Contacts:"+String.valueOf( numContact ) );
Now let’ see the full code for each class such as Model, DatabaseHandler, Util class and MainActivity class.
First see Util.class
package Utils;
public Contact() {
}
public Contact(String name, String phoneNumber) {
this.name = name;
this.phoneNumber = phoneNumber;
}
public Contact(int id, String name, String phoneNumber) {
this.id = id;
this.name = name;
this.phoneNumber = phoneNumber;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPhoneNumber() {
return phoneNumber;
}
public void setPhoneNumber(String phoneNumber) {
this.phoneNumber = phoneNumber;
}
}
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.view.View;
import androidx.annotation.Nullable;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import Model.Contact;
import Utils.Util;
@Override
public void onCreate(SQLiteDatabase sqLiteDatabase) {
String create_table = "CREATE TABLE " + Util.TABLE_NAME + "("
+ Util.KEY_ID + " INTEGER PRIMARY KEY," + Util.KEY_NAME + " TEXT," + Util.KEY_PHONE + "
TEXT"
+ ")";
sqLiteDatabase.execSQL( create_table );
}
@Override
public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {
sqLiteDatabase.execSQL( "DROP TABLE IF EXISTS " + Util.TABLE_NAME );
onCreate( sqLiteDatabase );
}
} while (cursor.moveToNext());
}
return contactList;
}
return result;
}
public int deleteContact(Contact contact){
SQLiteDatabase db =this.getWritableDatabase();
int result=db.delete( Util.TABLE_NAME,Util.KEY_ID+" =?",new String[]{String.valueOf( contact.getId() )} );
return result;
}
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import java.util.List;
import Data.DatabaseHandler;
import Model.Contact;
.Chapter Eleven
1. Grocery List Apps
In this, app we will build in the Grocery list apps which add an item to the apps when we open the apps the first
time, and also have an item list when we added an item and when we tapped on an item shows the details of a
list. And in this apps we can delete or update an items using some icons.
Since, create a new project with Basic activity instead of Empty activity because we need the floating buttons
apps. The floating button action allows us if we were to run an application which the buttons relay on. In the
floating button’s we have include tag in main_activity layout xml file that includes the layout content main
which means we could create another content on our xml view .
Now, in order to organize our application let’s create another packages to put our application files more better
ways. So, create Activities package firstly on our application which holds all of our activities class that we have.
And also, create Model, Data, UI and Utils package or folder to more organize our applications. When we have
those folder/package we going to start our UI layout. So, we are going to open our content main xml file from
our resource and we we’re going to put the float action to middle because this button is actually inside of content
activity. Then , change the layout gravity to center and the icons which means the default icon is email but we
want to change into add/plus sing icon. Then add text to give aditional information about what will expect from
users. Here, we want to say Add first item as information about when we tapped the button what we will do.
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_margin="@dimen/fab_margin"
app:srcCompat="@android:drawable/ic_input_add" />
<TextView
android:id="@+id/textView3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/add_item"
android:textSize="20sp"
/>
Then, to create a dialog file which when we tapped on add button then something will pop up and alert dialog
which will have two field and button that we can enter item to it. Since, create a new layout in our resource and
name it called popup.xml then inside it create our the our User Interface as follows:
<?xml version="1.0" encoding="utf-8"?>
<androidx.appcompat.widget.LinearLayoutCompat
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="14dp"
android:layout_margin="10dp">
<TextView
android:id="@+id/addGtocerytxtId"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:textSize="23dp"
android:textStyle="bold"
android:text="@string/add_grocery"/>
<EditText
android:id="@+id/enterGroceryId"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/addGtocerytxtId"
android:hint="@string/enter_item"
android:layout_marginTop="10dp"
/>
<EditText
android:id="@+id/enterQuantityId"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textStyle="italic"
android:hint="@string/enter_quantity"
android:layout_below="@+id/enterGroceryId"
android:layout_marginTop="10dp"/>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/save_btn"
android:textStyle="bold"
android:background="@color/backroudButtn"
android:layout_below="@+id/enterQuantityId"
android:textColor="@color/textColor"
android:textSize="24sp"
android:layout_marginTop="20dp"/>
</RelativeLayout>
</androidx.cardview.widget.CardView>
</androidx.appcompat.widget.LinearLayoutCompat>
Now, we’re go back to main activity class and set up our dialog alert. So first create a private method called
createpopupdialogue() which able to show our popup.xml layout that we created above. And instantiate our
dialog builder and alert dialog inside our method.
private void createDialogpopup(){
alertBuilder=new AlertDialog.Builder( this );
View view=getLayoutInflater().inflate( R.layout.popup ,null);
quantity=(EditText)view.findViewById( R.id.enterQuantityId );
save=(Button)view.findViewById( R.id.saveBtnId );
alertBuilder.setView( view );
dialog=alertBuilder.create();
dialog.show();
}
Now, we’re going to create a Grocery class inside our Model package which allows us to create a grocery object
which means its is the blue print of a grocery item. So,here we defined id, name, quantity and added date as a
fields inside the class Grocery. Then create constructors that have different signature which means the first
constructor is empty constructor/default the second constructor has all fields and the last constructor has name,
quantity and added date parameters. Then, set getter and setter of the fields that we have.
package com.course.mygroceryapps.Model;
public Grocery() {
}
public Grocery(String name, String quantity, String addDate) {
this.name = name;
this.quantity = quantity;
this.addDate = addDate;
}
public Grocery(int id, String name, String quantity, String addDate) {
this.id = id;
this.name = name;
this.quantity = quantity;
this.addDate = addDate;
}
Now, we’re going to create a database that will hold our Grocery entries. So, first create a constant class inside
Util package which holds all constants variable like database version, database name, table name and columns of
a table that we need to create our database.
package com.course.mygroceryapps.Utils;
public class Costants {
public static final int DB_VERSION=1;
public static final String DB_NAME="groceryDB";
public static final String TABLE_NAME="grocery";
Then, create DatabaseHandler class in data package which is extends/inherits from SQLiteOpenHelper
interface/class since we are extends an interface that we need to implement different methods like onCreate() and
onUpgrade() methods. And also create a constructor which will pass our database name and version to super
class that we inherited.
private Context context;
public DatabaseHandeler(Context context) {
super( context, Costants.DB_NAME, null, Costants.DB_VERSION );
this.context=context;
}
Here, we have passed our contex, our database name , factory as null and our database version to super class and
also we have set our context inside of our constructor. Then we’re going to go on create method which we’re
passing our database objects so it allow us to do things with our database. Since, we can create our table by
writing SQLite query statements. First write our SQLite query for creating a table and assign to a variable then
executed using our SQLiteDatabase class and pass our variable.
@Override
public void onCreate(SQLiteDatabase sqLiteDatabase) {
String CREATE_TABLE="CREATE TABLE "+Costants.TABLE_NAME+ "("
+ Costants.KEY_ID +" INTEGER PRIMARY KEY, "
+ Costants.KEY_NAME +" text,"
+ Costants.KEY_QTY +" TEXT ,"
+ Costants.ADD_DATE +" LONG );";
sqLiteDatabase.execSQL( CREATE_TABLE );
}
Let’s implement the other method called onUpgrade() methods when our table is already existing then we want
to delet/drop the existing table and replaced by our upgraded table as follows:
@Override
public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {
sqLiteDatabase.execSQL( "DROP TABLE IF EXISTS "+Costants.TABLE_NAME );
onCreate( sqLiteDatabase );
}
Now, we have a table/ a database so the next things that we want to do is CRUD(create, read, update or delete)
database operations. So, we’re going to create different methods for different operations which means
addgrocery(), updateGrocery(), getGerocery(), getAllGrocery(), deletGrocery() and countGrocery() methods fo
adding an item, updating grocery items, get a specific item from the list, get all grocery items, delete a specific
item from the list and get the total number we have in our database respectively.
//Add an item to our database
public int addGrocery(Grocery grocery){
return 0;
}
//Get a grocery item
public Grocery getGrocery(int id){
return null;
}
//Get all grocery lists
public List<Grocery> getAllGrocery(){
return null;
}
//update our database/table item
public int updateGrocery(Grocery grocery){
return 0;
}
//delete an item from our database/table
public int deleteGRocery(int id){
return 0;
}
//get the total number of our items
public int coundGrocery(){
return 0;
}
Hence, when we have the skeleton of our CRUD operations the we can implements each methods. So, let us start
from the first method called addGrocery() which add an item to our database. Firstlly, make our database
writable by instantiate SQLite database class and then create content values which allow us key-value pairs to
add an item to our table/database then put an items to our content values. Then excute the SQLite command
which insert any record to our table/database called insert().
//Add an item to our database
public void addGrocery(Grocery grocery){
SQLiteDatabase db=this.getWritableDatabase();
ContentValues values=new ContentValues( );
values.put( Costants.KEY_NAME, grocery.getName() );
values.put( Costants.KEY_QTY,grocery.getQuantity() );
values.put( Costants.ADD_DATE, java.lang.System.currentTimeMillis() );
long result= db.insert( Costants.TABLE_NAME,null,values );
}
Then, we’re going to implement the second method called getGrocery() method which will returns a specific
Grocery items. So, to implement the method first we have instantiate our database which allow us to execute any
SQLite database queries. And then, we want to create a cursor that we want to iterate through our database/table
inorder to retrieve an item from the table/database.
//Get a grocery item
public Grocery getGrocery(int id){
SQLiteDatabase db=this.getWritableDatabase();
Cursor cursor=db.rawQuery( "select * from "+Costants.TABLE_NAME,null );
Grocery grocery=new Grocery( );
grocery.setId( Integer.parseInt( cursor.getString( cursor.getColumnIndex( Costants.KEY_ID ) ) ) );
DateFormat dateFormat=DateFormat.getDateInstance();
String formatedDate=dateFormat.format( new Date( cursor.getLong( cursor.getColumnIndex( Costants.ADD_DATE
) ) ).getTime() );
grocery.setAddDate( formatedDate );
return grocery;
}
The third method we’re going to implement is retrieve all grocery item lists from our database/table. So, inside
this method we have instantiate our database and then create array list that holds different grocery items. Then
we have create a cursor that holds the executable query result and iterate through our database/table item lists.
After assign the our result set to cursor check if our cursor move to first if it is true use do--while loop for
iterating until the cursor reach on the last lists.
//Get all grocery lists
public List<Grocery> getAllGrocery(){
SQLiteDatabase db=this.getReadableDatabase();
List<Grocery> groceryList=new ArrayList<>( );
Cursor cursor=db.query( Costants.TABLE_NAME,new String[]{Costants.KEY_NAME, Costants.KEY_QTY,
Costants.ADD_DATE},null,null,null,null,Costants.ADD_DATE+" DESC" );
if (cursor.moveToFirst()){
do {
Grocery grocery=new Grocery( );
grocery.setId( Integer.parseInt(cursor.getString(cursor.getColumnIndex(Costants.KEY_ID ))));
grocery.setName(cursor.getString(cursor.getColumnIndex( Costants.KEY_NAME ) ) );
grocery.setQuantity(cursor.getString(cursor.getColumnIndex(Costants.KEY_QTY)) );
DateFormat dateFormat=DateFormat.getDateInstance();
String formatedDate=dateFormat.format( new
Date( cursor.getLong( cursor.getColumnIndex( Costants.ADD_DATE ) ) ).getTime() );
grocery.setAddDate( formatedDate );
groceryList.add( grocery );
}while (cursor.moveToNext());
}
return groceryList;
}
Then, now we’re going to implement the updateGrocery() methods which return an integer value and we passed
an actual Grocery object. So, here we have instantiate our database and then create Content values which holds
our values that get from user. Then we have execute update() query command and it return an integer values.
//update our database/table item
public int updateGrocery(Grocery grocery){
SQLiteDatabase db=this.getWritableDatabase();
The final method called countGrocery() which will return an integer value which is the total number of our
grocery items that found in our table/database. So, here first instantiate our database and then execute the
command and assign to new Cursor object. Then invoke getCount() method which returns the number of item
that have in our grocery table/database and it returns an integer value.
//get the total number of our items
public int coundGrocery(){
SQLiteDatabase db=this.getReadableDatabase();
String query="SELECT * FROM "+ Costants.TABLE_NAME;
Cursor cursor=db.rawQuery( query,null );
int totalItems=cursor.getCount();
return totalItems;
}
Then create a list activity which allow us to see all of the groceries items. Now, inside our content list layout
we’re going to add recycler view.
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerViewID"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="1.0" />
So, we have content list which will hold all of the groceries items once they are being displayed. And while, So
to do that create a separate xml file and we’re going to call list_row that will be then inflated to create an actual
view.
Here, let’s add a card view to our Linear layout because it just a card
looking item that will hold data inside of our list view or in our case recycler view. And also we going to add
Relative layout inside card view. So, we can see the component tree as follows :
Here, inside our list row we have add text views for grocery name,
quantity and grocery added date and table raw for edit and delete icon
button.
...
public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.ViewHolder> {
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
return null;
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) { }
@Override
public int getItemCount() { return 0; }
public class ViewHolder extends RecyclerView.ViewHolder {
public ViewHolder(View itemView) {
super( itemView );
}
}
}
Here, we have the skeleton of our recycler view adapter class which allow us to connect to our list row and data
grocery model. Now, create a context filed which allow us to pass the context of our adapter and grocery list
field. Then a constructor which have context and grocery list parameterize constructor and assign our fileds.
private Context context;
private List<Grocery> groceryList;
Then, let’s implement on onCreate(), first create a view that inflated from a context which attached our list
layouts. So now our list row ready to be set and ready to pass our view as a view holder.
public ViewHolder onCreateViewHolder( ViewGroup parent, int viewType) {
View view= LayoutInflater.from( parent.getContext() )
.inflate( R.layout.list_row,parent,false );
return new ViewHolder( view );
}
Now, let’s go to getItemCount(), which will return the grocery items list.
@Override
public int getItemCount() {
return groceryList.size();
}
Then, let’s implement on our inner class called our ViewHolder class. So, first implements onClickLitsener
interface for allow us an action event. And create all instance fields which means all our text view and buttons.
Then we have passed a context on our constructor and setting up our widgets inside it. Then register our button
to action listener and use switch statement inside onClick() event method to implements different action events
like edit grocery item and delete grocery item. And also, when we click/tapped on the whole card view then it
triggers to next windows. Hence, we have set onClickListener on our view. The implementation of our view
seem like as follows:
} );
@Override
public void onClick(View view) {
switch (view.getId()){
case R.id.editGroceryID:
break;
case R.id.deletGroceryID:
break;
}
}
}
Now, we’re going to implement onBindViewHolder() which is where everything is bind together because we
have view holder which has an actual view then bind it in this method. So, the first thing what we going to do is
create Grocery object and set the current position of object. Then set text to each view at a certain position.
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
Grocery grocery=groceryList.get( position );
holder.groceryItemName.setText( grocery.getName() );
holder.groceryQty.setText( grocery.getQuantity() );
holder.addedDate.setText( grocery.getAddDate() );
}
Now,we have an recycler view adapter, so let’s adding grocery item to our database/table. Since, we’re go back
to main activity and then instantiate our Database handler class which means we can use it everywhere we want.
...
private DatabaseHandeler db;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate( savedInstanceState );
db=new DatabaseHandeler( this );
…
}
Then, implement inside the action events which means the event when we tapped/clicked on save button. So,
first create a method called saveGroceryToDB() which to get data from our views which means getting the data
from enter grocery and quantity Edit text fields and invoke it from onclick event method.
saveButton.setOnClickListener( new View.OnClickListener() {
@Override
public void onClick(View view) {
if (!groceryItem.getText().toString().equals( "" ) && !quantity.getText().toString().equals( "" ))
saveGroceryToDB( view );
}
} );
To implement our saveGroceryToDB() method, first create Grocery object then create a string variables that will
hold get grocery item name and quantity from our view then set our grocery objects which holds grocery name
and quantity. Then, save/stored into our database/tables because our grocery object holds a data that we need to
be stored or inserted into database/table.
public void saveGroceryToDB(View view){
Grocery grocery=new Grocery( );
String itemName=groceryItem.getText().toString();
String amount=quantity.getText().toString();
grocery.setName( itemName );
grocery.setQuantity( amount );
//save to database
db.addGrocery( grocery );
Snackbar.make( view,"Grocery Item stored into table",Snackbar.LENGTH_LONG ).show();
}
Now, we’re going to do once the item is added then show the groceries item in another windows which display
the item name,quantity and added date of grocery information. So, we start seeing the item they will been add a
little delay when the snack bar shows up and says grocery item adding... and waits some seconds and take the
users to the second screen. Hence, we’re going to use Handler class and use the method postDelay() and passing
Runnable object's and inside of there run an object will have run() method and we need add how long will delay
in millisecond. And then it have override methods called run() and inside this method first dismiss our dialog and
then start the new activity called ListActivity.
Snackbar.make( view,"Grocery Item adding...",Snackbar.LENGTH_LONG ).show();
new Handler( ).postDelayed( new Runnable() {
@Override
public void run() {
dialog.dismiss();
startActivity( new Intent( MainActivity.this,ListActivity.class ));
}
}, 1000 );
Now, we able to add an item to our database then next thing what we’re going to do is display an item on
recycler view that stored on our database. So, we go back our list activity class and implement on it which first
create RecyclerView, RecyclerViewAdapter and Grocery list and database handler instances. Then instantiate all
of our instance classes inside onCreate() method and make sure that all the items are fixed correctly say
recyclerView.setHasFixedSize( true ). Next, we’re going to get groceries items from database using our database
handler object and invoking to getAllGrocery() methods which returns all groceries items from database then
assign to our grociery list objects groceryList=db.getAllGrocery(). Since,our groceryLists holds all of our
groceries from our database so now we’re going to do put into our views/recycler views and used for loop to
iterate through our lists. Then we can instantiate our recycler view adapter and pass our context and our list items
that holds all groceries items and add our recycler view adapter to our recycler view.
protected void onCreate(Bundle savedInstanceState) {
super.onCreate( savedInstanceState );
setContentView( R.layout.activity_list );
...
recyclerView=(RecyclerView)findViewById( R.id.recyclerViewID );
recyclerView.setHasFixedSize( true );
recyclerView.setLayoutManager( new LinearLayoutManager( this ) );
groceryList=db.getAllGrocery();
for (Grocery c: groceryList){
Grocery grocery=new Grocery( );
grocery.setId( c.getId() );
grocery.setName( c.getName() );
grocery.setQuantity( "Qty: "+ c.getQuantity() );
grocery.setAddDate(" Added on: "+ c.getAddDate() );
listItem.add( grocery );
}
recyclerViewAdapter=new RecyclerViewAdapter( this,listItem );
recyclerView.setAdapter( recyclerViewAdapter );
recyclerViewAdapter.notifyDataSetChanged();
}
Now, we’re going to make implementation when we tapped on groceries then go to the next activity which will
be the details activity. So, what going to do now create another new empty activity called DetialsActivity which
will display all the detail of grocery item that we taped/clicked on it. But when we go to detail activity we’re also
going to take a pass on the properties or the items of grocery object which able to get grocery name, quantity
and added date that will display on the screen. Hence, first we need to get the position of our item int
position=getAdapterPosition() Because all items in recycler view have its own positions which allows which items
was clicked/tapped. Since, create Grocery objects which holds the grocery item. Then create an intent which
navigate from current activity to Details Activity and attached id, name, quantity and added date for an item
attributes. Then start an activity because we are not inside an activity when we implementes inside
RecyclerViewAdapter class. So, we can get start activity method through our context and pass our intent.
view.setOnClickListener( new View.OnClickListener() {
@Override
public void onClick(View view) {
int position=getAdapterPosition();
Grocery grocery=groceryList.get( position );
Intent intent=new Intent( context, DetailsActivity.class );
intent.putExtra( "id",grocery.getId() );
intent.putExtra( "name",grocery.getName() );
intent.putExtra( "quantity",grocery.getQuantity() );
intent.putExtra( "addDate",grocery.getAddDate() );
context.startActivity( intent );
}
} );
Now,when we clicke/tapped anywhere we’re going to be taken to our DetailsActivity. So let’s start putt the user
interface on DetailsActivity so here add card view and inside it add relative layout then where we can put item
name, quantity, added date and an icons for edit and delete operations.
bundle=getIntent().getExtras();
if (bundle!=null) {
itemName.setText( bundle.getString( "name" ) );
qunatity.setText( bundle.getString( "quantity" ) );
addedDate.setText( bundle.getString( "addedDate" ) );
groceryId = bundle.getInt( "id" );
}
}
Now, we are able to go from one activity to another activity called detail activity. So, next we’re going to do
directly got to our recycler view when we have a grocery item in our database. So, create byPassActivity()
method and inside it start an activity and pass our intent directly goes to list activity and then invoke finish()
method which that when we can not back to main activity through back button.
protected void onCreate(Bundle savedInstanceState) {
...
byPassActivity(); }
public void byPassActivity(){
if (db.coundGrocery()>0){
startActivity( new Intent( MainActivity.this,ListActivity.class ) );
finish(); }
}