Android-Launcher开发之AppWidget(2)
AppWidget 通过内存共享进行数据通讯.原理图如下:
1.创建一个BroadcastReceiver,继承AppWidgetProvider.
2.在AndroidManifest.xml中配置如下:下面给出官网例子<action />是必须的.
<receiver android:name="ExampleAppWidgetProvider" > <intent-filter> <action android:name="android.appwidget.action.APPWIDGET_UPDATE" /> </intent-filter> <meta-data android:name="android.appwidget.provider" android:resource="@xml/example_appwidget_info" /> </receiver>
3.提供一个<meta-data />标签里resource的xml文件,定义appwidget的样式属性(配置参数可选)
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android" android:minWidth="294dp" android:minHeight="72dp" android:updatePeriodMillis="86400000" android:previewImage="@drawable/preview" android:initialLayout="@layout/example_appwidget" android:configure="com.example.android.ExampleAppWidgetConfigure" android:resizeMode="horizontal|vertical" android:widgetCategory="home_screen|keyguard" android:initialKeyguardLayout="@layout/example_keyguard"> </appwidget-provider>4.定义appwidget的样式.定义一个layout文件做为布局文件 ,并配置在上面intialLayout属性中.
5.当用户点击选取添加某个小部件.首先获取appWidgetId,并打开一个选取小部件的Activity
6.选取后,通过appWidgetId获取appWidgetInfo,并转化为LauncherAppWidgetInfo对象,并添加到workspace中.
当然,这段时间包括了appwidget的定位.其添加源码如下:
private void completeAddAppWidget(Intent data, CellLayout.CellInfo cellInfo) { Bundle extras = data.getExtras(); int appWidgetId = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID, -1); if (LOGD) Log.d(TAG, "dumping extras content=" + extras.toString()); AppWidgetProviderInfo appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(appWidgetId); // Calculate the grid spans needed to fit this widget CellLayout layout = (CellLayout) mWorkspace.getChildAt(cellInfo.screen); int[] spans = layout.rectToCell(appWidgetInfo.minWidth, appWidgetInfo.minHeight); // Try finding open space on Launcher screen final int[] xy = mCellCoordinates; if (!findSlot(cellInfo, xy, spans[0], spans[1])) { if (appWidgetId != -1) mAppWidgetHost.deleteAppWidgetId(appWidgetId); return; } // Build Launcher-specific widget info and save to database LauncherAppWidgetInfo launcherInfo = new LauncherAppWidgetInfo(appWidgetId); launcherInfo.spanX = spans[0]; launcherInfo.spanY = spans[1]; LauncherModel.addItemToDatabase(this, launcherInfo, LauncherSettings.Favorites.CONTAINER_DESKTOP, mWorkspace.getCurrentScreen(), xy[0], xy[1], false); if (!mRestoring) { mDesktopItems.add(launcherInfo); // Perform actual inflation because we're live launcherInfo.hostView = mAppWidgetHost.createView(this, appWidgetId, appWidgetInfo); launcherInfo.hostView.setAppWidget(appWidgetId, appWidgetInfo); launcherInfo.hostView.setTag(launcherInfo); mWorkspace.addInCurrentScreen(launcherInfo.hostView, xy[0], xy[1], launcherInfo.spanX, launcherInfo.spanY, isWorkspaceLocked()); } }总结:当我们执行完前3步,系统即会根据用户选择创建小部件.
--更新AppWidget界面,其原理如下:
1AppWidgetManager发送广播传递ID.
void updateProvidersForPackageLocked(String pkgName) { HashSet<String> keep = new HashSet<String>(); Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE); intent.setPackage(pkgName); List<ResolveInfo> broadcastReceivers = mPackageManager.queryBroadcastReceivers(intent, PackageManager.GET_META_DATA); // add the missing ones and collect which ones to keep int N = broadcastReceivers == null ? 0 : broadcastReceivers.size(); for (int i=0; i<N; i++) { ResolveInfo ri = broadcastReceivers.get(i); ActivityInfo ai = ri.activityInfo; if ((ai.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) { continue; } if (pkgName.equals(ai.packageName)) { ComponentName component = new ComponentName(ai.packageName, ai.name); Provider p = lookupProviderLocked(component); if (p == null) { if (addProviderLocked(ri)) { keep.add(ai.name); } } else { Provider parsed = parseProviderInfoXml(component, ri); if (parsed != null) { keep.add(ai.name); // Use the new AppWidgetProviderInfo. p.info = parsed.info; // If it's enabled final int M = p.instances.size(); if (M > 0) { int[] appWidgetIds = getAppWidgetIds(p); // Reschedule for the new updatePeriodMillis (don't worry about handling // it specially if updatePeriodMillis didn't change because we just sent // an update, and the next one will be updatePeriodMillis from now). cancelBroadcasts(p); registerForBroadcastsLocked(p, appWidgetIds); // If it's currently showing, call back with the new AppWidgetProviderInfo. for (int j=0; j<M; j++) { AppWidgetId id = p.instances.get(j); id.views = null; if (id.host != null && id.host.callbacks != null) { try { id.host.callbacks.providerChanged(id.appWidgetId, p.info); } catch (RemoteException ex) { // It failed; remove the callback. No need to prune because // we know that this host is still referenced by this // instance. id.host.callbacks = null; } } } // Now that we've told the host, push out an update. sendUpdateIntentLocked(p, appWidgetIds); } } } } } // prune the ones we don't want to keep N = mInstalledProviders.size(); for (int i=N-1; i>=0; i--) { Provider p = mInstalledProviders.get(i); if (pkgName.equals(p.info.provider.getPackageName()) && !keep.contains(p.info.provider.getClassName())) { removeProviderLocked(i, p); } } }
2.用户自定义AppwidgetProvider的onUpdate()接收数据.并通过Manager更新界面.(如下,官网给出了2个操作,1更新界面 2.点击小部件操作)
public class ExampleAppWidgetProvider extends AppWidgetProvider { public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { final int N = appWidgetIds.length; // Perform this loop procedure for each App Widget that belongs to this provider for (int i=0; i<N; i++) { int appWidgetId = appWidgetIds[i]; // Create an Intent to launch ExampleActivity Intent intent = new Intent(context, ExampleActivity.class); PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0); // Get the layout for the App Widget and attach an on-click listener // to the button RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.appwidget_provider_layout); views.setOnClickPendingIntent(R.id.button, pendingIntent); // Tell the AppWidgetManager to perform an update on the current app widget appWidgetManager.updateAppWidget(appWidgetId, views); } } }
--Updating the App Widget from the configuration Activity(通过配置选项更改APPwidget界面,当Appwidget创建时,先进入该Activity进行判断)
1.创建Activity并在AndroidManifest.xml里面配置:
<activity android:name="org.lean.ConfigureActivity" > <intent-filter> <action android:name="android.appwidget.action.APPWIDGET_CONFIGURE" /> </intent-filter> </activity>2.在appwidget的配置xml里面增加属性:
android:configure="org.lean.ConfigureActivity"
3.在Activity中操作,1>根据配置修改远程界面,2>跳转回选择的activity并由他关闭选择Activity并显示Launcher界面
package org.lean; import android.app.Activity; import android.appwidget.AppWidgetManager; import android.content.Intent; import android.os.Bundle; import android.view.View; import android.view.View.OnClickListener; import android.widget.EditText; import android.widget.RemoteViews; /** * * * @author Lean @date:2014-8-27 */ public class ConfigureActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.configure_view); findViewById(R.id.button1).setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { int appwidgetId=getIntent().getExtras().getInt(AppWidgetManager.EXTRA_APPWIDGET_ID,AppWidgetManager.INVALID_APPWIDGET_ID); if (appwidgetId!=AppWidgetManager.INVALID_APPWIDGET_ID) { //修改远程界面 EditText editText=(EditText) findViewById(R.id.editText1); String currenEtStr=editText.getText().toString(); RemoteViews views=new RemoteViews(ConfigureActivity.this.getPackageName(), R.layout.layout_widget); views.setTextViewText(R.id.btn,currenEtStr); AppWidgetManager manager=AppWidgetManager.getInstance(ConfigureActivity.this); manager.updateAppWidget(appwidgetId, views); //返回activity Intent reIntent=new Intent(); reIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,appwidgetId); setResult(RESULT_OK,reIntent); finish(); } } }); } }
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。