widget开发之helloworld(基础篇)

widget开发之helloworld

由于工作需求,上周刚开始接触widget。经过一周的开发和了解,对widget的一些基础点做一下整理。

什么是widget:

widget也叫小部件,在launcher中点击menu键,会出现小部件选项,进入后会显示所以手机上已经安装的widget。长按或者单击(launcher决定)后,可添加到手机桌面上。

从功能上,widget功能比较简单,专注于某一件事情。比如时钟、天气、音乐等等。

widget也可以和响应的普通应用联系起来,点击之后进入应用界面。这时候widget更像是一个应用的快捷入口。

下面来具体介绍一下widget的开发步骤:

1、注册widget处理器

在manifest中,注册一个receiver,用来接收系统发来的widget的广播。

<!-- receiver的 android:name指向的是widget的请求处理器或者说请求接收者 -->
		<receiver
			android:label="@string/app_name"
			android:name="com.cooee.phenix.widget.WidgetProvider">
			<intent-filter>
			    <!-- 点击事件对应的广播字符串 -->
                <action android:name="com.cooee.phenix.widget.click.clock_layout"/>
                <action android:name="com.cooee.phenix.widget.click.weather_layout"/>
                <action android:name="com.cooee.phenix.widget.click.date_layout"/>
				<!-- widget默认的事件action -->
				<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
			</intent-filter>
			<!-- widget元数据,name是写死的,resource指的是widget的配置文件 -->
			<meta-data
				android:name="android.appwidget.provider"
				android:resource="@layout/widgetinfo" />
		</receiver>

2、WidgetProvider处理器

WidgetProvider是我们自己创建的类,继承AppWidgetProvider,AppWidgetProvider extends BroadcastReceiver,用来处理widget的各种请求。

widget中的事件都是通过广播进行传递的:

