积累系统性知识
积聚技术精华
  首页    个人中心    撰写积文    建立课题    订立目标    整理积文    管理课题    管理目标    技能Get    代码积累 
Android-Launcher开发之AppWidget(2)
error997 (error997)    2015-05-10 19:11:14      目标    课题
   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>


   <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>


   <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());  
       }  
   }


   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);  
            }  
        }  
    }


   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);  
        }  
    }  
}


   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>


   <activity android:name="org.lean.ConfigureActivity" >
            <intent-filter>
                <action android:name="android.appwidget.action.APPWIDGET_CONFIGURE" />
            </intent-filter>
        </activity>

切换到: 纯代码  
   
android:configure="org.lean.ConfigureActivity"


   android:configure="org.lean.ConfigureActivity"

切换到: 纯代码  
   
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();  
                }  
                  
            }  
        });  
    }  
      
}


   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();
            }
            
         }
      });
   }
   
}

转自 http://blog.csdn.net/qq285016127/article/details/38849199
(+0)技能Get

建议楼主:搜索关键字 |参考其他资源 |回复 |追问
  error997(error997):   个人中心    课题    目标    代码积累