Tumgik
#how to use gridlayout in android
eiheducation · 2 years
Text
How to get started with LinearLayout on Android Studio with App?
LinearLayout is among the most important layout managers used in Android Studio. It is employed to lay out the layout's content. It is used by default to arrange the content of Activity and Fragment layout. For beginners, it is sufficient to be able to use it but for advanced developers, it's recommended to use other layout managers like GridLayout and FrameLayout. LinearLayout is also sufficient to design the layout of your application. In this article, I'll describe the fundamental procedures and configurations that you have to be aware of to begin working with LinearLayout.
The 5 Best CCNA Certification Books for 2022
1. What is LinearLayout?
LinearLayout is a layout manager used to build the scrolling list of views. It is similar to a ListView or RecyclerView however, it's not a replacement for these. It's an adaptable layout manager that can be used to create footers and headers as well as navigation or other type of content.
CCNA Routing And Switching All In One Study Guide BOOK
2. Basic steps of using LinearLayout
LinearLayout LinearLayout class is among the most effective layout classes in Android. It can be used to create almost any layout you can think of. One of the most frequent uses of LinearLayout could be creating a scrolling layout that behaves like a list. This layout can be made by creating a LinearLayout using only one child, which is an a ListView. This is the basic design of the scrolling list. You can also create an a scrolling list with TabLayout. TabLayout class. This layout is more suitable to create a list of tabs. LinearLayout is a crucial component of the Android framework. It can be used to create various layouts, such as lists that scroll. This article will show you how to start using LinearLayout using Android Studio.
How To Configure OSPF Single Area On 4 Routers In Cisco Packet Tracer
3. Fundamental configurations and configurations to LinearLayout
This section demonstrates how to make use of the LinearLayout to build the layout with just one column and only one row. The layout is comprised of the following components: TextView, Button TextView and Button. Button. The basic configurations of LinearLayout How To Configure OSPF Multi Area On 4 Routers In Cisco Packet Tracer
4. Conclusion.
To begin using linearLayout using Android Studio, it's helpful to know the different layout classes. Each layout class comes with its own set or utilities and behavior. LinearLayout is one of the most widely used layout classes. LinearLayout is a layout that lays out its children horizontally and vertically. It has multiple children that it can support, which means you can use it to display an item list such as a navigation bar or even a navigation drawer. Another thing to consider about linearLayout is that it does not have any way to connect to its children, other than setsChild() as well as getChild(). This means that you will have to go through each child to access the desired one.
Basic Cisco Router Configuration | Cisco Packet Tracer Tutorial
0 notes
superaakash24 · 5 years
Link
0 notes
iyarpage · 6 years
Text
Android App Widgets Tutorial
The most successful applications are often the simplest to use. This means that users want to see the information they need “at-a-glance” without unlocking their phone or launching the related app. On the Android platform you can achieve this in two different ways. The first, and most recent, is Android Wear, and you can learn more about in Getting Started with Android Wear with Kotlin. The second, the topic of this tutorial, is through the implementation of App Widgets. App Widgets have been available in the Android ecosystem since version Android 1.6 (Donut).
In this tutorial you’ll create an App Widget for a Coffee Log application that will allow you to control your daily usage of caffeine right from your home screen. :]
Note: Most developers love coffee, but we also know that health is very important, so I advise you to read the interesting article Health and Fitness for Developers
You’ll follow the typical process for Widget development and learn how to:
Create the Widget user interface
Get up-to-date information in the Widget
Interact with the Widget
If you’re new to Android Development, I recommended that you read Beginning Android Development with Kotlin before you start, as well as Kotlin for Android. For this tutorial you’ll also need Android Studio 3.1.2 or later.
Getting started
The first thing you should do is to download the sample project for this tutorial using the download button at the top or bottom of the tutorial. The zip file contains Android Studio projects for the starter and final versions of the Coffee Log application.
Unzip the file in a folder of your choice, go to File/Open or choose “Open an existing Android Studio project” from the Welcome to Android Studio window, and select the build.gradle file in the root folder of the starter project.
Once the project finishes loading and performing a Gradle build, you can have a look at the file structure, which should be like this:
Now that you are in the project, take a look around, especially in MainActivity, where all the logging happens. CoffeeTypes is a simple enum class with all the coffee types and their caffeine quantity in grams, while the CoffeeLoggerPersistence class is managing persistence using SharedPreferences.
It’s time to start tracking our caffeine consumption! Build and run the app by going to the Build\Make Project or using the green “play” button from the toolbar. The app will appear in your emulator or device, looking like this:
The app allows you to see how many grams of coffee you drank so far today and select new drinks to update your consumption count. Each selection leads to an update of the total displayed.
To use the app to log your coffee consumption, you have to launch the full application. As always, we can do better. What about making your user’s life simpler with an App Widget like this one?
With a Widget, you can access the same information as the application, and display a powerful motivational quote, just by using your device home screen. As you can see the layout is different because the list is now a set of 3 buttons.
There’s a lot to cover to create an App Widegt, so let’s dig in!
App widget anatomy
As the Android documentation says, an App Widget is a component that can be embedded in other applications, typically the Home screen. Security and performance are very important, so the Android platform has defined a very clear protocol that describes how an App Widget communicates with its own app and interacts with the hosting one. This is why the developer has to provide a configuration file with the following information:
The Widget layout
The Widget screen space
Whether the Widget can resize and how
A preview image that users will see when dragging the Widget on the screen
How often refreshing data can happen
An optional Configuration screen
As you’ll see, the Android system uses this information in different stages of the Widget lifecycle. The layout information is useful when the Widget is running and interacting with the user. Resize, preview and screen space required are useful when the user decides to select the Widget and drag it into the Home screen.
User interface
As you’ve seen in the previous images, apps and Widgets have different UIs. This is because the available space is different, as well as the user interaction modes. For both apps and Widgets, you can define the layout using a resource file.
You have to remember that a Widget is running in a different application and so some restrictions are in place for security and performance reasons. This means that you can only use a subset of the standard components, with which you can then interact only using a specific object of type RemoteViews. In particular, you can use only:
AnalogClock
Button
Chronometer
ImageButton
ImageView
ProgressBar
TextView
ViewFlipper
ListView
GridView
StackView
AdapterViewFlipper
Along with ViewStub, which allows a lazy inflation of a layout, you can only use the following containers:
FrameLayout
LinearLayout
RelativeLayout
GridLayout
Extensions of these classes are not allowed.
The check on these constraints is strong. Because of these restrinctions, a Widget layout has to be very simple and only use simple components like TextView, Button or ImageView.
Resizability and preview
The configuration file is the mechanism used to describe your Widget to the Android system. You can use this for setting the supported Widget sizes, telling the system whether the Widget is resizable or not, and providing an image to display when the user decides to add a Widget to their Home screen. You’ll see all of these when you insert your Widget for the first time.
Refreshing the widget
The data the Widget displays must always be up to date without wasting system resources. This means that the UI should be updated only when the data changes, and this can happen for different reasons. If the user interacts with the Widget, you need a way to update the UI and then send the event to the main app. If something is happening in the main app, you need a way to tell the Widget to refresh.
The Android platform also provides a third way, an automatic refresh of the Widget at an interval that can be set using the configuration file. Performance limitations don’t allow an update frequency greater than 30 minutes.
Widget customisation
In the case of Coffee Log, there are just three different type of coffees. But what if the user is not interested in Long coffee or they just want a different drink instead, or what if they want to simply change the quantity of grams. Or maybe the user wants to customise the background color of the Widget. As you’ll see, it’s possible to provide a configuration screen to allow all the needed customisation.
Create your Widget
Enough theory, now you can start creating your Widget. Creating a Widget requires the definition of some code and configuration files according to the specification defined by the Android platform.
Android Studio makes this process very easy, through the usage of a simple wizard, which you can access by selecting New\Widget\App widget from the File menu. You’ll see the following window:
Add the following input to the window:
Class name: CoffeeLoggerWidget
Minimum Width (cells): 3
Minimum Height (cells): 2
Here you can also see how it’s possible to define whether the Widget is resizable and what its possible destinations are. A Widget is usually part of the Home screen, but it could also part of the Keyguard, which is the screen that appears when the phone is locked.
Select Finish, and Android Studio will create three files for you:
CoffeeLoggerWidget.kt: this is a Kotlin class with the same name used in the wizard, and acts as the controller for the Widget. You’ll learn how to change this code in order to access the UI component through the RemoteViews class and how to receive and manage events from the Widget itself.
coffee_logger_widget_info.xml: this is the configuration file we described earlier with information about the refresh rate, resizability, dimensions, etc. This is the file you’re going to edit in order to provide a configuration Activity for the Widget.
coffee_logger_widget.xml: this file contains the widget’s user interface layout.
It’s important to note where all these files are in the project structure:
In particular, you see how the configuration file has been created as an XML resource file.
As you’ll see later, the wizard also made some changes to the app AndroidManifest.xml file.
Customizing the User Interface
In order to customize the UI for the Widget, open coffee_logger_widget.xml in the app\res\layout folder. The Android Studio wizard generated the following layout that you need to update:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#09C" android:padding="@dimen/widget_margin"> <TextView android:id="@+id/appwidget_text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerHorizontal="true" android:layout_centerVertical="true" android:layout_margin="8dp" android:background="#09C" android:contentDescription="@string/appwidget_text" android:text="@string/appwidget_text" android:textColor="#ffffff" android:textSize="24sp" android:textStyle="bold|italic" /> </RelativeLayout>
Remove the TextView and replace the RelativeLayout with a LinearLayout. In Android Studio, you can do this by double-clicking on the old name and typing the new name in its place. After this change you should have this:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#09C" android:padding="@dimen/widget_margin"> </LinearLayout>
Note: You’re going to use styles that are already defined in the sample project. They contain text sizes and colors, heights, widths, alignments, and other style values. If you are curious about them, check out styles.xml in the res/values folder.
Next, add three more attributes to the LinearLayout:
... android:id="@+id/widget_layout" android:orientation="vertical" android:gravity="center" ...
The android:orientation and android:gravity attributes give the LinearLayout information about how to align its content. Providing an id is also important in case we need to get a reference to the layout in the Kotlin code.
To achieve rounded corners, change the android:background attribute to @drawable/background, a drawable available in the starter project. Now the root element of the layout looks like this:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/widget_layout" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@drawable/background" android:gravity="center" android:orientation="vertical" android:padding="@dimen/widget_margin"> </LinearLayout>
Thinking vertically
For the sake of aesthetics, the user interface should look good regardless of the Widget size. It’s best to have the Widget elements spread over the available space. There are many ways to achieve that, but you should go for the simplest which consists of adding some TextView components that will expand in the remaining space between the rest of the elements.
Here’s a schematic of the layout you’ll create:
The green pattern will be a TextView that expands vertically and the blue pattern will be a TextView that expands horizontally. Keep this schematic in mind as you build the layout to understand why you add each element.
Note:If you’re tempted to fill the empty spaces using a Space instead of TextView, remember that a Widget has some UI restrictions and that a Space is not one of the allowed components.
The first element in the LinearLayout is a vertical space that you can define by adding this code as the first child:
<TextView style="@style/WidgetButtonVerticalSpace" />
Now you can add the TextView components for the amout of coffee:
<TextView android:id="@+id/appwidget_text" style="@style/WidgetTextView.Big" /> <TextView style="@style/WidgetTextView" android:text="@string/grams" />
Then add another TextView for the next vertical space before the buttons:
<TextView style="@style/WidgetButtonVerticalSpace" />
Notice that the first text view needs to have an id because you will need to change the text later on from the Kotlin code. The second one is fixed text. You’re using the predefined styles on the text views.
Next, add a container for the buttons as a LinearLayout with horizontal orientation:
<LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal"> <!-- Buttons go here --> </LinearLayout>
Then a TextView for the quote after the last vertical space.
<TextView style="@style/WidgetButtonVerticalSpace" /> <TextView android:id="@+id/coffee_quote" style="@style/WidgetQuote" />
Adding buttons
Now the green part of the layout is fnished and you have to deal with the blue part for the buttons following this schematic:
You’ve already created a container for them so you just need to start with a TextView that expands horizontally and will keep the first button at a distance from the left margin:
<TextView style="@style/WidgetButtonHorizontalSpace" />
Then you can add the first button for smallest coffee in the world:
<LinearLayout android:id="@+id/ristretto_button" style="@style/WidgetBeverageButton" > <ImageView style="@style/WidgetButtonImage" android:src="@drawable/ic_ristretto" /> <TextView style="@style/WidgetButtonText" android:text="@string/ristretto_short" /> </LinearLayout> <TextView style="@style/WidgetButtonHorizontalSpace" />
Each button has a LinearLayout that contains an ImageView and a TextView. After the button, you added another horizontally expanding TextView to help the buttons spread.
Add the next button for Espresso:
<LinearLayout android:id="@+id/espresso_button" style="@style/WidgetBeverageButton"> <ImageView style="@style/WidgetButtonImage" android:src="@drawable/ic_espresso" /> <TextView style="@style/WidgetButtonText" android:text="@string/espresso_short" /> </LinearLayout> <TextView style="@style/WidgetButtonHorizontalSpace" />
And the final button for the Long:
<LinearLayout android:id="@+id/long_button" style="@style/WidgetBeverageButton" > <ImageView style="@style/WidgetButtonImage" android:src="@drawable/ic_long_coffee" /> <TextView style="@style/WidgetButtonText" android:text="@string/long_coffee_short" /> </LinearLayout> <TextView style="@style/WidgetButtonHorizontalSpace" />
Phew! That was long but you’re done with the layout for the widget. :]
Run your Widget
The Widget you’ve created is beautiful, but it’s not doing anything quite yet. Build and run your app to make sure there’s no error in the XML. Just to be sure everything is fine, add the widget to the screen. If you’ve never added a widget to your Home screen before, here are the steps:
Go to the Home screen
Long press on an empty space
Select “Widgets”
Long press on the Coffee Log Widget
Drop it wherever you like on the screen
Your widget looks like this:
Notice how the autogenerated code populated the first TextView with “EXAMPLE”. Later in this tutorial, you will update it with the right number of coffee grams.
Performing actions
Now it’s time to add some interactivity to the Widget. When the user selects a button, you’ll have to open MainActivity, passing information about the selected coffee in order to update the total number of grams in today’s record.
Unfortunately, launching a simple Intent is not enough, because we have to remember that our Widget is running in an application that is different from ours and runs in another Android process. The Android platform has a solution for this called PendingIntent that is basically a way to ask another application to launch an Intent for you.
Open then the CoffeeLoggerWidget.kt file and add this utility function at the end of the companion object:
private fun getPendingIntent(context: Context, value: Int): PendingIntent { //1 val intent = Intent(context, MainActivity::class.java) //2 intent.action = Constants.ADD_COFFEE_INTENT //3 intent.putExtra(Constants.GRAMS_EXTRA, value) //4 return PendingIntent.getActivity(context, value, intent, 0) }
This Kotlin function has the responsibility of creating a PendingIntent for a given coffee:
First you define the Intent to launch as usual using the destination class as argument; in your case it’s the MainActivity class.
The MainActivity can be launched in different ways, and you need something that identifies how much to vary the coffee content. To do this you use an action MainActivity can recognise.
You also need to put into the Intent the quantity to add. Remember, MainActivity doesn’t know what button was pressed on the Widget!
Create the PendingIntent and return it to the caller of the function
Since you now have the action prepared, attach them to the buttons. Go to the updateAppWidget() function in the companion object and add the following code just before its last instruction appWidgetManager.updateAppWidget(...):
views.setOnClickPendingIntent(R.id.ristretto_button, getPendingIntent(context, CoffeeTypes.RISTRETTO.grams)) views.setOnClickPendingIntent(R.id.espresso_button, getPendingIntent(context, CoffeeTypes.ESPRESSO.grams)) views.setOnClickPendingIntent(R.id.long_button, getPendingIntent(context, CoffeeTypes.LONG.grams))
It is worth noting that updateAppWidget() is a convenience method the Android Studio wizard created in order to encapsulate the update logic for a given Widget. Looking at the same Kotlin class, you see that it’s invoked in the onUpdate() method for each Widget that requires an update. This call also happens when the Widget appears in the hosting application for the first time.
override fun onUpdate(context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray) { // There may be multiple widgets active, so update all of them for (appWidgetId in appWidgetIds) { updateAppWidget(context, appWidgetManager, appWidgetId) } }
The RemoteViews class
Now your code should look like this:
internal fun updateAppWidget(context: Context, appWidgetManager: AppWidgetManager, appWidgetId: Int) { //1 val widgetText = context.getString(R.string.appwidget_text) //2 val views = RemoteViews(context.packageName, R.layout.coffee_logger_widget) //3 views.setTextViewText(R.id.appwidget_text, widgetText) //4 views.setOnClickPendingIntent(R.id.ristretto_button, getPendingIntent(context, CoffeeTypes.RISTRETTO.grams)) views.setOnClickPendingIntent(R.id.espresso_button, getPendingIntent(context, CoffeeTypes.ESPRESSO.grams)) views.setOnClickPendingIntent(R.id.long_button, getPendingIntent(context, CoffeeTypes.LONG.grams)) // 5 appWidgetManager.updateAppWidget(appWidgetId, views) }
Here’s what’s going on:
You’re using the Context in order to access a string resource.
An instance of the RemoteViews class is created and given the widget’s layout id. A RemoteViews is basically a mirror image of what you’re going to display in the Widget.
You set the previous string as content of the TextView with id R.id.appwidget_text. It’s very important to note that you can’t access the TextView directly and that only some operations are allowed using the RemoteViews; in this case you’re setting a text.
Using the RemoteViews instance, you register a PendingIntent to use when the user clicks on a each Widget button.
The last instruction binds the specific instance of RemoteViews to the specific instance of the Widget.
Build and run now. You won’t see any difference in the widget, but clicking the Widget buttons will open the app with an updated value of grams. Great job!
Updating the Widget
Widgets should always display the lastest available information, and the update frequency depends on the specific type of data. A Weather Widget doesn’t need a very frequent update, unlike the score of a football match or the price of a specific stock.
You need a way to invoke the previous onUpdate() method at a specific time interval in order to create the new RemoteViews with the new data.
The following drawing gives you an idea of the process:
The problem is how to send the “I need a refresh!” message to the Widget.
Widget configuration
When the update frequency you need is longer than 30 minutes, you don’t need to write any code and you can simply rely on the configuration file coffee_logger_widget_info.xml Android Studio generated in the res\xml folder.
<?xml version="1.0" encoding="utf-8"?> <appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android" android:initialKeyguardLayout="@layout/coffee_logger_widget" android:initialLayout="@layout/coffee_logger_widget" android:minHeight="110dp" android:minWidth="180dp" android:previewImage="@drawable/example_appwidget_preview" android:resizeMode="horizontal|vertical" android:updatePeriodMillis="86400000" android:widgetCategory="home_screen"> </appwidget-provider>
The Widget refresh rate is the one defined in the attribute android:updatePeriodMillis. The default value is one day in milliseconds.
Managing updates requests
If you understand how the Android platform manages updates to your Widget, you can replicate the same thing at will. The Android Studio wizard created the CoffeeLoggerWidget class that extends AppWidgetProvider, but we didn’t realize that this was a particular implementation of a BroadcastReceiver.
You can see that by looking at the updates the wizard made to the AndroidManifest.xml file:
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.raywenderlich.android.coffeelogs"> - - - - <receiver android:name=".CoffeeLoggerWidget"> <intent-filter> <action android:name="android.appwidget.action.APPWIDGET_UPDATE" /> </intent-filter> <meta-data android:name="android.appwidget.provider" android:resource="@xml/coffee_logger_widget_info" /> </receiver> - - - - </manifest>
Based on the specific Intent‘s action, the AppWidgetProvider dispatches the call to a different methods. Launching an Intent with the android.appwidget.action.APPWIDGET_UPDATE action results in the invocation of the onUpdate() function.
This is exactly what the Android system does at the interval set in the coffee_logger_widget_info.xml configuration file. This means that the updateAppWidget() function is the perfect place for the code to execute on every update.
So add the following line to the beginning of the function:
val coffeeLoggerPersistence = CoffeeLoggerPersistence(context)
and change widgetText to take the value from there:
val widgetText = coffeeLoggerPersistence.loadTitlePref().toString()
Good! Build and run and you’ll see that the widget is periodically updating the “grams” value. Seems like someone had a little too much coffee:
Update the widget manually
If your app needs to update the data in the Widget more frequently, you already have the solution: you can simply periodically launch the same Intent the Android system does. In the case of the Coffee Log application this happens every time the user selects a coffee in the app.
Open MainActivity and add the following code at the end of refreshTodayLabel:
// Send a broadcast so that the Operating system updates the widget // 1 val man = AppWidgetManager.getInstance(this) // 2 val ids = man.getAppWidgetIds( ComponentName(this, CoffeeLoggerWidget::class.java)) // 3 val updateIntent = Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE) // 4 updateIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, ids) // 5 sendBroadcast(updateIntent)
Since this code has some new elements, let me walk you through it:
Get the AppWidgetManager instance, which is responsible for all the installed Widgets.
Ask for the identifiers of all the instances of your widget (you could add more than one to your homescreen).
Create an Intent with the android.appwidget.action.APPWIDGET_UPDATE action asking for an update.
Add the ids of the widgets you are sending the Intent to as extras of the Intent for the AppWidgetManager.EXTRA_APPWIDGET_IDS key.
Finally, send the broadcast message.
Build and run tha app to check that everytime you add some coffee, the widget also updates.
Communicating via Service
Not all the updates needed for Widgets are a consequence of an action from the user. Typical cases are data from a server through periodic polling and push notification events. In cases like these, the request has to come from a different component, which you usually implement as an Android Service.
Choose File\New\Service\Service and change the name to CoffeeQuotesService.
When you click Finish, Android studio generates a Kotlin file for you for the Service.
In CoffeeQuotesService, replace the current implementation of onBind() with:
return null
Change the return type of onBind to be the nullable IBinder?.
Then add this function, which is the one the Android system invokes at every launch of the service Service:
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { val appWidgetManager = AppWidgetManager.getInstance(this) val allWidgetIds = intent?.getIntArrayExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS) //1 if (allWidgetIds != null) { //2 for (appWidgetId in allWidgetIds) { //3 CoffeeLoggerWidget.updateAppWidget(this, appWidgetManager, appWidgetId) } } return super.onStartCommand(intent, flags, startId) }
You’ve seen the first two lines before. The others do the following:
Check that the array of allWidgetIds was in the Intent.
Loop through the allWidgetIds list.
Update each widget.
Now, you need to call this service instead of directly updating the widget. Open CoffeeLoggerWidget and replace the content of onUpdate() with the following in order to start the Service:
val intent = Intent(context.applicationContext, CoffeeQuotesService::class.java) intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds) context.startService(intent)
This creates an Intent, puts the Widget ids in the intent, and starts the Service.
In the companion object, add the following function:
private fun getRandomQuote(context: Context): String { //1 val quotes = context.resources.getStringArray(R.array.coffee_texts) //2 val rand = Math.random() * quotes.size //3 return quotes[rand.toInt()].toString() }
This function generates a random coffee quote:
It takes a quote array from the strings file
It picks a random number
Finally, it returns the string at the random position
After you have the string, update the widget. In updateAppWidget() add this before the last call:
views.setTextViewText(R.id.coffee_quote, getRandomQuote(context))
That’s it. Every time the widget updates, you get a new quote!
Making it personal
People like to personalize the look and functionality of their Home screens, and Widgets are no exception. You have to take into account that a general purpose Widget won’t bring much value to a user. To make it personal you need to let the users set up preferences and configurations.
Earlier, when covering the configuration of a Widget, you learned that it can have a Configuration screen. This is an Activity that is automatically launched when the user adds a Widget on the home screen. Note that the preferences are set up per Widget because users can add more than one instance of a Widget. It’s better to think about saving this preferences with the id of the Widget.
In this project, the configuration screen could contain a coffee amount limit. If the user logs more coffee than the limit, the Widget will turn into a soft but alarming pink.
Creating a preferences screen
The preference screen for a Widget is an Activity. Choose New\Activity\Empty activity from the File menu and edit the fields to be
Activity name: CoffeeLoggerWidgetConfigureActivity
Layout Name: activity_coffee_logger_widget_configure
Make sure the Launcher Activity checkbox is unchecked and the Source Language is Kotlin.
When you click Finish, Android Studio will generate the code for the new Activity and a template for the layout file, along with adding the registration of the Activity in the AndroidManifest.xml file.
Now create the layout for the configuration screen. Open activity_coffee_logger_widget_configure.xml and add the following:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:padding="16dp"> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="8dp" android:labelFor="@+id/appwidget_text" android:text="@string/coffee_amount_limit" /> <EditText android:id="@id/appwidget_text" android:layout_width="match_parent" android:layout_height="wrap_content" android:inputType="number" /> <Button android:id="@+id/add_button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="8dp" android:text="@string/save_configuration" /> </LinearLayout>
The layout is nothing complicated: a TextView that represents a label to the EditText, and a Button for the user to save the preferences.
Know your limits
Open CoffeeLoggerWidgetConfigureActivity and add these fields above onCreate() (developers usually put fields at the beginning of the class):
private lateinit var appWidgetText: EditText private var appWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID private val coffeeLoggerPersistence = CoffeeLoggerPersistence(this)
You will need to use these fields later to save the limit value for each widget.
In onCreate(), add the following code at the end:
//1 appWidgetText = findViewById(R.id.appwidget_text) //2 val extras = intent.extras //3 appWidgetId = extras.getInt( AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID) //4 setResult(Activity.RESULT_CANCELED)
Here’s what the code does:
Find the EditText in the layout.
Get the extras from the Intent that launched the Activity.
Extract the appWidgetId of the widget.
Make sure that if the user doesn’t press the “Save Configuration” button, the widget is not added.
Finally, you need to save the configuration when the user presses the “Save Configuration” button. Below onCreate(), declare the following OnClickListener implementation:
private var onClickListener: View.OnClickListener = View.OnClickListener { // 1 val widgetText = appWidgetText.text.toString() // 2 coffeeLoggerPersistence.saveLimitPref(widgetText.toInt(), appWidgetId) // 3 val resultValue = Intent() resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId) // 4 setResult(RESULT_OK, resultValue) // 5 finish() }
Here you:
Get the text input – the coffee limit.
Save the limit to local storage (using the Widget id).
Create a new Intent to return to the caller of the Activity and add the id of the Widget you’re configuring.
Tell the operating system that the configuration is OK. Do this by passing an Intent that contains the widget id.
Close the configuration screen
Attach this listener to the button by adding the following line below setContentView() in onCreate():
findViewById<View>(R.id.add_button).setOnClickListener(onClickListener)
This is a chained instruction that finds the Button object and sets its listener.
Linking preferences to the widget
It is a good idea to refresh the widget after the user saves the preferences. That’s because the limit might already be exceeded at the moment of adding a new widget. For this reason, write another method at the end of CoffeeLoggerWidgetConfigureActivity to trigger the refresh:
private fun updateWidget() { val appWidgetManager = AppWidgetManager.getInstance(this) CoffeeLoggerWidget.updateAppWidget(this, appWidgetManager, appWidgetId) }
The function retrieves the AppWidgetManager and triggers an update to the corresponding widget. Call this function in the OnClickListener after saving the coffee limit to coffeeLoggerPersistence. It should be before creating the Intent:
updateWidget()
To launch the configuration screen whenever the user adds a widget, you need to add it to the widget configuration file. With this in mind, open coffee_logger_widget_info.xml and add the following attribute to appwidget-provider:
android:configure="com.raywenderlich.android.coffeelogs.CoffeeLoggerWidgetConfigureActivity"
Build and run, then go to the home screen. Long press the widget and drag it to the “Remove” area. Add another widget as before and check that the configuration screen appears. It should look like this:
Enter a value in the field like 10 and press “Save configuration” to add the widget.
To make the widget react to the limit, add this in CoffeeLoggerWidget inside updateAppWidget*(, before the last line:
// 1 val limit = coffeeLoggerPersistence.getLimitPref(appWidgetId) // 2 val background = if (limit <= widgetText.toInt()) R.drawable.background_overlimit else R.drawable.background // 3 views.setInt(R.id.widget_layout, "setBackgroundResource", background)
Step by step:
First, get the limit saved by the user for that widget.
Decide if the user exceeds the limit of coffee and establish one of the two possible backgrounds: pink or blue.
Set the background to the widget's root element.
Finally, build and run. After the app opens log more coffees than the limit you set. Let's say your limit was 10: log three Espresso and go back to the home screen. As a result, your widget is now pink:
Best practices
Some final advice before you start adventuring into the world of Widgets:
Design the smallest Widget size you can. Don't take up screen real-estate if you don't need it. Be aware that the user might resize it into a bigger area.
Don't refresh the Widget too often because it will drain the battery. On the other hand, don't refresh it too rarely because it won't be useful on the screen.
Make sure you read the official guidelines for Widget design and follow the recommendations. Revisit them from time to time because things change and things get added.
Think of Widgets as a shortcut window into your app. Provide the most important information and actions in it.
Where to go from here
Congratulations, you've finished your App Widget! Download the final project using the button at the top or bottom of the tutorial.
You learned how to develop an App widget to track your coffee intake. In summary, some of your new skills are:
Create a widget layout
Link a configuration screen
Communicate via a Service
... and tie them all together. This is impressive!
You can learn more about App Widgets by checking out the official docs.
For a better understanding of Intents, have a look at the Android Intents Tutorial.
You can create a better user interface for your apps and widgets with more Material Design. Get a little knowledge boost from Android: An Introduction to Material Design.
If you have any questions or comments about Android App Widgets, please join the forum discussion below!
The post Android App Widgets Tutorial appeared first on Ray Wenderlich.
Android App Widgets Tutorial published first on https://medium.com/@koresol
0 notes
luxus4me · 7 years
Link
Envato Tuts+ Code http://j.mp/2uEwfJz
NativeScript is a framework for building cross-platform native mobile apps using XML, CSS, and JavaScript. In this series, we're trying out some of the cool things you can do with a NativeScript app: geolocation and Google Maps integration, SQLite database, Firebase integration, and push notifications. Along the way, we're building a fitness app with real-time capabilities that will use each of these features.
In this tutorial, you'll learn how to integrate a SQLite database into the app to store data locally. Specifically, we'll be storing the walking sessions data that we gathered in the previous tutorial.
What You'll Be Creating
Picking up from the previous tutorial, you'll be adding a tab view for displaying the different portions of the app. Previously our app just had the Tracking page, so we didn't need tabs. In this post, we'll be adding the Walks page. This page will display the user's walking sessions. A new data point will be added here every time the user tracks their walking session. There will also be a function for clearing the data.
Here's what the final output will look like:
Setting Up the Project
If you have followed the previous tutorial on geolocation, you can simply use the same project and build the features that we will be adding in this tutorial. Otherwise, you can create a new project and copy the starter files into your project's app folder.
tns create fitApp --appid "com.yourname.fitApp"
After that, you also need to install the geolocation and Google Maps plugins:
tns plugin add nativescript-geolocation tns plugin add nativescript-google-maps-sdk
Once installed, you need to configure the Google Maps plugin. You can read the complete instructions on how to do this by reading the section on Installing the Google Maps Plugin in the previous tutorial.
Once all of those are done, you should be ready to follow along with this tutorial.
Running the Project
You can run the project by executing tns run android. But since this app will build on the geolocation functionality, I recommend you use a GPS emulator for quickly setting and changing your location. You can read about how to do so in the section on Running the App in the previous tutorial. 
Installing the SQLite Plugin
The first thing that you need to do to start working with SQLite is to install the plugin:
tns plugin add nativescript-sqlite
This allows you to do things like connecting to a database and doing CRUD (create, read, update, delete) operations on it.
Connecting to the Database
Open the main-page.js file and import the SQLite plugin:
var Sqlite = require("nativescript-sqlite");
You can now connect to the database:
var db_name = "walks.db"; new Sqlite(db_name).then(db => { // next: create table for storing walks data }, error => { });
The walks.db file was created from the terminal using the touch command, so it's just an empty file. Copy it into the app folder.
If it successfully connected, the promise's resolve function will be executed. Inside that, we run the SQL statement for creating the walks table. To keep things simple, all we need to save is the total distance covered (in meters) and the total steps, as well as the start and end timestamps. 
db.execSQL("CREATE TABLE IF NOT EXISTS walks (id INTEGER PRIMARY KEY AUTOINCREMENT, total_distance INTEGER, total_steps INTEGER, start_datetime DATETIME, end_datetime DATETIME)").then(id => { page.bindingContext = createViewModel(db); }, error => { console.log("CREATE TABLE ERROR", error); });
Once the query executes successfully, we pass the database instance (db) into the page context. This will allow us to use it from the main-view-model.js file later on.
Fetching Data
Now we're ready to work with the data. But since we'll be working with dates, we first need to install a library called fecha. This allows us to easily parse and format dates:
npm install --save fecha
Once it's installed, open the main-view-model.js file and include the library:
var fecha = require('fecha');
Next is the code for checking if geolocation is enabled. First, create a variable (walk_id) for storing the ID of a walking record. We need this because the app will immediately insert a new walk record into the walks table when the user starts location tracking. walk_id will store the ID that's auto-generated by SQLite so that we'll be able to update the record once the user stops tracking.
var walk_id;
Next, get the current month and year. We'll use it to query the table so it only returns records that are in the same month and year. This allows us to limit the number of records that appear in the UI.
var month = fecha.format(new Date(), 'MM'); //e.g 07 var year = fecha.format(new Date(), 'YYYY'); //e.g 2017
We also need a variable for storing the start timestamp. We'll use it later on to update the UI. This is because we're only querying the table once when the app is loaded, so we need to manually update the UI of any new data which becomes available. And since the starting timestamp will only have a value when the user starts tracking, we need to initialize it outside the scope so we can update or access its value later on.
var st_datetime; // start datetime
Initialize the walks data that will be displayed in the UI:
var walks = []; viewModel.walks = []; viewModel.has_walks = false;
Get the data from the walks table using the all() method. Here, we're supplying the month and the year as query parameters. The strftime() function is used to extract the month and year part of the start_datetime. 
db.all( "SELECT * FROM walks WHERE strftime('%m', start_datetime) == ? AND strftime('%Y', start_datetime) == ? ORDER BY start_datetime DESC", [month, year] ).then((err, rs) => { if(!err){ // next: update the UI with the walks data } });
Once a success response is returned, we loop through the result set so that we can format the data correctly. Note that the indexes in which we access the individual values depend on the table structure that was described earlier in the main-page.js file. The first column is ID, the second is the total distance, and so on.
The formatted data is then pushed to the walks array and is used to update the UI. has_walks is used as a toggle for the UI so that we can show or hide things based on its value.
rs.forEach((w) => { let start_datetime = new Date(w[3]); let end_datetime = new Date(w[4]); walks.push({ start: fecha.format(start_datetime, 'MMM D, h:mm'), // e.g Jun 5, 5:30 end: fecha.format(end_datetime, 'h:mm a'), // e.g 6:30 pm distance: commafy(w[1]) + 'm', // e.g 2,000m steps: commafy(w[2]) // e.g 2,300 }); }); if(walks.length){ viewModel.set('has_walks', true); } viewModel.set('walks', walks);
This will supply the data for the ListView in the main-page.xml file:
<ListView items=""> <ListView.itemTemplate> <GridLayout columns="2*,*,*" rows="auto" class="item item-row"> <Label text="" textWrap="true" row="0" col="0"/> <Label text="" textWrap="true" row="0" col="1" /> <Label text="" textWrap="true" row="0" col="2" /> </GridLayout> </ListView.itemTemplate> </ListView>
Saving Data
Once the user starts tracking, set the current datetime as the start_datetime and insert initial values into the table using the execSQL() function. Just like the all() function, this expects the SQL query as the first argument and an array of parameters as the second.
If the query is successful, it should return the auto-generated ID for the inserted record. We then assign it as the value for the walk_id so it can be used later on to update this specific record.
st_datetime = new Date(); var start_datetime = fecha.format(st_datetime, 'YYYY-MM-DD HH:mm:ss'); db.execSQL( "INSERT INTO walks (total_distance, total_steps, start_datetime) VALUES (?, ?, ?)", [0, 0, start_datetime] ).then((id) => { walk_id = id; }, (e) => { dialogs.alert(e.message); });
Once the user stops tracking, we again get the current timestamp and format it accordingly for storage:
var ed_datetime = new Date(); var end_datetime = fecha.format(ed_datetime, 'YYYY-MM-DD HH:mm:ss');
Since we've ordered the results from most to least recent, we use unshift() (instead of push()) to add the new item to the top of the walks array.
walks.unshift({ start: fecha.format(st_datetime, 'MMM D, h:mm'), end: fecha.format(ed_datetime, 'h:mm a'), distance: commafy(total_distance) + 'm', steps: commafy(total_steps) }); viewModel.set('walks', walks); if(walks.length > 0){ viewModel.set('has_walks', true); }
After that, we once again we use the execSQL() function to update the record that we inserted earlier:
db.execSQL( "UPDATE walks SET end_datetime = ?, total_steps = ?, total_distance = ? WHERE id = ?", [end_datetime, total_steps, total_distance, walk_id] ).then( (err, id) => { if(!err){ // todo: add code for resetting the tracking UI here } } );
Be sure to move the code for resetting the tracking UI (to reset the total distance and steps) inside the promise's resolve function so you can easily test whether the update query executed successfully or not. 
Clearing Data
Deleting data is done by clicking on the Clear Data button below the list of walk data:
<ListView items=""> ... </ListView> <Button text="Clear Data" tap="" class="bg-danger" />
In the main-view-model.js file, add the code for deleting all the data from the walks table. If you're used to MySQL, you might be wondering why we're using the DELETE query instead of TRUNCATE for emptying the table. Well, that's because SQLite doesn't have the TRUNCATE function. That's why we have to use the DELETE query without supplying a condition so that it will delete all the records that are currently in the table. 
viewModel.clearData = function() { dialogs.confirm("Are you sure you want to clear your data?").then((agree) => { if(agree){ db.execSQL("DELETE FROM walks", []).then( (err) => { if(!err){ dialogs.alert("Data has been cleared!"); walks = []; viewModel.set('walks', []); viewModel.set('has_walks', false); } } ); } }); }
Conclusion
In this tutorial, you've learned how to locally store data in your NativeScript apps using the SQLite plugin. As you have seen, SQLite allows you to reuse your existing SQL skills in managing a local database. It's important to note that not all functions that you're used to in MySQL are supported in SQLite. So it's always wise to consult the documentation if you're not sure whether a certain function is supported or not. 
If you want to learn about other options for storing data in NativeScript apps, I recommend you read this article on Going Offline With NativeScript.
In the final post of this series, we'll add push notifications to our app.
In the meantime, check out some of our other posts on NativeScript and cross-platform mobile coding.
Mobile App
Create a Weather App With TypeScript and NativeScript
Wernher-Bel Ancheta
Mobile Development
Introducing Vue and Weex for Native Mobile Apps
Lawrence Turton
Ionic
Get Started With Ionic Services: Auth
Wernher-Bel Ancheta
For a comprehensive introduction to NativeScript, try our video course Code a Mobile App With NativeScript. In this course, Keyvan Kasaei will show you step by step how to build a simple application. Along the way, you'll learn how to implement a simple app workflow with network requests, an MVVM architecture, and some of the most important NativeScript UI components. By the end, you'll understand why you should consider NativeScript for your next mobile app project.
  http://j.mp/2tBd5DL via Envato Tuts+ Code URL : http://j.mp/2etecmc
0 notes
iyarpage · 7 years
Text
Android SDK Versions Tutorial with Kotlin
Update Note: This tutorial has been updated to Kotlin by Eric Crawford. The original tutorial was written by Eunice Obugyei.
Ever since the first release of Android, the range of supported devices has grown to represent a wide array of phones, smart watches and more. Everything necessary to start developing Android applications for those devices falls under one specification called the Android SDK (software development kit). New SDK versions are released with each new version of Android, and that is our focus for this tutorial.
These new sdk versions take advantage of the increased processing power available on the latest devices to provide great new features. Awesome, right? The flip side, of course, is that Android developers face the challenge of making sure an application will work on a range of devices running different versions of the Android SDK.
Luckily, there are some best practice guidelines and tools to help get the work done without compromising on UX or deadlines.
In this Android SDK Versions tutorial you’ll learn about:
Android SDK versions and API Levels
Android Support Libraries and their importance
How to use the Android Support Library to make an app backward-compatible
How to use the CardView Support Library
To put theory into practice, you’ll play with a simple application called Continents. Continents gives short descriptions of the continents in the world.
Note: This Android SDK Versions tutorial assumes you are already familiar with the basics of Android development and Kotlin. If you are completely new to Android development, read Beginning Android Development and our other Android and Kotlin tutorials to familiarize yourself with the basics.
Note: This tutorial requires Android Studio 3.0 Beta 4 or later.
Getting Started
Download the starter project for this tutorial and extract the downloaded zip file to get the project.
Select Open an existing Android Studio project from the Quick Start menu to open the starter project:
If you are on a windows machine you can also select File \ Open. Navigate to and select the starter project folder.
If you are prompted to install (or update to) a new version of the Android build tools, or are prompted to update your Gradle plugin version, please do so.
Build and run the project on the emulator or device to make sure it compiles and runs correctly.
Emulating Different SDK Versions
We want to try running the sample app on different Android versions. But it’s unlikely anyone has an Android device for every single API Level of the SDK. So first, let’s learn how to set up emulators with different SDK versions.
To set up an emulator, locate the AVD (Android Virtual Device) Manager on the Android Studio Toolbar.
Creating A Virtual Device
If the Toolbar is not showing, select View \ Toolbar to show it. You can also select select Tools \ Android \ AVD Manager to open the AVD Manager.
Click the Create a virtual device button. That will open the Select Hardware section of the Virtual Device Configuration window.
Select a device of your choice and click Next. That opens the System Image section, which currently shows recommended system images.
The x86 Images tab will list all the x86 emulator images. The Other Images tab will show both ARM and x86 emulator images.
Note: It is recommended to always use x86 images. These will run the fastest on most pc and mac computers
Downloading A System Image
To download a system image that you do not already have installed, click the Download link by the release name.
Notice that the Android platform currently has fifteen major versions of the SDK . Starting with Android 1.5, major versions of the SDK have been developed under a confectionery-themed code name. Google has managed to choose these code names in an alphabetical order. They haven’t run out of names of sweets yet :]
Each version (minor or major) of the Android SDK has an integer value that uniquely identifies it. This unique identifier is referred to as the API Level. The higher the API Level, the later the version. For developers, API Level is important because it is what determines the range of devices an app can run on.
Let’s look at an example, the Android 8.0 release. We can see that:
It is the most recent version of the Android SDK
Its version number is 8.0
Its code name is Oreo
It has an API Level of 26
For this tutorial, we will need at least two emulators, one with API Level 15 and another one with API Level 26.
Going back to the System Image screen in Android Studio, click the Download button for each of the SDK versions you will need for this tutorial that you have not already downloaded (Level 15 and Level 26). Then select the system image for Level 26 and click Next.
On the next screen, click Finish.
Repeat the same steps to setup an emulator with API level 15. You may choose one with API level 16 instead if you are unable to download one with API level 15.
First Run
Try running the sample app on the emulator running API Level 26:
It all looks great, right? But if you were to try and run the app on a device with API Level lower than 26 it wouldn’t run. This is because the app only runs on devices that run Android API Level 26 and upwards, which isn’t great for older devices. Later, you’ll learn how to extend the app’s support from Android API Level 26 to as low as Android API Level 14.
SDK Versions and API Levels
As mentioned earlier, the API Level is a unique integer that identifies a specific version of the Android SDK. Let’s look at how to specify API Levels in Android Studio to compile and release an application.
Open build.gradle for the app module:
Here we can see three important attributes:
minSdkVersion is the minimum API Level with which the app is compatible. The Android system will prevent a user from installing the application if the system’s API Level is lower than the value specified in this attribute. Android requires the minSdkVersion attribute to always be set.
targetSdkVersion is the API Level that the application targets. This attribute informs the system that you have tested against the target version. The targetSdkVersion defaults to the minSdkVersion if not specified.
compileSdkVersion specifies the API Level to compile the application against.
These attributes can be adjusted in the app modules build.gradle file.
Note on SDK previews: It’s important to know that when you set the compileSdkVersion to a preview release of the Android framework, Android Studio will force the minSdkVersion and targetSdkVersion to equal the exact same string as compileSdkVersion. This policy is necessary to prevent situations where you might upload your app to the Google Play Store. As a result, you can only run applications where compileSdkVersion is set to a preview release on emulators with that exact same preview and you won’t be able to run it on older devices.
Backward Compatibility
The Android SDK is by default forward compatible but not backward compatible — this means that an application that is built with and supports a minimum SDK version of 3.0 can be installed on any device running Android versions 3.0 and upwards. But not on devices running Android versions below 3.0.
Since the Android SDK is not backward compatible, you should choose the minimum SDK carefully. This means striking a balance between supporting a wide range of devices and designing an app that implements useful features in later SDK versions.
For example, when Android 3.0 was released in 2011, the Action Bar was unleashed on the Android Community. Since the Action Bar was only supported in Android 3.0 and later, using it in an app meant choosing either a cool user interface or supporting devices that ran older versions of the SDK. Sometimes you can’t have your honeycomb and eat it too :[
Or can you? To help with the Action Bar issue, the Android Support Library introduced a backward-compatible version in the v7-appcompat support library. So it would allow developers to support older versions of the SDK and still use the latest Action Bar APIs in their apps. Sweet! Honeycomb for everyone!
Let’s take a deeper look at what the Support Library does and how it works.
Note on picking minimum sdk version: Google provides a Dashboard that breaks down the user distribution percentage per api level. You can use this to help target a good percentage of users.
Android Support Libraries
A wide range of components make up what is referred to as the “Support Library” or “Support Libraries,” and they can be categorized in two groups:
The AppCompat library: The intention here is to make sure all (or most) of the framework APIs for the latest API Level have been backported to earlier versions and can be found in this single library. The first version of AppCompat was released at Google I/O 2013.
The goal of this first release was to allow developers to backport the ActionBar to devices running IceScreamSandwich level. This gave API parity to the framework across as many API Levels as possible. Since then, the AppCompat library has continued to evolve. And with Android L the support library is now at the point where the API is equivalent to the framework itself — the first time that has ever happened :]
Others: The rest of the libraries that make up the Support Library essentially provide new functionality with the same consideration for backward compatibility (palette, gridview, gridlayout, recycler view, material design widgets).
When you break these up into independent libraries, you can pick and choose the ones you need in your project. It’s important to note that each support library is backward-compatible to a specific API Level. And they are usually named based on which API Level they are backward-compatible to. For example, v7-appcompat provides backward compatibility to API Level 7.
You can find the full list of components that fall under the Support Library in the Android documentation.
Note: Support Library minimum sdk change: Beginning with Support Library release 26.0.0, Google has changed the minimum supported level to Api 14. This means that your minimum sdk version cannot be set below Api level 14 when using version 26.0.0+ of the Support Library.
How to Use an Android Support Library
Time to see an Android support library in action! Open MainActivity.kt. As you may have noticed in the onCreate() method, the app uses a Toolbar (which is part of the material design patterns) instead of an Action Bar.
The Toolbar was added in API 21 (Android Lollipop) as a flexible widget that can be used anywhere in layouts, be animated and change in size, unlike the Action Bar.
Thanks to AppCompat, that feature has been back-ported all the way to API 14, which is code-named Ice Cream Sandwich (are you hungry yet?). You’re going to use the v7-appcompat support library to extend your app’s compatibility to a minSdkVersion of 15.
Update Build File
First add google() to the Maven repositories in your project-level build.gradle file, if it’s not already there:
repositories { jcenter() google() }
Now, open build.gradle for the app module and add the following to the dependencies section:
implementation "com.android.support:appcompat-v7:26.0.1"
By adding this, you’re declaring the appcompat-v7 support library as a dependency for your application. You can ignore the warning to use a newer version of the Support library. Though you may update, it’s recommended you stick to the one in this tutorial.
Next, change the minSdkVersion attribute to 15.
minSdkVersion 15
Here you’re declaring that the app should be able to run on devices with Android SDK version 4.0.4. Now try running your application on an emulator running API Level 15. You should see the following exceptions in the logcat:
The important line to look for is:
Caused by: java.lang.ClassNotFoundException: android.widget.Toolbar
The ClassNotFoundException error indicates that there is no such class in the SDK version you’re running the app against. Indeed, it’s only available in API Level 21, while you’re currently running API Level 15.
Update For Backward Compatibility
You’re going to update the code to use the backward-compatible version of Toolbar. In MainActivity.kt, and update the android.widget.Toolbar import statement to match the following:
import android.support.v7.widget.Toolbar
This replaces the SDK import with one from the AppCompat library.
Next import the AppCompatActivity from the AppCompat library:
import android.support.v7.app.AppCompatActivity
Next update the MainActivity class definition line so that it inherits from AppCompatActivity:
class MainActivity : AppCompatActivity(), ContinentSelectedListener
Once again, you’re replacing a class from the latest SDKs with one that exists in the support library.
You now need to work through the class and replace some method calls with their support library equivalents:
Find the call to setActionBar(toolbar) at the end of the onCreate() method body and update it to setSupportActionBar(toolbar).
Find the calls to actionBar? in onContinentSelected(), goToContinentList(), and onBackPressed() and replace both of them with supportActionBar?.
Replace the calls to fragmentManager() in onContinentSelected() and goToContinentList() with supportFragmentManager().
Update Fragment Classes
Open DescriptionFragment.kt and MainFragment.kt, find the following line:
import android.app.Fragment
and update to match the following:
import android.support.v4.app.Fragment
Here you’re using the support version of the Fragment class instead of the one in the main SDK. The latter can only be used in apps with a minSdkVersion of 14 and above.
Note: AppCompat v7 depends on the v4 Support Library. That’s why you can also use all the APIs in the android.support.v4.app package.
So far you’ve replaced all the main API calls with corresponding methods from the support library. Next you will need to update your layout files to use the Support Library.
In the res / layout folder, open toolbar_custom.xml and do the following:
Change android.widget.Toolbar to android.support.v7.widget.Toolbar
Change ?android:attr/actionBarSize to ?attr/actionBarSize
Again, all this does is change the package name from android to v7-appcompat.
Now that all of the compile-time errors have been checked and fixed, try to run the app again. You will now get the following run-time error:
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.raywenderlich.continents/com.raywenderlich.continents.MainActivity}: java.lang.IllegalStateException: You need to use a Theme.AppCompat theme (or descendant) with this activity.
Update Styles
The error message is pretty self-explanatory, but why do you need to use the AppCompat theme? A feature from the Lollipop release of AppCompat is the different approach to theming. One of the interesting things about this is the capability to get an L-friendly version of your app on prior versions. If an app uses the framework version of everything (Activity instead of AppCompatActivity for example), it would only get the material theme on phones with the L release. Devices with prior releases would get the default theme for those releases. The goal of the AppCompat theming feature is to have a consistent experience across all devices.
In the res\values folder, open styles.xml, and change android:Theme.Black.NoTitleBar to Theme.AppCompat.NoActionBar.
Now build and run. You can test the app on an API 15 device or emulator as well.
Well done! The sample app is now backward compatible. Ice cream sandwich and lollipops and jelly beans for everyone!
Let’s throw in some cards to make the detail screen look nicer.
How to Use the Card View Support Library
Open build.gradle for the app module and add the following to the dependencies section:
implementation "com.android.support:cardview-v7:26.0.1"
Adding this declares the v7-cardview support library as a dependency for the application.
Open the fragment_description.xml file and place the ImageView in a CardView:
<android.support.v7.widget.CardView android:id="@+id/card_view" android:layout_width="match_parent" android:layout_height="0dp" android:layout_gravity="center" android:layout_weight="1" card_view:cardBackgroundColor="#316130" card_view:cardElevation="20dp"> <ImageView android:id="@+id/continentImage" android:layout_width="match_parent" android:layout_height="match_parent" android:contentDescription="@string/continent_image_description" android:paddingBottom="@dimen/activity_vertical_margin" android:src="@drawable/africa" /> </android.support.v7.widget.CardView>
Notice that when using widgets from the Support Library, some XML attributes (cardBackgroundColor and cardElevation for the CardView) are not prefixed with “android.” That’s because they come from the Support Library API as opposed to the Android framework. Hit option+return (or Alt+Enter on PC) if you need to setup the card_view namespace in the xml file.
Now, build and run the project:
Cool, you’ve added this new-style cardview to your app and using the compatibility library it works from modern versions of Android, right back to ancient API-level 15.
Did You Say Material Design?
You’ve successfully used the AppCompat theming to give the app the Android Lollipop look and feel across a wide range of SDK versions. In addition to these elements, the Material Design specification includes many more patterns and widgets not contained in AppCompat. This is where the Design Library comes into play. It provides widgets such as navigation drawers, floating action buttons, snackbars and tabs. Let’s include it in the project and add a floating action button.
In build.gradle add the following in the dependencies section:
implementation "com.android.support:design:26.0.1"
Next add the following XML element above the closing tag for FrameLayout in fragment_description.xml:
<android.support.design.widget.FloatingActionButton android:id="@+id/search_button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="bottom|end" android:layout_margin="16dp" android:src="@drawable/ic_search_white_18dp" />
Build and run. You will see the floating button as expected.
Backport All the Things?
Some features in the latest releases of the SDK are just too complex to backport. Ultimately, it’s your call to strike the right balance between performance and usability. If you find yourself wanting to use an unavailable framework API, you can check for the API Level at run-time.
For the following snippet from MainActivity, import the classes from the base package instead of the Support Library package. Then in the onContinentSelected, add the following after the description fragment is instantiated but before the fragment transaction:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { descriptionFragment.enterTransition = Fade() mainFragment?.exitTransition = Fade() descriptionFragment.exitTransition = Slide(Gravity.BOTTOM) mainFragment?.reenterTransition = Fade() descriptionFragment.allowReturnTransitionOverlap = true }
Build and run on both emulators. You should see no animations on the emulator running API Level 15, but notice the fade in and slide out on the emulators running API Level 25 and above:
Where To Go From Here?
Congratulations! Finally you’ve learned about Android SDK versions and their sweet code names. You made an API Level 26 application backward-compatible to API Level 15, and used the cardview and design library to add additional components. You might also have a sugar craving :]
Blame Android.
The final project for this Android SDK Versions tutorial can be downloaded here.
If you are interested in Android SDK version history, check out this wikipedia page or the versions page on the Android developer site. You can also read further about the minSdkVersion and targetSdkVersion attributes from the manifest page on the developer site. Finally, check out the developer pages on Support libraries and its feature list.
We hope you enjoyed this Android SDK Versions tutorial, and if you have any questions or comments, please join the forum discussion below!
The post Android SDK Versions Tutorial with Kotlin appeared first on Ray Wenderlich.
Android SDK Versions Tutorial with Kotlin published first on http://ift.tt/2fA8nUr
0 notes
iyarpage · 7 years
Text
Android SDK Versions Tutorial with Kotlin
Update Note: This tutorial has been updated to Kotlin by Eric Crawford. The original tutorial was written by Eunice Obugyei.
Ever since the first release of Android, the range of supported devices has grown to represent a wide array of phones, smart watches and more. Everything necessary to start developing Android applications for those devices falls under one specification called the Android SDK (software development kit). New SDK versions are released with each new version of Android, and that is our focus for this tutorial.
These new sdk versions take advantage of the increased processing power available on the latest devices to provide great new features. Awesome, right? The flip side, of course, is that Android developers face the challenge of making sure an application will work on a range of devices running different versions of the Android SDK.
Luckily, there are some best practice guidelines and tools to help get the work done without compromising on UX or deadlines.
In this Android SDK Versions tutorial you’ll learn about:
Android SDK versions and API Levels
Android Support Libraries and their importance
How to use the Android Support Library to make an app backward-compatible
How to use the CardView Support Library
To put theory into practice, you’ll play with a simple application called Continents. Continents gives short descriptions of the continents in the world.
Note: This Android SDK Versions tutorial assumes you are already familiar with the basics of Android development and Kotlin. If you are completely new to Android development, read Beginning Android Development and our other Android and Kotlin tutorials to familiarize yourself with the basics.
Note: This tutorial requires Android Studio 3.0 Beta 4 or later.
Getting Started
Download the starter project for this tutorial and extract the downloaded zip file to get the project.
Select Open an existing Android Studio project from the Quick Start menu to open the starter project:
If you are on a windows machine you can also select File \ Open. Navigate to and select the starter project folder.
If you are prompted to install (or update to) a new version of the Android build tools, or are prompted to update your Gradle plugin version, please do so.
Build and run the project on the emulator or device to make sure it compiles and runs correctly.
Emulating Different SDK Versions
We want to try running the sample app on different Android versions. But it’s unlikely anyone has an Android device for every single API Level of the SDK. So first, let’s learn how to set up emulators with different SDK versions.
To set up an emulator, locate the AVD (Android Virtual Device) Manager on the Android Studio Toolbar.
Creating A Virtual Device
If the Toolbar is not showing, select View \ Toolbar to show it. You can also select select Tools \ Android \ AVD Manager to open the AVD Manager.
Click the Create a virtual device button. That will open the Select Hardware section of the Virtual Device Configuration window.
Select a device of your choice and click Next. That opens the System Image section, which currently shows recommended system images.
The x86 Images tab will list all the x86 emulator images. The Other Images tab will show both ARM and x86 emulator images.
Note: It is recommended to always use x86 images. These will run the fastest on most pc and mac computers
Downloading A System Image
To download a system image that you do not already have installed, click the Download link by the release name.
Notice that the Android platform currently has fifteen major versions of the SDK . Starting with Android 1.5, major versions of the SDK have been developed under a confectionery-themed code name. Google has managed to choose these code names in an alphabetical order. They haven’t run out of names of sweets yet :]
Each version (minor or major) of the Android SDK has an integer value that uniquely identifies it. This unique identifier is referred to as the API Level. The higher the API Level, the later the version. For developers, API Level is important because it is what determines the range of devices an app can run on.
Let’s look at an example, the Android 8.0 release. We can see that:
It is the most recent version of the Android SDK
Its version number is 8.0
Its code name is Oreo
It has an API Level of 26
For this tutorial, we will need at least two emulators, one with API Level 15 and another one with API Level 26.
Going back to the System Image screen in Android Studio, click the Download button for each of the SDK versions you will need for this tutorial that you have not already downloaded (Level 15 and Level 26). Then select the system image for Level 26 and click Next.
On the next screen, click Finish.
Repeat the same steps to setup an emulator with API level 15. You may choose one with API level 16 instead if you are unable to download one with API level 15.
First Run
Try running the sample app on the emulator running API Level 26:
It all looks great, right? But if you were to try and run the app on a device with API Level lower than 26 it wouldn’t run. This is because the app only runs on devices that run Android API Level 26 and upwards, which isn’t great for older devices. Later, you’ll learn how to extend the app’s support from Android API Level 26 to as low as Android API Level 14.
SDK Versions and API Levels
As mentioned earlier, the API Level is a unique integer that identifies a specific version of the Android SDK. Let’s look at how to specify API Levels in Android Studio to compile and release an application.
Open build.gradle for the app module:
Here we can see three important attributes:
minSdkVersion is the minimum API Level with which the app is compatible. The Android system will prevent a user from installing the application if the system’s API Level is lower than the value specified in this attribute. Android requires the minSdkVersion attribute to always be set.
targetSdkVersion is the API Level that the application targets. This attribute informs the system that you have tested against the target version. The targetSdkVersion defaults to the minSdkVersion if not specified.
compileSdkVersion specifies the API Level to compile the application against.
These attributes can be adjusted in the app modules build.gradle file.
Note on SDK previews: It’s important to know that when you set the compileSdkVersion to a preview release of the Android framework, Android Studio will force the minSdkVersion and targetSdkVersion to equal the exact same string as compileSdkVersion. This policy is necessary to prevent situations where you might upload your app to the Google Play Store. As a result, you can only run applications where compileSdkVersion is set to a preview release on emulators with that exact same preview and you won’t be able to run it on older devices.
Backward Compatibility
The Android SDK is by default forward compatible but not backward compatible — this means that an application that is built with and supports a minimum SDK version of 3.0 can be installed on any device running Android versions 3.0 and upwards. But not on devices running Android versions below 3.0.
Since the Android SDK is not backward compatible, you should choose the minimum SDK carefully. This means striking a balance between supporting a wide range of devices and designing an app that implements useful features in later SDK versions.
For example, when Android 3.0 was released in 2011, the Action Bar was unleashed on the Android Community. Since the Action Bar was only supported in Android 3.0 and later, using it in an app meant choosing either a cool user interface or supporting devices that ran older versions of the SDK. Sometimes you can’t have your honeycomb and eat it too :[
Or can you? To help with the Action Bar issue, the Android Support Library introduced a backward-compatible version in the v7-appcompat support library. So it would allow developers to support older versions of the SDK and still use the latest Action Bar APIs in their apps. Sweet! Honeycomb for everyone!
Let’s take a deeper look at what the Support Library does and how it works.
Note on picking minimum sdk version: Google provides a Dashboard that breaks down the user distribution percentage per api level. You can use this to help target a good percentage of users.
Android Support Libraries
A wide range of components make up what is referred to as the “Support Library” or “Support Libraries,” and they can be categorized in two groups:
The AppCompat library: The intention here is to make sure all (or most) of the framework APIs for the latest API Level have been backported to earlier versions and can be found in this single library. The first version of AppCompat was released at Google I/O 2013.
The goal of this first release was to allow developers to backport the ActionBar to devices running IceScreamSandwich level. This gave API parity to the framework across as many API Levels as possible. Since then, the AppCompat library has continued to evolve. And with Android L the support library is now at the point where the API is equivalent to the framework itself — the first time that has ever happened :]
Others: The rest of the libraries that make up the Support Library essentially provide new functionality with the same consideration for backward compatibility (palette, gridview, gridlayout, recycler view, material design widgets).
When you break these up into independent libraries, you can pick and choose the ones you need in your project. It’s important to note that each support library is backward-compatible to a specific API Level. And they are usually named based on which API Level they are backward-compatible to. For example, v7-appcompat provides backward compatibility to API Level 7.
You can find the full list of components that fall under the Support Library in the Android documentation.
Note: Support Library minimum sdk change: Beginning with Support Library release 26.0.0, Google has changed the minimum supported level to Api 14. This means that your minimum sdk version cannot be set below Api level 14 when using version 26.0.0+ of the Support Library.
How to Use an Android Support Library
Time to see an Android support library in action! Open MainActivity.kt. As you may have noticed in the onCreate() method, the app uses a Toolbar (which is part of the material design patterns) instead of an Action Bar.
The Toolbar was added in API 21 (Android Lollipop) as a flexible widget that can be used anywhere in layouts, be animated and change in size, unlike the Action Bar.
Thanks to AppCompat, that feature has been back-ported all the way to API 14, which is code-named Ice Cream Sandwich (are you hungry yet?). You’re going to use the v7-appcompat support library to extend your app’s compatibility to a minSdkVersion of 15.
Update Build File
First add google() to the Maven repositories in your project-level build.gradle file, if it’s not already there:
repositories { jcenter() google() }
Now, open build.gradle for the app module and add the following to the dependencies section:
implementation "com.android.support:appcompat-v7:26.0.1"
By adding this, you’re declaring the appcompat-v7 support library as a dependency for your application. You can ignore the warning to use a newer version of the Support library. Though you may update, it’s recommended you stick to the one in this tutorial.
Next, change the minSdkVersion attribute to 15.
minSdkVersion 15
Here you’re declaring that the app should be able to run on devices with Android SDK version 4.0.4. Now try running your application on an emulator running API Level 15. You should see the following exceptions in the logcat:
The important line to look for is:
Caused by: java.lang.ClassNotFoundException: android.widget.Toolbar
The ClassNotFoundException error indicates that there is no such class in the SDK version you’re running the app against. Indeed, it’s only available in API Level 21, while you’re currently running API Level 15.
Update For Backward Compatibility
You’re going to update the code to use the backward-compatible version of Toolbar. In MainActivity.kt, and update the android.widget.Toolbar import statement to match the following:
import android.support.v7.widget.Toolbar
This replaces the SDK import with one from the AppCompat library.
Next import the AppCompatActivity from the AppCompat library:
import android.support.v7.app.AppCompatActivity
Next update the MainActivity class definition line so that it inherits from AppCompatActivity:
class MainActivity : AppCompatActivity(), ContinentSelectedListener
Once again, you’re replacing a class from the latest SDKs with one that exists in the support library.
You now need to work through the class and replace some method calls with their support library equivalents:
Find the call to setActionBar(toolbar) at the end of the onCreate() method body and update it to setSupportActionBar(toolbar).
Find the calls to actionBar? in onContinentSelected(), goToContinentList(), and onBackPressed() and replace both of them with supportActionBar?.
Replace the calls to fragmentManager() in onContinentSelected() and goToContinentList() with supportFragmentManager().
Update Fragment Classes
Open DescriptionFragment.kt and MainFragment.kt, find the following line:
import android.app.Fragment
and update to match the following:
import android.support.v4.app.Fragment
Here you’re using the support version of the Fragment class instead of the one in the main SDK. The latter can only be used in apps with a minSdkVersion of 14 and above.
Note: AppCompat v7 depends on the v4 Support Library. That’s why you can also use all the APIs in the android.support.v4.app package.
So far you’ve replaced all the main API calls with corresponding methods from the support library. Next you will need to update your layout files to use the Support Library.
In the res / layout folder, open toolbar_custom.xml and do the following:
Change android.widget.Toolbar to android.support.v7.widget.Toolbar
Change ?android:attr/actionBarSize to ?attr/actionBarSize
Again, all this does is change the package name from android to v7-appcompat.
Now that all of the compile-time errors have been checked and fixed, try to run the app again. You will now get the following run-time error:
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.raywenderlich.continents/com.raywenderlich.continents.MainActivity}: java.lang.IllegalStateException: You need to use a Theme.AppCompat theme (or descendant) with this activity.
Update Styles
The error message is pretty self-explanatory, but why do you need to use the AppCompat theme? A feature from the Lollipop release of AppCompat is the different approach to theming. One of the interesting things about this is the capability to get an L-friendly version of your app on prior versions. If an app uses the framework version of everything (Activity instead of AppCompatActivity for example), it would only get the material theme on phones with the L release. Devices with prior releases would get the default theme for those releases. The goal of the AppCompat theming feature is to have a consistent experience across all devices.
In the res\values folder, open styles.xml, and change android:Theme.Black.NoTitleBar to Theme.AppCompat.NoActionBar.
Now build and run. You can test the app on an API 15 device or emulator as well.
Well done! The sample app is now backward compatible. Ice cream sandwich and lollipops and jelly beans for everyone!
Let’s throw in some cards to make the detail screen look nicer.
How to Use the Card View Support Library
Open build.gradle for the app module and add the following to the dependencies section:
implementation "com.android.support:cardview-v7:26.0.1"
Adding this declares the v7-cardview support library as a dependency for the application.
Open the fragment_description.xml file and place the ImageView in a CardView:
<android.support.v7.widget.CardView android:id="@+id/card_view" android:layout_width="match_parent" android:layout_height="0dp" android:layout_gravity="center" android:layout_weight="1" card_view:cardBackgroundColor="#316130" card_view:cardElevation="20dp"> <ImageView android:id="@+id/continentImage" android:layout_width="match_parent" android:layout_height="match_parent" android:contentDescription="@string/continent_image_description" android:paddingBottom="@dimen/activity_vertical_margin" android:src="@drawable/africa" /> </android.support.v7.widget.CardView>
Notice that when using widgets from the Support Library, some XML attributes (cardBackgroundColor and cardElevation for the CardView) are not prefixed with “android.” That’s because they come from the Support Library API as opposed to the Android framework. Hit option+return (or Alt+Enter on PC) if you need to setup the card_view namespace in the xml file.
Now, build and run the project:
Cool, you’ve added this new-style cardview to your app and using the compatibility library it works from modern versions of Android, right back to ancient API-level 15.
Did You Say Material Design?
You’ve successfully used the AppCompat theming to give the app the Android Lollipop look and feel across a wide range of SDK versions. In addition to these elements, the Material Design specification includes many more patterns and widgets not contained in AppCompat. This is where the Design Library comes into play. It provides widgets such as navigation drawers, floating action buttons, snackbars and tabs. Let’s include it in the project and add a floating action button.
In build.gradle add the following in the dependencies section:
implementation "com.android.support:design:26.0.1"
Next add the following XML element above the closing tag for FrameLayout in fragment_description.xml:
<android.support.design.widget.FloatingActionButton android:id="@+id/search_button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="bottom|end" android:layout_margin="16dp" android:src="@drawable/ic_search_white_18dp" />
Build and run. You will see the floating button as expected.
Backport All the Things?
Some features in the latest releases of the SDK are just too complex to backport. Ultimately, it’s your call to strike the right balance between performance and usability. If you find yourself wanting to use an unavailable framework API, you can check for the API Level at run-time.
For the following snippet from MainActivity, import the classes from the base package instead of the Support Library package. Then in the onContinentSelected, add the following after the description fragment is instantiated but before the fragment transaction:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { descriptionFragment.enterTransition = Fade() mainFragment?.exitTransition = Fade() descriptionFragment.exitTransition = Slide(Gravity.BOTTOM) mainFragment?.reenterTransition = Fade() descriptionFragment.allowReturnTransitionOverlap = true }
Build and run on both emulators. You should see no animations on the emulator running API Level 15, but notice the fade in and slide out on the emulators running API Level 25 and above:
Where To Go From Here?
Congratulations! Finally you’ve learned about Android SDK versions and their sweet code names. You made an API Level 26 application backward-compatible to API Level 15, and used the cardview and design library to add additional components. You might also have a sugar craving :]
Blame Android.
The final project for this Android SDK Versions tutorial can be downloaded here.
If you are interested in Android SDK version history, check out this wikipedia page or the versions page on the Android developer site. You can also read further about the minSdkVersion and targetSdkVersion attributes from the manifest page on the developer site. Finally, check out the developer pages on Support libraries and its feature list.
We hope you enjoyed this Android SDK Versions tutorial, and if you have any questions or comments, please join the forum discussion below!
The post Android SDK Versions Tutorial with Kotlin appeared first on Ray Wenderlich.
Android SDK Versions Tutorial with Kotlin published first on http://ift.tt/2fA8nUr
0 notes