本篇文章主要是记录自己学习Android自定义桌面悬浮控件,AppWidget的过程。
现在很多App都支持悬浮桌面的控件,我们可以通过系统菜单长按,或者别的方式来设置,例如自带的备忘录,就可以写完后挂在手机桌面上。
现在我们看看运行效果:
在安装应用后,设置桌面悬浮的时候会看到我们刚才安装的应用:
然后我们点击这个控件,让他显示在桌面上:
可以看到他已经现在我们桌面上了,而且长按可以拖动摆放他的位置。
现在来记录一下学习过程,
一.设置简单的自定义桌面控件:
1.在Layout下新建widget_setting.xml文件,用于设置桌面控件属性:
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:initialLayout="@layout/layout_widget"
android:minHeight="140dp"
android:minWidth="140dp"
android:previewImage="@mipmap/ic_launcher"
android:updatePeriodMillis="20000"
android:widgetCategory="home_screen"
>
appwidget-provider>
这里面设置了minHeight minWidth 和previreImage还有他的update函数时间间隔。
2.设计自己的AppWidget界面:
layout_widget.xml
<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="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/iamge_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/ic_launcher"/>
<Button
android:id="@+id/btn"
android:textAllCaps="false"
android:text="sure"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
LinearLayout>
<TextView
android:id="@+id/text_view"
android:textSize="20sp"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
LinearLayout>
这里我们的界面设计就是图二显示在桌面上的布局设计。
3.建立TestWidget类继承自AppWidgetProvider并且建立服务并且按时更新界面
就和广播一样,我们需要实现其方法onReceive;
在AppWidgetManager中有很多系统广播,例如ACTION_APPWIDGET_UPDATE,
ACTION_APPWIDGET_DELETED
等,会在桌面控件更新,创建,和删除时候发出广播给广播接收器,我们可以在里面做一些操作。
TestWidget.java
public class TestWedigt extends AppWidgetProvider {
private static final String WIDGET_BTN_ACTION = "WIDGET_BTN_ACTION";
private static final int UPDATE_DURATION = 2 * 1000;
private PendingIntent pendingIntent = null;
static int time =0;
private WedigetService service ;
public void onReceive(Context context, Intent intent) {
super.onReceive(context, intent);
String action = intent.getAction();//获取到广播的Action
if(intent!=null&& TextUtils.equals(intent.getAction(),WIDGET_BTN_ACTION))//广播的Action为按钮按下
{
RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.layout_widget);//注意:需要【重新】构造一个RemoteViewstime++;
remoteViews.setTextViewText(R.id.text_view,"Hello Andoird"+time);
remoteViews.setTextColor(R.id.text_view, Color.GRAY);
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);// 单例模式
ComponentName componentName = new ComponentName(context,TestWedigt.class);
appWidgetManager.updateAppWidget(componentName, remoteViews);//setText之后,记得更新一下,传入自定义界面
}
if(AppWidgetManager.ACTION_APPWIDGET_UPDATE.equals(action)){//广播为更新广播
Bundle bundle = intent.getExtras();
if(bundle!=null){
int[] appWidgetIds = bundle.getIntArray(AppWidgetManager.EXTRA_APPWIDGET_IDS);
if( appWidgetIds!=null&&appWidgetIds.length>0){
this.onUpdate(context,AppWidgetManager.getInstance(context),appWidgetIds);
}
}
}else if(AppWidgetManager.ACTION_APPWIDGET_DELETED.equals(action)){//广播为删除控件广播
Bundle bundle = intent.getExtras();
if(bundle!=null&&bundle.containsKey(AppWidgetManager.EXTRA_APPWIDGET_ID)){
final int appWidgetIds = bundle.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID);
this.onDeleted(context,new int[]{appWidgetIds});
}
}
}
/*
* 重写onUpdate方法,
* */
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
super.onUpdate(context, appWidgetManager, appWidgetIds);
RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.layout_widget);
Intent intent = new Intent();
intent.setClass(context, TestWedigt.class);
intent.setAction(WIDGET_BTN_ACTION);//设置广播Action为BuutonAction
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, 0);
remoteViews.setOnClickPendingIntent(R.id.btn, pendingIntent);//设置按钮的点击监听
appWidgetManager.updateAppWidget(appWidgetIds, remoteViews);
context.startService(new Intent(context, WedigetService.class));//开启服务
}
}
因为AppWidget的更新时间系统有个默认值,好像是二三十分钟,所以我们为了实现到时间自动更新,我们就要自己设置。但是在widget_setting我们设置的那个时间间隔,是不起作用的,所以我们需要通过服务来达到计时,到时间,然后更新界面的操作,这样一直循环。
我们这个类里面,还设置了界面中Button的监听事件,通过发送广播并且设置Action然后在onReceive中接受广播,判断Action的方法来设置监听。
RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.layout_widget);
Intent intent = new Intent();
intent.setClass(context, TestWedigt.class);
intent.setAction(WIDGET_BTN_ACTION);//设置广播Action为BuutonAction
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, 0);
remoteViews.setOnClickPendingIntent(R.id.btn, pendingIntent);//设置按钮的点击监听
public void onReceive(Context context, Intent intent) {
super.onReceive(context, intent);
String action = intent.getAction();//获取到广播的Action
if(intent!=null&& TextUtils.equals(intent.getAction(),WIDGET_BTN_ACTION))//广播的Action为按钮按下
{
RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.layout_widget);//注意:需要【重新】构造一个RemoteViewstime++;
remoteViews.setTextViewText(R.id.text_view,"Hello Andoird"+time);
remoteViews.setTextColor(R.id.text_view, Color.GRAY);
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);// 单例模式
ComponentName componentName = new ComponentName(context,TestWedigt.class);
appWidgetManager.updateAppWidget(componentName, remoteViews);//setText之后,记得更新一下,传入自定义界面
}
下面看看服务类
WeidgetService.java
public class WedigetService extends Service {
static int time=0;
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
/*
* 每次重复启动服务后,后会调用这个方法。
* */
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
time++;
buildUpdate();
AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
Intent i = new Intent(this,WedigetService.class);
long cuutime = SystemClock.elapsedRealtime()+1000;//时间间隔
PendingIntent pi = PendingIntent.getService(this,0,i,0);
alarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP,cuutime,pi);//设置Alarm然后到时间再次启动服务,达成循环目的。
return super.onStartCommand(intent, flags, startId);
}
/*
* 更新控件的数据
* */
private void buildUpdate() {
RemoteViews view = new RemoteViews(getPackageName(), R.layout.layout_widget);
RemoteViews remoteViews = new RemoteViews(getApplicationContext().getPackageName(), R.layout.layout_widget);
remoteViews.setTextViewText(R.id.text_view,""+time);
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(getBaseContext());
appWidgetManager.updateAppWidget(new ComponentName(getBaseContext(), TestWedigt.class), remoteViews);
}
}
这里面没什么东西,主要就是在多次启动服务的时候会重复调用onStartCommand方法,
所以我们在onStartCommand里,设置了Alarm,然后设定时间间隔,每次到达时间后就进行更新界面操作和服务自启动操作,这样就可以让服务一直运行并且自己到达时间后更新界面的目的。
4.添加注册。
我们需要注册服务和我们刚才写的TestWidget.
<receiver android:name=".TestWedigt">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="@layout/widget_setting"
>meta-data>
receiver>
<service android:name=".WedigetService">
service>
data
android:name="android.appwidget.provider"
android:resource="@layout/widget_setting"
> data>
要注意这里的写法哦。