widget的添加、删除、更新等操作,都会收到相应的广播。在AppWidgetProvider的public void onReceive(方法中可以看到

AppWidgetProvider类中:

public void onUpdate:widget更新时回调

public void onDeleted:一个widget从桌面删除时回调

public void onEnabled:第一个widget被放到桌面时回调

public void onDisabled:最后一个widget从桌面删除时回调

public void onReceive:用来接收广播,处理widget的消息。

如果我们需要在这些时刻做一下自己的逻辑处理,WidgetProvider可以重写这几个方法。

3、widgetinfo和widget布局

widget的界面和widget宽高等信息在哪里定义呢?

在上面代码的注释中可以看到,widgetinfo是关联在receiver里的mete-data中。(把这部分代码单独贴出来方便大家看)

<!-- widget元数据,name是写死的,resource指的是widget的配置文件 -->
			<meta-data
				android:name="android.appwidget.provider"
				android:resource="@layout/widgetinfo" />

那么,widgetinfo中究竟定义了什么东西,我们来具体看一下这个xml文件:

<?xml version="1.0" encoding="utf-8"?>
<!-- appwidget-provider Widget的配置文件 -->
<!-- android:minWidth 最小宽度 -->
<!-- android:minHeight 最小高度 -->
<!-- android:updatePeriodMillis 组件更新频率(毫秒) -->
<!-- android:initialLayout 用来关联widget的布局文件 -->
<!-- android:configure Widget设置用Activity ??? -->
<appwidget-provider
	android:layout_width="wrap_content"
	android:layout_height="wrap_content"
	xmlns:android="http://schemas.android.com/apk/res/android"
	android:initialLayout="@layout/widget_layout"
	android:minWidth="@dimen/widget_width"
	android:minHeight="@dimen/widget_height"
	android:updatePeriodMillis="86400000" />

关于android:configure属性,这次并没有用到,具体我也不懂这是干嘛用的。
在这里,我最关心的是布局文件,毕竟,它是要显示的东西。

由于布局文件比较长这里就不贴代码了。(布局文件就像我们app里的一样布局就ok啦)

4、widget点击事件(action的意义)

说道点击事件,我们得先来说一下widget中的view。

remoteview:

上面讲到widget关联布局的方式,和我们写app有点不同。那么 我们想对显示的布局做一些动态的改变(改变文字或者背景之类),该怎么办呢?

按照写app的思考方式,我们需要获取到一个view,它里面填充的整个布局,然后最好有个findviewbyid获取到布局里每一个控件,然后,嘎嘎嘎,我们就能为所欲为了。

设置点击监听、改变文字、设置图片背景神马的还不是洒洒水???!!!

想法,是很美好的。但是百度了一番之后我才发现并没有想象中辣么简单。。。。。。

下面来介绍widget如何获取我们想要的view:

rv = new RemoteViews( mContext.getPackageName() , R.layout.widget_layout );

得到view之后,悲剧的事情发生了,它居然没有findviewbyid方法!!!!!也就是说我得不到布局里每一个具体的view

一番网上搜索之后,设置点击事件监听和改变文字之类的找到了对应的方法,不过,动画好像就与我们绝缘了。。。

点击事件:

//日期
Intent intentDateClick = new Intent( WidgetProvider.CLICK_DATE_LAYOUT );//intent的action用来区分具体点击的是哪一个view
PendingIntent pendingDateIntent = PendingIntent.getBroadcast( mContext , 0 , intentDateClick , 0 );
rv.setOnClickPendingIntent( R.id.date_textview , pendingDateIntent );//设置点击事件监听
//时钟
if( showClockVeiw )
{
	Intent intentClockClick = new Intent( WidgetProvider.CLICK_CLOCK_LAYOUT );
	PendingIntent pendingClockIntent = PendingIntent.getBroadcast( mContext , 0 , intentClockClick , 0 );
	rv.setOnClickPendingIntent( R.id.clock_layout , pendingClockIntent );
}
visibility = showClockVeiw ? View.VISIBLE : View.GONE;
rv.setViewVisibility( R.id.clock_layout , visibility );
//天气
if( showWeatherVeiw )
{
	Intent intentWeatherClick = new Intent( WidgetProvider.CLICK_WEATHER_LAYOUT );
	PendingIntent pendingWeatherIntent = PendingIntent.getBroadcast( mContext , 0 , intentWeatherClick , 0 );
	rv.setOnClickPendingIntent( R.id.city_textview , pendingWeatherIntent );
	rv.setOnClickPendingIntent( R.id.weather_city , pendingWeatherIntent );
	rv.setOnClickPendingIntent( R.id.temperature_current , pendingWeatherIntent );
	rv.setOnClickPendingIntent( R.id.temperature_range , pendingWeatherIntent );
}
visibility = showWeatherVeiw ? View.VISIBLE : View.GONE;
setWeatherVisibility( visibility );

上面代码可以看到。我们的关键点在于:

rv.setOnClickPendingIntent( R.id.date_textview , pendingDateIntent );就是这个方法设置了点击事件的监听。第一个参数是rv这个view中,布局里面的某一个view的id,第二个参数是PendingIntent是具体的广播意图。里面包含了如果点击这个view,WidgetProvider将受到的广播。

widget中,点击事件是通过广播传递的。intent的action就是我们在manifest里看到的三个action。所以我们用三个action来区分三种view的点击事件。

下面贴出来三个action的变量值,方便大家逻辑跟踪。这三个值都是WidgetProvider中的:

public static final String CLICK_CLOCK_LAYOUT = "com.cooee.phenix.widget.click.clock_layout";
public static final String CLICK_WEATHER_LAYOUT = "com.cooee.phenix.widget.click.weather_layout";
public static final String CLICK_DATE_LAYOUT = "com.cooee.phenix.widget.click.date_layout";	

通过点击事件的启发,我发现虽然我们不能具体拿到布局里的每一个view,但是我们可以通过remoteview来改变它。

比如这句:rv.setViewVisibility( R.id.clock_layout , visibility );//设置view是否可见,我似乎找到了某种规律。
至此,我们已经设置好了widget中view的点击事件。

当我们点击view时,WidgetProvider就会收到广播,我们通过区分intent的action来处理不同view的点击:

public void onClick(
		String action )
{
	//点击时钟布局
	if( action.equals( WidgetProvider.CLICK_CLOCK_LAYOUT ) )
	{
		ClockCalendarManager.getInstance( mContext ).onClickClock();
	}
	//点击日期
	else if( action.equals( WidgetProvider.CLICK_DATE_LAYOUT ) )
	{
		ClockCalendarManager.getInstance( mContext ).onclickDate();
	}
	//点击天气
	else if( action.equals( WidgetProvider.CLICK_WEATHER_LAYOUT ) )
	{
		WeatherManager.getInstance( mContext ).onClick();
	}
	else
	{
		Log.d( TAG , "其他" );
	}
}

5、widget修改后更新显示

以上我们讲了widget的manifest注册、widgetinfo、widget布局、如何获取view、如何设置点击事件、如何响应点击事件。

下面我们来看看如何更新一个view显示的内容、如何更新我们的widget。

以我们的时钟为例,当我们需要更新view时:

TwinkleClockwidgetManager instance = TwinkleClockwidgetManager.getInstance( mContext );
RemoteViews rv = instance.getRv();
if( instance.showClockVeiw )
{
	rv.setImageViewResource( R.id.clock_minute_tens , timeNumbers[mCurrentMinute / 10] );
	rv.setImageViewResource( R.id.clock_minute_ones , timeNumbers[mCurrentMinute % 10] );
}

哈哈,果然跟想的一样,都是通过remoteview来设置,第一个参数是要改变的view的id,第二个是要设置的值。

改变完view之后,我们需要更新一下widget,就能改变桌面上widget的显示了:

//更新插件
AppWidgetManager appWidgetManger = AppWidgetManager.getInstance( mContext );//第一步,通过context拿到appwidgetManager
int[] appIds = appWidgetManger.getAppWidgetIds( new ComponentName( mContext , WidgetProvider.class ) );//第二步,通过widgetManager和包类名拿到widgetId
appWidgetManger.updateAppWidget( appIds , rv );//第三步,更新widget的显示

总结:

以上就是这一周多时间对widget的了解,贴一张widget的图展示小成果:嘿嘿嘿

其中还有些坑无法解惑:

1、widget如何做动画?(好像在哪看到过别的桌面的widget,日历时钟,过一天,日历更新时是带一个旋转的动画的)

2、widget如何可拉伸?(launcher上显示widget时,有的widget长按松手之后可以改变widget在桌面上显示的大小,目前还不确定这部分是桌面做?还是需要widget这边协助。。。)

以上仅代表个人观点,如有理解不到位的地方,还请指正。欢迎留言,欢迎私信。

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值