Android Tutorial Android home screen widgets tutorial - part I

Status
Not open for further replies.
Edit: widgets are handled with receivers now. See the attached example.

This tutorial will explain how to implement your own home screen widgets (also named App Widgets).

It is important to understand that the widgets are created and managed in another process, different than the process that your application is running in. The home screen application is hosting your widgets.
This means that it is not possible to directly access the widgets views. Instead we are using a special object named RemoteViews which gives us indirect access to the widget views.

Widgets do not support all views types. The following views are supported:
- Button (default drawable)
- Label (ColorDrawable or GradientDrawable)
- Panel (ColorDrawable or GradientDrawable)
- ImageView
- ProgressBar (both modes)

All views support the Click event and no other event.

The widget layout and configuration must be defined with XML files. During compilation Basic4android reads the layout file created with the designer and generates the required XML files.

Each widget is tied to a Service module. Through this module the widget is created and updated.

Creating a widget - step by step guide

- Add a Service module. Note that the service module handling the widget is a standard service.
- Design the widget layout with the designer. First add a Panel and then add the other views to this Panel.
The widget layout will be made from this panel.
- Add code similar to the following code the service module:
B4X:
Sub Process_Globals
    Dim rv As RemoteViews
End Sub

Sub Service_Create
    rv = ConfigureHomeWidget("LayoutFile", "rv", 0, "Widget Name")
End Sub

Sub Service_Start (StartingIntent As Intent)
     RV.HandleWidgetEvents(StartingIntent)
    Sleep(0)
    Service.StopAutomaticForeground
End Sub

Sub rv_RequestUpdate
    rv.UpdateWidget
End Sub

Sub rv_Disabled
    StopService("")
End Sub

Sub Service_Destroy

End Sub
- Compile and run your application. Go to the home screen, long press on the screen and you will see your widget listed on the widgets list.

ConfigureHomeWidget is a special keyword. At runtime it creates the RemoteViews object from the layout and set the events. At compile time the compiler generates the required files based on the arguments of this keyword.
The four parameters are: layout file, event name, update interval and the widget name.
Event name sets the subs that will handle the RequestUpdate and Disabled events.
The widget can be configured to update itself automatically. The interval, measured in minutes, defines how often will the widget request to update itself. Set to 0 to disable automatic updates. Updating the widget too often will have a bad impact on the battery. The minimum value is 30 minutes.
Widget name - the name that will appear in the widgets list.

As these arguments are read by the compiler, only strings or numbers are accepted.

Events:
B4X:
Sub Service_Start (StartingIntent As Intent)
    If rv.HandleWidgetEvents(StartingIntent) Then Return
End Sub
The above code checks the Intent message that caused this service to start and is responsible for raising the events related to the widget. It returns true if an event was raised.
The widget raises two events. RequestUpdate is raised when the widget needs to update itself. It will fire after adding the widget to the screen, after the device has booted, based on the scheduled updating interval (if set) or after the application was updated.
The Disabled event is raised when the last instance of our widget is removed from the screen.

As mentioned above all views support the Click event. All that needs to be done in order to handle the click event of a button named Button1 is to add a sub named Button1_Click (the sub name should actually match the EventName property which is by default the same as the name).

[/code]Modifying the widget:
It is not possible to directly access the widget views. Instead we need to use one of the RemoteView.Set methods.
If we want to change the text of a label named Label1 then we need to write the following code:
B4X:
rv.SetText("Label1", "This is the new text.")
'do more changes if needed
rv.UpdateWidget
After writing all the changes we call rv.UpdateWidget to send the updates to the widget.

A simple example is attached.
The example adds a simple widget. The widget doesn't do anything useful.
SS-2011.07.11-12.55.04.png

Note that it is recommended to test widgets in Release mode (the widget will fail in debug mode when the process is started by the OS).
 

Attachments

  • HomeWidgets.zip
    24.6 KB · Views: 797
Last edited:

NFOBoy

Active Member
Licensed User
Longtime User
Another (slightly different) Widget Height Question

Ok, got it that based on specific device, cell size can vary.

For my particular case, if I go above 60 for the width, 62 for the height, I no longer have a 1x1.

The problem is, that is way smaller than other 1x1 widgets that are on my display (like the battery monitor 1x1 widget, that uses 15% more height than my widget.)

