Blog migration-I have migrated my blog to www.ijavaboy.com to better manage it. We are sorry for the inconvenience caused by no updates! New address of this article: Click me
Here, our launcher can run, and the effect is similar to that of the system launcher. However, unfortunately, our desktop seems to be a simple cut, let's look at the system desktop, search box, weather controls, various sizes, and various controls on the interface. In addition to partition cut, widgets of different sizes should also be available on the desktop.
To make our desktop support widgets, we need to study the widget. A widget is a special independent body that can be embedded in another application. As long as the application is implemented as a widgethost, it can accommodate various widgets. Android provides a set of widget development interfaces, including creating widgets and creating widgethost. To avoid directly adding complex code to the implemented functions, we first use two small demos to see how to develop a widget and how to implement a widgethost, through these two demos, we can add code that supports widgets to our launcher, which is easier to understand.
I hereby declare that the following two demos are obtained from my personal code library. Due to a long time, I do not know whether they are collected online or written by myself. If you find that your credit is yours, I am sorry, but your demo is of great reference value. I will borrow it and say: Thank you!
1. Develop a widget-desktop Widgets
Develop a widget and make the following preparations:
1. appwidget-provider: Create a. xml file in the res \ XML \ directory, which contains the following content:
<?xml version="1.0" encoding="utf-8"?><appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android" android:minWidth="294dip" android:minHeight="150dip" android:updatePeriodMillis="0" android:initialLayout="@layout/widget_demo"> </appwidget-provider>
This file mainly defines the size occupied by the widget on the desktop and the layout file used by the specified widget. Android: updateperiodmillis indicates the interval at which the widget is updated. This value may take at least 30 minutes to take effect in Versions later than 1.5, but I have not tried it. Only know, set it to a few seconds, a few minutes is certainly invalid.
2. inherit from appwidgetprovider and implement the logic you need: do not be fooled by its name. It is a broadcastreceiver that implements the onreceive method and provides several methods of its own:
* Onupdate (context, appwidgetmanager, appwidgetids)
* Ondeleted (context, int [] appwidgetids)
* Onenabled (context)
* Ondisabled (context)
For more information about each method, see the document. We often need to implement the onupdate method when developing. This method is to update the widgets added to the desktop.
3. Configure in manifest:
<receiver android:name="DemoAppWidgetProvider"> <meta-data android:name="android.appwidget.provider" android:resource="@xml/widget_demo" /> <intent-filter> <action android:name="android.appwidget.action.APPWIDGET_UPDATE" /> </intent-filter> </receiver>
A meta-data file is specified to specify the widget description file. In addition, the android. appwidget. Action. appwidget_update action is used in intent-filter.
4. widget layout file:
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <LinearLayout android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="100dip" android:background="#F3F3F3"> <TextView android:id="@+id/demo" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="20dip" android:layout_marginTop="20dip" android:text="this is a demo" android:textSize="20sp" android:textStyle="bold"/> </LinearLayout> <LinearLayout android:orientation="horizontal" android:layout_width="fill_parent" android:layout_height="50dip" android:background="@drawable/widget_bottom"> <Button android:id="@+id/pre" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@drawable/pre_bg" android:layout_marginLeft="20dip" android:layout_marginTop="2dip"/> <Button android:id="@+id/next" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@drawable/next_bg" android:layout_marginLeft="20dip" android:layout_marginTop="2dip"/> </LinearLayout> </LinearLayout>
Note: This is a normal layout file. However, the specification system documentation for the layout file provides guidance. You need to follow the self-export documentation to see the issues that need to be paid attention to when defining widgets on the horizontal and vertical screens. It is mainly about the size.
In addition to the above, you may need a service. If the operation to be completed is time-consuming, you need to create a service to complete the main functions. A service is created in this demo.
The following code is directly used:
Public class demoappwidgetprovider extends appwidgetprovider {public static final componentname appwidget_component = new componentname ("demo. widget "," demo. widget. demoappwidgetprovider "); Public void onupdate (context, appwidgetmanager, int [] appwidgetids) {/*** run */FINAL remoteviews views = new remoteviews (context. getpackagename (), R. layout. widget_demo ); Linkbuttons (context, views); Final appwidgetmanager GM = appwidgetmanager. getinstance (context); If (appwidgetids! = NULL) {GM. updateappwidget (appwidgetids, views);} else {GM. updateappwidget (appwidget_component, views);} // start demoservice context. startservice (new intent (actiondefinition. action_app_widget_service);}/*** bind an event to the button * @ Param context * @ Param views */private void linkbuttons (context, remoteviews views) {final componentname servicename = new componentname (context, demoservice. class); intent = NULL; pendingintent pintent = NULL; // Bind The onclick event intent = new intent (actiondefinition. action_app_widget_prev); intent. setcomponent (servicename); pintent = pendingintent. getservice (context, 0, intent, 0); views. setonclickpendingintent (R. id. PRE, pintent); // Bind The onclick event intent = new intent (actiondefinition. action_app_widget_next); intent. setcomponent (servicename); pintent = pendingintent. getservice (context, 0, intent, 0); views. setonclickpendingintent (R. id. next, pintent );}
The above inheritance appwidgetprovider implements an appwidgetprovider and binds two buttons to the onupdate Method for event behavior. All the actions seem to be completed by remoteview, but remoteview is not the view you see on the desktop. It only encapsulates some operations and descriptions, transmits information between the widget and widgethost.
In the above program, a service is used, as follows:
/*** The service updates the app widget; simultaneously process The onclick event * @ author liner **/public class demoservice extends Service {private string [] contentdemos = new string [] {"demo1 ", "demo2", "demo3", "demo4"}; private int currentdisplayitem = 0; Public void oncreate () {super. oncreate (); log. V ("demoservice", "oncreate execute");} public void onstart (intent, int startid) {super. onstart (intent, startid); log. V ("demoservice", "onstart execute"); // obtain the action information to determine the action string action = intent. getaction (); If (action. equals (actiondefinition. action_app_widget_prev) {doprev ();} else if (action. equals (actiondefinition. action_app_widget_next) {donext ();} else {// If (action. equals (actiondefinition. action_app_widget_service) yywidget () ;}// notification update private void yywidget () {componentname widget = new componentname (this, demoappwidgetprovider. class); appwidgetmanager manager = appwidgetmanager. getinstance (this); remoteviews views = buildupdateviews (); manager. updateappwidget (widget, views);} // during each update, a remoteview is created to complete the update. Private remoteviews buildupdateviews () {remoteviews views = new remoteviews (this. getpackagename (), R. layout. widget_demo); views. settextviewtext (R. id. demo, contentdemos [currentdisplayitem]); Return views;} private void donext () {If (currentdisplayitem> = contentdemos. length-1) {currentdisplayitem = 0;} else {currentdisplayitem ++;} notifywidget ();} private void doprev () {If (currentdisplayitem> 0) {currentdisplayitem --;} else {currentdisplayitem = contentdemos. length-1 ;}yywidget () ;}@ overridepublic ibinder onbind (intent) {return NULL ;}}
When the widget is added to the desktop, after the onupdate method is executed, we click the pre and next buttons on the widget to actually trigger the service and complete the update operation through the service.
Here, we know how to develop a widget. Next, let's take a look at how to develop an application that can accommodate widgets. The effect is as follows:
2. Develop a widgethost so that my applications can also be deployed in Haina baichuan.
To develop a widgethost, we first need a layout control that can accommodate various Widgets. The celllayout is used in the system launcher, so we also inherit the viewgroup to implement a simple layout.
Public class widgetlayout extends viewgroup {private int [] cellinfo = new int [2]; private onlongclicklistener mlongclicklistener; Public widgetlayout (context) {This (context, null );} public widgetlayout (context, attributeset attrs) {This (context, attrs, 0);} public widgetlayout (context, attributeset attrs, int defstyle) {super (context, attrs, defstyle);} // @ 1: This action is triggered when a long press is triggered. The position of the long press is recorded as public Boolean dispatchtouchevent (motionevent event) {cellinfo [0] = (INT) event. getx (); cellinfo [1] = (INT) event. gety (); log. E ("Event:", cellinfo [0] + "," + cellinfo [1]); return Super. dispatchtouchevent (event);} // @ 2: When you select a widget, this action is triggered and the selected widget (child) is added to the public void addinscreen (view child, int width, int height) {layoutparams Params = new layoutparams (width, height); Params. X = cellinfo [0]; Params. y = cellinfo [1]; // Params. width = widthchild. setonlongclicklistener (mlongclicklistener); log. E ("size", "x, y, width, height" + Params. X + "," + Params. Y + "," + Params. width + "," + Params. height); addview (child, Params);} // @ 3: measure the width and height of each child. Public void onmeasure (INT widthmeasurespec, int heightmeasurespec) {// super. onmeasure (widthmeasurespec, heightmeasurespec); Final int COUNT = getchildcount (); layoutparams Lp = NULL; For (INT I = 0; I <count; I ++) {view child = getchildat (I); Lp = (layoutparams) child. getlayoutparams (); log. E ("onmeasure: W, H", LP. width + "," + LP. height); child. measure (measurespec. makemeasurespec (LP. width, measurespec. exactly), measurespec. makemeasurespec (LP. height, measurespec. exactly);} setmeasureddimension (measurespec. getsize (widthmeasurespec), measurespec. getsize (heightmeasurespec);} // @ 4: Layout each child according to the layoutparams defined in @ overrideprotected void onlayout (Boolean changed, int L, int T, int R, int B) {final int COUNT = getchildcount (); layoutparams Lp = NULL; For (INT I = 0; I <count; I ++) {view child = getchildat (I); Lp = (layoutparams) child. getlayoutparams (); child. layout (LP. x, LP. y, LP. X + LP. width, LP. Y + LP. height) ;}} public static class layoutparams extends viewgroup. layoutparams {int X; int y; Public layoutparams (INT width, int height) {super (width, height); this. width = width; this. height = height ;}}}
How can we implement it with a place that can be accommodated? The system launcher is, a dicher is displayed when you press the desktop, and there is an item in it: Add widget. From this point on, we call the built-in widget selection program to choose from. The complete code is as follows:
/*** How to select installed Widgets, add the selected widget to the specified position * @ author liner **/public class appwidgethostdemoactivity extends activity {Private Static final int appwidget_host_id = 0x200; private Static final int request_add_widget = 1; Private Static final int request_create_widget = 2; private appwidgethost mwidgethost; private appwidgetmanager mwidgetmananger; private widgetlayout mlayout; @ override public void o Ncreate (bundle savedinstancestate) {super. oncreate (savedinstancestate); mwidgethost = new appwidgethost (getapplicationcontext (), appwidget_host_id); mwidgetmananger = appwidgetmanager. getinstance (getapplicationcontext (); mlayout = new widgetlayout (this); mlayout. setonlongclicklistener (new view. onlongclicklistener () {@ overridepublic Boolean onlongclick (view v) {selectwidgets (); Return false ;}}); s Etcontentview (mlayout); // starts to listen to the changes of the widget mwidgethost. startlistening ();} public void onactivityresult (INT requestcode, int resultcode, intent data) {If (resultcode = result_ OK) {Switch (requestcode) {Case request_add_widget: addwidget (data ); break; Case request_create_widget: createwidget (data); break; default: break;} else if (requestcode = request_create_widget & resultcode = result_canceled & Data! = NULL) {int appwidgetid = data. getintextra (appwidgetmanager. extra_appwidget_id,-1); If (appwidgetid! =-1) {mwidgethost. deleteappwidgetid (appwidgetid) ;}} private void createwidget (intent data) {// get the ID of the selected widget int appwidgetid = data. getintextra (appwidgetmanager. extra_appwidget_id,-1); // obtain the appwidgetproviderinfo information of the selected widget. appwidgetproviderinfo appwidget = mwidgetmananger. getappwidgetinfo (appwidgetid); // create hostview = mwidgethost Based on appwidgetproviderinfo. createview (this, appwid GETID, appwidget); // Add the hostview to the desktop mlayout. addinscreen (hostview, appwidget. minwidth, appwidget. minheight);}/*** Add the selected widget. You need to determine whether the configuration exists. If yes, first enter * @ Param data */private void addwidget (intent data) {int appwidgetid = data. getintextra (appwidgetmanager. extra_appwidget_id,-1); appwidgetproviderinfo appwidget = mwidgetmananger. getappwidgetinfo (appwidgetid); log. D ("appwidget", "Configure:" + appwidget. configure); If (appwidget. configure! = NULL) {// configured. Intent intent = new intent (appwidgetmanager) is displayed. action_appwidget_configure); intent. setcomponent (appwidget. configure); intent. putextra (appwidgetmanager. extra_appwidget_id, appwidgetid); startactivityforresult (intent, request_create_widget);} else {// No configuration, add onactivityresult (request_create_widget, result_ OK, data) directly );}} /*** display the existing widget information in the system for the user to choose */protected void selectwidgets () {int widgetid = mwidgethost. allocateappwidgetid (); intent pickintent = new intent (appwidgetmanager. action_appwidget_pick); pickintent. putextra (appwidgetmanager. extra_appwidget_id, widgetid); startactivityforresult (pickintent, request_add_widget );}}
Effect:
At this point, we should be able to understand that to allow our launcher to accommodate all kinds of widgets, we need to add at least similar code above. However, what problems need to be solved urgently?
1. How does celllayout calculate the number of cells required for the currently added widget?
2. How to allocate the number of cells occupied by the currently added widget to celllayout
3. There is no enough area in celllayout to accommodate widgets of this size. What should I do?
With the previous Code, the following tasks can be clustered on the above three issues, solve these three problems, and then add the previous code, so that our desktop can accommodate all kinds of widgets like the system desktop.
The next article will solve these problems...