`
chroya
  • 浏览: 656744 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

让自己的Android应用支持appwidget

阅读更多

        经常看到一些教程教你如何写appwidget,但是,你知道你的appwidget是如何被添加到桌面上的吗?
   
    一般的,如果是做桌面的童鞋,基本上都会让自己的桌面支持appwidget。下面说说如何实现。
   
    首先是得定义一个承载appwidget的容器,系统的Launcher里面是用的CellLayout,实现的很不错。我这里就用一个简单的自定义ViewGroup来搞定,它是以长按的坐标处为要添加的appwidget的起始位置,简单点说就是按到哪儿就添加到哪儿。

package chroya.demo.widget;

import android.content.Context;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;

/**
 * 承载widget的容器
 * @author chroya
 */
public class WidgetLayout extends ViewGroup {
	//存放touch的坐标
	private int[] cellInfo = new int[2];
	private OnLongClickListener mLongClickListener;	

	public WidgetLayout(Context context) {
		super(context);
		mLongClickListener = new OnLongClickListener() {
			
			@Override
			public boolean onLongClick(View v) {
				
				return false;
			}
		};
	}
	
	public void addInScreen(View child, int width, int height) {
		LayoutParams lp = new LayoutParams(width, height);
		lp.x = cellInfo[0];
		lp.y = cellInfo[1];
		child.setOnLongClickListener(mLongClickListener);
		addView(child, lp);
	}
	
	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		super.onMeasure(widthMeasureSpec, heightMeasureSpec);
		LayoutParams lp;
		for(int index=0; index<getChildCount(); index++) {
			lp = (LayoutParams) getChildAt(index).getLayoutParams();
			getChildAt(index).measure(
					MeasureSpec.makeMeasureSpec(MeasureSpec.EXACTLY, lp.width), 
					MeasureSpec.makeMeasureSpec(MeasureSpec.EXACTLY, lp.height));
		}
	}
	
	@Override
	public boolean dispatchTouchEvent(MotionEvent event) {
		cellInfo[0] = (int)event.getX();
		cellInfo[1] = (int)event.getY();
		return super.dispatchTouchEvent(event);
	}

	@Override
	protected void onLayout(boolean changed, int l, int t, int r, int b) {
		LayoutParams lp;
		for(int index=0; index<getChildCount(); index++) {
			lp = (LayoutParams) getChildAt(index).getLayoutParams();
			getChildAt(index).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);
		}		
	}
}
 


        然后是重点了。还记得系统默认的桌面上,长按的时候出现的上下文菜单吗?里面有好几个选项,选择widget之后,会弹出一个已经安装的widget列表,选择一个widget之后,就会添加到桌面。我们可以把第一步去掉,长按之后,直接弹出已安装的widget列表,这是一个activity,用AppWidgetManager.ACTION_APPWIDGET_PICK这个Intent来启动,必须带上Extras,下面给出代码中有,不详叙。

package chroya.demo.widget;

import static android.util.Log.d;

import java.util.ArrayList;

import android.app.Activity;
import android.appwidget.AppWidgetHost;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProviderInfo;
import android.content.ComponentName;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnLongClickListener;

/**
 * 添加appwidget
 * @author chroya
 *
 */
public class Main extends Activity {
	private AppWidgetHost mAppWidgetHost;
	private AppWidgetManager mAppWidgetManager;
	private WidgetLayout layout;	
	
	private static final int REQUEST_PICK_APPWIDGET = 1;
	private static final int REQUEST_CREATE_APPWIDGET = 2;	
	private static final int APPWIDGET_HOST_ID = 0x100;
	private static final String EXTRA_CUSTOM_WIDGET = "custom_widget";
	
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        
        mAppWidgetManager = AppWidgetManager.getInstance(getApplicationContext());
        mAppWidgetHost = new AppWidgetHost(getApplicationContext(), APPWIDGET_HOST_ID);
        //开始监听widget的变化
        mAppWidgetHost.startListening();
        
        layout = new WidgetLayout(this);
        layout.setOnLongClickListener(new OnLongClickListener() {
			
			@Override
			public boolean onLongClick(View v) {
				
				addWidget();
				return false;
			}
		});
        setContentView(layout);
    }
    
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    	if (resultCode == RESULT_OK) {
    		switch (requestCode) {
    		case REQUEST_PICK_APPWIDGET:
                addAppWidget(data);
                break;
            case REQUEST_CREATE_APPWIDGET:
                completeAddAppWidget(data);
                break;
    		}
    	} else if (requestCode == REQUEST_PICK_APPWIDGET &&
                resultCode == RESULT_CANCELED && data != null) {
            // Clean up the appWidgetId if we canceled
            int appWidgetId = data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1);
            if (appWidgetId != -1) {
                mAppWidgetHost.deleteAppWidgetId(appWidgetId);
            }
        }
    }
    
    /**
     * 选中了某个widget之后,根据是否有配置来决定直接添加还是弹出配置activity
     * @param data
     */
    private void addAppWidget(Intent data) {
        int appWidgetId = data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1);

        String customWidget = data.getStringExtra(EXTRA_CUSTOM_WIDGET);
        d("addAppWidget", "data:"+ customWidget);
        if ("search_widget".equals(customWidget)) {
        	//这里直接将search_widget删掉了
            mAppWidgetHost.deleteAppWidgetId(appWidgetId);
        } else {
            AppWidgetProviderInfo appWidget = mAppWidgetManager.getAppWidgetInfo(appWidgetId);
            
            d("addAppWidget", "configure:"+ appWidget.configure);
            if (appWidget.configure != null) {
            	//有配置,弹出配置
                Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_CONFIGURE);
                intent.setComponent(appWidget.configure);
                intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);

                startActivityForResult(intent, REQUEST_CREATE_APPWIDGET);
            } else {
            	//没有配置,直接添加
                onActivityResult(REQUEST_CREATE_APPWIDGET, Activity.RESULT_OK, data);
            }
        }
    }
    
    /**
     * 请求添加一个新的widget
     */
    private void addWidget() {
    	int appWidgetId = mAppWidgetHost.allocateAppWidgetId();

        Intent pickIntent = new Intent(AppWidgetManager.ACTION_APPWIDGET_PICK);
        pickIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
        // add the search widget
        ArrayList<AppWidgetProviderInfo> customInfo =
                new ArrayList<AppWidgetProviderInfo>();
        AppWidgetProviderInfo info = new AppWidgetProviderInfo();
        info.provider = new ComponentName(getPackageName(), "XXX.YYY");
        info.label = "Search";
        info.icon = R.drawable.ic_search_widget;
        customInfo.add(info);
        pickIntent.putParcelableArrayListExtra(
                AppWidgetManager.EXTRA_CUSTOM_INFO, customInfo);
        ArrayList<Bundle> customExtras = new ArrayList<Bundle>();
        Bundle b = new Bundle();
        b.putString(EXTRA_CUSTOM_WIDGET, "search_widget");
        customExtras.add(b);
        pickIntent.putParcelableArrayListExtra(
                AppWidgetManager.EXTRA_CUSTOM_EXTRAS, customExtras);
        // start the pick activity
        startActivityForResult(pickIntent, REQUEST_PICK_APPWIDGET);
    }    
    
    /**
     * 添加widget
     * @param data
     */
    private void completeAddAppWidget(Intent data) {
        Bundle extras = data.getExtras();
        int appWidgetId = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID, -1);

        d("completeAddAppWidget", "dumping extras content="+extras.toString());
        d("completeAddAppWidget", "appWidgetId:"+ appWidgetId);
        AppWidgetProviderInfo appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(appWidgetId);
        
        View hostView = mAppWidgetHost.createView(this, appWidgetId, appWidgetInfo);
        
        layout.addInScreen(hostView, appWidgetInfo.minWidth, appWidgetInfo.minHeight);        
    }
}

 

        运行效果如下:


   
    需要注意的几点:
1。 必须调用AppWidgetHost的startListening方法来监听appwidget的状态变化,否则添加上去的appwidget不会更新的。
2。 需要override一个onActivityResult方法,来接收添加appwidget和appwidget的配置activity的返回值。
3。 启动AppWidgetManager.ACTION_APPWIDGET_PICK这个Intent,必须要给列表中加上自己定义的一个选项,否则出错。如本例中是用的Search。

 

        源码见附件。

 

分享到:
评论
6 楼 chroya 2011-02-10  
liyh.vip 写道
LZ你有没有出现过
StackTrace=bitmap size exceeds VM budgetjava.lang.OutOfMemoryError: bitmap size exceeds VM budget
at android.graphics.Bitmap.nativeCreateFromParcel(Native Method)
at android.graphics.Bitmap.access$000(Bitmap.java:29)
at android.graphics.Bitmap$1.createFromParcel(Bitmap.java:927)
at android.graphics.Bitmap$1.createFromParcel(Bitmap.java:933)
at android.widget.RemoteViews$ReflectionAction.<init>(RemoteViews.java:344)
at android.widget.RemoteViews.<init>(RemoteViews.java:559)
at android.widget.RemoteViews$1.createFromParcel(RemoteViews.java:1012)
at android.widget.RemoteViews$1.createFromParcel(RemoteViews.java:1015)
at com.android.internal.appwidget.IAppWidgetHost$Stub.onTransact(IAppWidgetHost.java:53)
at android.os.Binder.execTransact(Binder.java:304)
at dalvik.system.NativeStart.run(Native Method)

这样的情况一般出现在大分辨率的机子上,图片占用的内存过大而导致的。暂时没有很完美的解决办法,但是还是能用一些方法来避免,网上可以搜到的。
5 楼 liyh.vip 2011-02-09  
LZ你有没有出现过
StackTrace=bitmap size exceeds VM budgetjava.lang.OutOfMemoryError: bitmap size exceeds VM budget
at android.graphics.Bitmap.nativeCreateFromParcel(Native Method)
at android.graphics.Bitmap.access$000(Bitmap.java:29)
at android.graphics.Bitmap$1.createFromParcel(Bitmap.java:927)
at android.graphics.Bitmap$1.createFromParcel(Bitmap.java:933)
at android.widget.RemoteViews$ReflectionAction.<init>(RemoteViews.java:344)
at android.widget.RemoteViews.<init>(RemoteViews.java:559)
at android.widget.RemoteViews$1.createFromParcel(RemoteViews.java:1012)
at android.widget.RemoteViews$1.createFromParcel(RemoteViews.java:1015)
at com.android.internal.appwidget.IAppWidgetHost$Stub.onTransact(IAppWidgetHost.java:53)
at android.os.Binder.execTransact(Binder.java:304)
at dalvik.system.NativeStart.run(Native Method)
4 楼 sendy618 2010-12-20  
好东西,呵呵,暂时用不到,收藏Le,谢谢
3 楼 chroya 2010-12-02  
aa87963014 写道
LZ 我看过你写的一个事件机制

但是有一点我遇到了一个问题。

就是子View 和父View 事件重复问题。

具体的就是 ScrollView 里面有一个TextView TextView 有一个OnTouchListener
我做的功能是 让这个TextView 可以拖动,但是拖动的时候触发了 ScrollView 的事件 导致 TextView 移动不成功(动几下就停了)

请问 这种怎么解决? 怎么在子View 里面屏蔽/拦截掉父View的事件?


TextView 的OnTouchListener里面onTouch返回值设为true。ScrollView也setOnTouchListener,返回false。
2 楼 aa87963014 2010-12-02  
LZ 我看过你写的一个事件机制

但是有一点我遇到了一个问题。

就是子View 和父View 事件重复问题。

具体的就是 ScrollView 里面有一个TextView TextView 有一个OnTouchListener
我做的功能是 让这个TextView 可以拖动,但是拖动的时候触发了 ScrollView 的事件 导致 TextView 移动不成功(动几下就停了)

请问 这种怎么解决? 怎么在子View 里面屏蔽/拦截掉父View的事件?
1 楼 justamusicer 2010-12-01  
我就喜欢带例子的

相关推荐

    新版Android开发教程.rar

    这一联盟将会支持 Google 发布的 Android 手机操作系统或者应用软件,共同开发名为 Android 的 开 放源代码的移动系统。开放手机联盟包括手机制造商、手机芯片厂商和移动运营商几类。目前,联盟成员 数 量已经达到了...

    (完整)Android手机天气预报项目报告.doc

    简言之,需要以下软件,才能搭建Android开发环境,从而进行Android应用程序的开 发。 (1) Java SDK (2) Eclipse (3) Android SDK (4) ADT 2。2.2 技术支持 本软件使用基于SOAP(简单对象访问协议)的Web ...

    2010年谢彦的android笔记

    4.1.3 android应用的启动过程 127 4.2 服务的原理与使用 128 4.3 键盘事件处理 130 4.4 包管理 131 4.5 传感器 132 4.6 浅析dalvik虚拟机JIT技术的实现 133 4.7 应用程序的签名(Signature) 135 4.8 应用的权限 138 ...

    机智云公版开源App-智能插座Android版

    使用机智云开源APP之前,需要先在机智云开发平台创建您自己的产品和应用。 开源App需要使用您申请的AppId、AppSecret以及您自己的产品ProductKey才能正常运行。 具体申请流程请参见:http://docs.gizwits.com/hc/...

    Android手机天气预报项目报告.docx

    解析Android应用程序中获得的列表数据XML文件,对XML文件中的数据实现分离,得到指定的所需要的信息,然后执行存入数据库或显示到页面等相应的处理。 Android的开发组Activity、Intent Receiver、Service、Content ...

    Android基础知识详解

    Android应用开发和Dalvik虚拟机 15 Activity生命周期 16 一、Activity栈 16 二、Activity的4种状态 16 三、Activity的生命周期 17 四、实例说明 18 Android控件的继承关系 22 一、View与ViewGroup关系 22 二、各控件...

    《Google Android SDK开发范例大全(第3版)》.pdf

    2.3 android应用程序架构——从此开始 15 2.4 可视化的界面开发工具 18 2.5 部署应用程序到android手机 19 第3章 用户人机界面 22 3.1 更改与显示文字标签 23 3.2 更改手机窗口画面底色 24 3.3 ...

    Android手机天气预报项目报告(1).docx

    解析Android应用程序中获得的列表数据XML文件,对XML文件中的数据实现分离,得到指定的所需要的信息,然后执行存入数据库或显示到页面等相应的处理。 Android的开发组Activity、Intent Receiver、Service、Content ...

    Android手机天气预报项目报告(2).docx

    简言之,需要以下软件,才能搭建Android开发环境,从而进行Android应用程序的开发。 (1) Java SDK (2) Eclipse (3) Android SDK (4) ADT 2。2。2 技术支持 本软件使用基于SOAP(简单对象访问协议)的Web Service...

    Google Android SDK开发范例大全(第3版) 1/5

    4.30 实时配置桌面上的AppWidget UI Layout 4.31 识别输入装置ID与InputDevice装置 4.32 选取文字的聪明文字联想 4.33 设计与Launcher相同的三页滑动Layout 第5章 交互式通信服务与手机控制 5.1 具有正则表达式的...

    android笔记.rar

    4.1.3 android应用的启动过程... ..127 4.2 服务的原理与使用... ..128 4.3 键盘事件处理... .130 4.4 包管理... .131 4.5 传感器... .132 4.6 浅析dalvik虚拟机JIT技术的实现... ...133 4.7 应用程序的签名...

    Google Android SDK开发范例大全(第3版) 4/5

    4.30 实时配置桌面上的AppWidget UI Layout 4.31 识别输入装置ID与InputDevice装置 4.32 选取文字的聪明文字联想 4.33 设计与Launcher相同的三页滑动Layout 第5章 交互式通信服务与手机控制 5.1 具有正则表达式的...

    Google Android SDK开发范例大全(第3版) 3/5

    4.30 实时配置桌面上的AppWidget UI Layout 4.31 识别输入装置ID与InputDevice装置 4.32 选取文字的聪明文字联想 4.33 设计与Launcher相同的三页滑动Layout 第5章 交互式通信服务与手机控制 5.1 具有正则表达式的...

    Google Android SDK开发范例大全(第3版) 5/5

    4.30 实时配置桌面上的AppWidget UI Layout 4.31 识别输入装置ID与InputDevice装置 4.32 选取文字的聪明文字联想 4.33 设计与Launcher相同的三页滑动Layout 第5章 交互式通信服务与手机控制 5.1 具有正则表达式的...

    动态支持:使用内置主题引擎构建Android应用程序的完整库

    implementation ' com.pranavpandey.android:dynamic-support:4.0.0 ' // For legacy projects. implementation ' com.pranavpandey.android:dynamic-support:1.3.0 '}用法它是构建标准Android应用程序所需的...

    MapView的使用

    import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.view.View; import android.widget.Button; import android.widget.EditText; public class SendCityName...

    Kommunicate-Android-Chat-SDK:提供客户支持的Kommunicate.io Android Chat SDK

    提供客户支持的 Android Chat SDK ... Kommunicate SDK可让您将自定义聊天机器人集成到移动应用程序中以自动执行任务。 它具有多种功能,使其成为功能完善的客户支持SDK。 开始使用 要开始使用Kommunicate Andro

    google android sdk开发范例大全 第二版 PDF 光盘代码

     2.3 Android应用程序架构——从此开始   2.4 可视化的界面开发工具   2.5 部署应用程序到Android手机  第3章 用户人机界面   3.1 更改与显示文字标签   3.2 更改手机窗口画面底色   3.3 ...

Global site tag (gtag.js) - Google Analytics