It almost appears that some space is reserved for the "name" of the widget on the bottom part of it, as the top lines up nicely with other widgets (a tiny, sukoshi, bit lower maybe, but the bottom doesn't reach as low, but matches up surprisingly well with all the apps... just no name)

Anyone know the trick on getting the widget to actually get max height available? I would edit the xml file, but don't know what to put in, as even a slightly bigger number just make it a 1x2, which is not my desire.
 

corwin42

Expert
Licensed User
Longtime User
Anyone know the trick on getting the widget to actually get max height available? I would edit the xml file, but don't know what to put in, as even a slightly bigger number just make it a 1x2, which is not my desire.

Unfortunately B4A uses a very static layout (AbsoluteLayout) for the widgets but widget sizes vary on all devices so the static solution isn't the best choice. It is possible to create nice scaling widgets with B4A but you will change the generated xml files completely.

You can find a small example of what can be done with B4A and widgets in the following tutorial:
Multiple instances of the same widget

The Worldclock example has a fully scalable layout and uses the standard Android widget sizes for all Android versions. The widget_info.xml and all layout files are created manually (not with the B4A Designer)

The trick is to create a basic layout with the B4A designer with all your views you want in it. Then modify the generated layout xml and make it scalable with LinearLayouts/RelativeLayouts etc. Then make the layout read only so B4A won't touch them anymore.

The example was a test to check whats possible with B4A and widgets.
 

NFOBoy

Active Member
Licensed User
Longtime User
Are you sure that the other icon is a widget and not a regular shortcut?

If they aren't, then I dunno why I add them from the Widget selector screen! :)

Unfortunately B4A uses a very static layout (AbsoluteLayout) for the widgets but widget sizes vary on all devices so the static solution isn't the best choice. It is possible to create nice scaling widgets with B4A but you will change the generated xml files completely.

You can find a small example of what can be done with B4A and widgets in the following tutorial:
Multiple instances of the same widget
.

Thanks for the info.. I will take a look and see what I can learn to do!

Ros
 

Ionut Indigo

Member
Licensed User
Longtime User
After playing with the Designer to make a 4x2 widget, and making it bigger than 4x3, the widget doesn't appear and i want to put it on screen i get "Your home screen is full please remove an item to add another." Tried it on an empty screen and going back to 4x2 and lower and i still get that message.
 

gadgetmonster

Active Member
Licensed User
Longtime User
Hi,

My app is a database driven app and I have developed a couple of widgets that show the last entry made. Instead of refreshing the widgets every x minutes, I need to perform a widget refresh when the user presses save from within my app.

So I did this on the save click event:

B4X:
CallSubDelayed (Widget_1, "rv_RequestUpdate")

Whilst this appears to work, when you have 3 or 4 widgets to update it takes a little time and seems to cause my app to stop.

Is there a better way?
 

gadgetmonster

Active Member
Licensed User
Longtime User
1. Yes.
2. You can use the enabled event to know which widgets were added. Make sure to save the information in a file as the process will eventually be killed.

Hi Erel,

Sorry for sounding thick but I just wanted to clarify.

1. To speed things up I have declared the SQL and Cursor variables in widgets Process_Globals
2. In Service_Start I initialize the SQL connection thinking that this will remain open so long as the service is running. If it gets re-started then the db connection will be opened again.
3. I'm a little confused on this enabled event, which enabled event am I checking here as the service doesn't seem to have one?

Many thanks
 

DonManfred

Expert
Licensed User
Longtime User
As till today everything was ok with my widget. Now, without any changes to the Widget-Source (the layout has changed) i get an compile-Error with the line

B4X:
rv = ConfigureHomeWidget("widget", "rv", 30, "Bestellungen", True)

Compiling code. Error
Error compiling program.
Error description: Der angegebene Schlüssel war nicht im Wörterbuch angegeben.
Occurred on line: 12
rv = ConfigureHomeWidget("widget", "rv", 30, "Bestellungen", True)
Word: )

(few minutes later)
Commando BACK; i solved it by myself... For any others with an equal problem. I saved the LAyout in the Designer with "Save as..." using the same name (i overwrote the old layoutfile). Now there is no more Compileerror and i can successfully build the app.

STRANGE
 

GeeKay

Member
Licensed User
Longtime User
Scrollable widgets are supported by Android 3.0 or above. For now Basic4android doesn't support such widgets.
Hi Erel!

I like a lot the B4A new version 3.0. Do you plan support Scrollable widgets sometime in a near future?
Thx.
G.
 

sorex

Expert
Licensed User
Longtime User
Is there special manifest or other tweaking requered to get this working?

I followed the guide and all seemed to work right, until I wasted hours trying to figure out why my clicks didn't do much.

When I compare the log from your example I see

** Activity (main) Create, isFirst = true **
** Service (widgetservice) Create **
** Service (widgetservice) Start **
** Activity (main) Pause, UserClosed = true **
** Service (widgetservice) Destroy **


when I run mine it only gives this

** Activity (main) Create, isFirst = true **
** Activity (main) Resume **
** Activity (main) Pause, UserClosed = true **

so for some reasone the service doesn't start at all.

this code:

Sub Service_Create
Log("kjkjkjkj")
rv = ConfigureHomeWidget("layoutWidget", "rv", 60, "Phone Forwarder")
End Sub

seems to work since I can select the widget by that name,
altho the log line doesn't doesn't output to the debug window.

I must be missing something since it's a while since I did some actual B4A coding (shame on me, I know)
 

sorex

Expert
Licensed User
Longtime User
figured it out, it was again the device that was getting confused by another "B4A Example" app, trashing all those solved the problem.

As I stated "again" this caused issues before, can you force a rename of that package name and application label at first compile/save ? (why is that label not a menu item? couldn't find it at first glance)
 
Status
Not open for further replies.
Top