最近开始准备做车机的 Launcher ,之前没接触过Launcher最近开始恶补这块知识,在学习Launcher3 的过程中发现了一个很有趣的东西那就 AppWidget(桌面小部件),
并且在我们项目规划的Launcher 中AppWidget占了很大的比重,所以学习好AppWidget至关重要。
如下图红色箭头所指的都是 AppWidget

Launcher启动onCreate()方法初始化mAppWidgetManager, mAppWidgetHost对象,AppWidgetHost是launcher承载AppWidgetView的宿主。
public void onCreate() {
...
//得到AppWidget管理实例 : AppWidgetManager , AppWidgetHost , AppWidgetHostView三个类的关系
mAppWidgetManager = AppWidgetManagerCompat.getInstance(this); //1
mAppWidgetHost = new LauncherAppWidgetHost(this); //2
// Host启动监听,监听LauncherProvider中的数据改变
mAppWidgetHost.startListening(); //3
...
}
public static AppWidgetManagerCompat getInstance(Context context) {
synchronized (sInstanceLock) {
if (sInstance == null) {
if (Utilities.ATLEAST_OREO) {
sInstance = new AppWidgetManagerCompatVO(context.getApplicationContext());
} else {
sInstance = new AppWidgetManagerCompatVL(context.getApplicationContext());
}
}
return sInstance;
}
}
public AppWidgetHost(Context context, int hostId, OnClickHandler handler, Looper looper) {
mContextOpPackageName = context.getOpPackageName();
mHostId = hostId;
mOnClickHandler = handler;
mHandler = new UpdateHandler(looper);
mCallbacks = new Callbacks(mHandler);
mDisplayMetrics = context.getResources().getDisplayMetrics();
bindService(context);
}
private static void bindService(Context context) {
synchronized (sServiceLock) {
if (sServiceInitialized) {
return;
}
sServiceInitialized = true;
PackageManager packageManager = context.getPackageManager();
if (!packageManager.hasSystemFeature(PackageManager.FEATURE_APP_WIDGETS)
&& !context.getResources().getBoolean(R.bool.config_enableAppWidgetService)) {
return;
}
IBinder b = ServiceManager.getService(Context.APPWIDGET_SERVICE);
sService = IAppWidgetService.Stub.asInterface(b);
}
}
public void startListening() {
if (sService == null) {
return;
}
final int[] idsToUpdate;
synchronized (mViews) {
int N = mViews.size();
idsToUpdate = new int[N];
for (int i = 0; i < N; i++) {
idsToUpdate[i] = mViews.keyAt(i);
}
}
List<PendingHostUpdate> updates;
try {
updates = sService.startListening(
mCallbacks, mContextOpPackageName, mHostId, idsToUpdate).getList();
}
catch (RemoteException e) {
throw new RuntimeException("system server dead?", e);
}
int N = updates.size();
for (int i = 0; i < N; i++) {
PendingHostUpdate update = updates.get(i);
switch (update.type) {
case PendingHostUpdate.TYPE_VIEWS_UPDATE:
updateAppWidgetView(update.appWidgetId, update.views);
break;
case PendingHostUpdate.TYPE_PROVIDER_CHANGED:
onProviderChanged(update.appWidgetId, update.widgetInfo);
break;
case PendingHostUpdate.TYPE_VIEW_DATA_CHANGED:
viewDataChanged(update.appWidgetId, update.viewId);
}
}
}
@Thunk void completeAddAppWidget(int appWidgetId, ItemInfo itemInfo,
AppWidgetHostView hostView, LauncherAppWidgetProviderInfo appWidgetInfo) {
if (appWidgetInfo == null) {
appWidgetInfo = mAppWidgetManager.getLauncherAppWidgetInfo(appWidgetId);
}
LauncherAppWidgetInfo launcherInfo;
launcherInfo = new LauncherAppWidgetInfo(appWidgetId, appWidgetInfo.provider);
launcherInfo.spanX = itemInfo.spanX;
launcherInfo.spanY = itemInfo.spanY;
launcherInfo.minSpanX = itemInfo.minSpanX;
launcherInfo.minSpanY = itemInfo.minSpanY;
launcherInfo.user = appWidgetInfo.getProfile();
getModelWriter().addItemToDatabase(launcherInfo,
itemInfo.container, itemInfo.screenId, itemInfo.cellX, itemInfo.cellY);
if (hostView == null) {
// Perform actual inflation because we're live
hostView = mAppWidgetHost.createView(this, appWidgetId, appWidgetInfo);
}
hostView.setVisibility(View.VISIBLE);
prepareAppWidget(hostView, launcherInfo);
mWorkspace.addInScreen(hostView, launcherInfo);
}
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2009 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<favorites xmlns:launcher="http://schemas.android.com/apk/res-auto/com.android.launcher3">
<!-- Hotseat -->
<include launcher:workspace="@xml/dw_phone_hotseat" />
<!-- Bottom row -->
<resolve
launcher:screen="0"
launcher:x="0"
launcher:y="-1" >
<favorite launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_EMAIL;end" />
<favorite launcher:uri="mailto:" />
</resolve>
<resolve
launcher:screen="0"
launcher:x="1"
launcher:y="-1" >
<favorite launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_GALLERY;end" />
<favorite launcher:uri="#Intent;type=images/*;end" />
</resolve>
<resolve
launcher:screen="0"
launcher:x="4"
launcher:y="-1" >
<favorite launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_MARKET;end" />
<favorite launcher:uri="market://details?id=com.android.launcher" />
</resolve>
<!-- 预置 小组件 -->
<appwidget
launcher:packageName="com.example.democollect"
launcher:className="com.example.democollect.appwidget.MyAppWidgetProvider"
launcher:screen="0"
launcher:container="-100"
launcher:spanX="3"
launcher:spanY="1"
launcher:x="2"
launcher:y="2"/>
</favorites>
其中
<receiver
android:name=".appwidget.MyAppWidgetProvider"
android:label="测试小组件">
<intent-filter>
<!--所有的窗口小部件都接收android.appwidget.action.APPWIDGET_UPDATE 动作的广播,
该广播根据android:updatePeriodMillis设定的间隔时间发出广播,用于定时更新桌面上的所有窗口小部件。-->
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
<!--定义一个自定义的动作广播,可以通过在该广播接收器中注册自定义的动作以使窗口小部件接收自定义的广播。-->
<action android:name="com.oitsme.REFRESH_WIDGET" />
<action android:name="com.oitsme.LOCK_ACTION" />
<action android:name="com.oitsme.UNLOCK_ACTION" />
</intent-filter>
<!--声明了 Widget 的 AppWidgetProviderInfo 对应的资源 xml 的位置,用的是 xml 目录下的 example_appwidget_info.xml。-->
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/appwidget" />
</receiver>
<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:initialLayout="@layout/appwidget_layout"
android:minWidth="200dp"
android:minHeight="100dp"
android:previewImage="@mipmap/ic_launcher"
android:resizeMode="vertical|horizontal"
android:updatePeriodMillis="0"
android:widgetCategory="home_screen|keyguard" />
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/ll_right"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="40dp"
android:background="#ccc">
<ImageView
android:id="@+id/iv_icon"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_centerVertical="true"
android:layout_marginEnd="5dp"
android:layout_marginStart="5dp"
android:background="@mipmap/ic_launcher_round" />
<TextView
android:id="@+id/tv_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toEndOf="@id/iv_icon"
android:text="Widget" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_alignParentEnd="true"
android:gravity="center_vertical"
android:orientation="horizontal">
<ProgressBar
android:id="@+id/progress_bar"
android:layout_width="20dp"
android:layout_height="20dp"
android:indeterminateTint="@color/teal_200"
android:indeterminateTintMode="src_atop"
android:visibility="gone" />
<Button
android:id="@+id/tv_refresh"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="15dp"
android:text="刷新"
android:padding="5dp"
android:textSize="12sp" />
</LinearLayout>
</RelativeLayout>
</LinearLayout>
仅支持以下布局类:
FrameLayout、LinearLayout 、RelativeLayout 、GridLayout 、AnalogClock 、Button 、Chronometer 、ImageButton 、ImageView 、ProgressBar 、TextView 、ViewFlipper 、 ListView 、 GridView 、StackView 、AdapterViewFlipper 、 ViewStub 不支持这些类的后代。
AppWidgetProvider 继承自 BroadcastReceiver,内部逻辑非常简单,就是在 onReceive() 中处理 Widget 相关的广播事件,分发到各个回调函数中(onUpdate(), onDeleted(), onEnabled(), onDisabled, onAppWidgetOptionsChanged())。
public class MyAppWidgetProvider extends AppWidgetProvider {
private static final String TAG = MyAppWidgetProvider.class.getSimpleName();
public static final String REFRESH_WIDGET = "com.oitsme.REFRESH_WIDGET";
private Context mContext;
private static final Handler mHandler = new Handler();
private final Runnable runnable = new Runnable() {
@Override
public void run() {
hideLoading(mContext);
}
};
@Override
public void onReceive(Context context, Intent intent) {
super.onReceive(context, intent);
String action = intent.getAction();
Log.i(TAG, "onReceive");
if (action.equals(REFRESH_WIDGET)) {
// 接受“bt_refresh”的点击事件的广播
showLoading(context);
mHandler.postDelayed(runnable, 2000);
}
}
/**
* 到达指定的更新时间或者当用户向桌面添加AppWidget时被调用
* appWidgetIds:桌面上所有的widget都会被分配一个唯一的ID标识,这个数组就是他们的列表
*
* @param context
* @param appWidgetManager
* @param appWidgetIds
*/
@Override
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
super.onUpdate(context, appWidgetManager, appWidgetIds);
this.mContext = context;
Log.i(TAG, "onUpdate");
// 获取AppWidget对应的视图
RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.appwidget_layout);
// 设置响应 “按钮(bt_refresh)” 的intent
Intent btIntent = new Intent(context, MyAppWidgetProvider.class);
btIntent.setAction(REFRESH_WIDGET);
// btIntent.putExtra(REFRESH_WIDGET,"REFRESH_WIDGET");
PendingIntent btPendingIntent = PendingIntent.getBroadcast(context, 0, btIntent, PendingIntent.FLAG_UPDATE_CURRENT);
remoteViews.setOnClickPendingIntent(R.id.tv_refresh, btPendingIntent);
// 调用集合管理器对集合进行更新
appWidgetManager.updateAppWidget(appWidgetIds, remoteViews);
}
/**
* 显示加载loading
*/
private void showLoading(Context context) {
RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.appwidget_layout);
remoteViews.setViewVisibility(R.id.tv_refresh, View.VISIBLE);
remoteViews.setViewVisibility(R.id.progress_bar, View.VISIBLE);
remoteViews.setTextViewText(R.id.tv_refresh, "正在刷新...");
refreshWidget(context, remoteViews, false);
}
/**
* 隐藏加载loading
*/
private void hideLoading(Context context) {
RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.appwidget_layout);
remoteViews.setViewVisibility(R.id.progress_bar, View.GONE);
remoteViews.setTextViewText(R.id.tv_refresh, "刷新");
refreshWidget(context, remoteViews, false);
}
/**
* 刷新Widget
*/
private void refreshWidget(Context context, RemoteViews remoteViews, boolean refreshList) {
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
ComponentName componentName = new ComponentName(context, MyAppWidgetProvider.class);
appWidgetManager.updateAppWidget(componentName, remoteViews);
}
}
桌面长按桌面空白部分弹框选择 Widgets

选择自己的小组件长按拖拽到桌面


按照目前的情况,这个问题不适合我们的问答形式。我们希望答案得到事实、引用或专业知识的支持,但这个问题可能会引发辩论、争论、投票或扩展讨论。如果您觉得这个问题可以改进并可能重新打开,visitthehelpcenter指导。关闭10年前。我想知道是否可以使用ruby创建桌面应用程序以及缺点,你能举个例子吗?在Windows中使用的应用程序谢谢
我正在尝试使用ruby中的seleniumwebdriver从gmail桌面通知中获取数据 最佳答案 开箱即用的想法,用Selenium截屏并用OCR处理图像?https://github.com/suyesh/ocr_space我假设Selenium只允许您与页面数据交互。 关于ruby-如何在seleniumwebdriver-ruby中自动化桌面通知,我们在StackOverflow上找到一个类似的问题: https://stackoverflo
1.当前环境及情况说明宽带:电信、光猫桥接、路由器拨号ipv6地址:在各大网站都能ping通这个ipv6地址,本机也能访问ipv6的网站问题:其它外网电脑除了能ping通这个ipv6地址之外什么都访问不了2.可能出现问题的原因本机防火墙拦截了(关闭防火墙也是一样的)×光猫防火墙拦截了(试了不行,貌似桥接后跟光猫就没关系了)×路由器防火墙拦截了(用的是小米AX6000,IPV6配置的地方有个防火墙没有关闭)√运营商拦截了(根据最终效果测试,80端口、443端口被拦截无法使用,尽量用些不常用的端口)×3.路由器设置(关闭IPV6防火墙) 不同路由器可能设置不同,根据情况处理,我这里做为一个参考关闭
关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭7年前。Improvethisquestion到目前为止,我所有的编程经验都是桌面开发(主要是C/C++和OpenGL/DirectX),但我有兴趣尝试一些Web开发。我正在考虑的两个方向是RubyonRails和ASP.net。哪个应用最广泛?拥有哪种技能更有市场值(value)?谢谢!
关闭。这个问题不符合StackOverflowguidelines.它目前不接受答案。我们不允许提问寻求书籍、工具、软件库等的推荐。您可以编辑问题,以便用事实和引用来回答。关闭2年前。Improvethisquestion经过长时间的谷歌搜索,我想知道是否真的存在基于Ruby的维护图形用户界面。这是我检查过的:鞋子:我觉得我不能用它打造坚如磐石的平台Cocoa和MacRuby:没有新鲜消息,几乎没有教程Qt4Ruby:同上FxRuby几乎没有更新...简而言之,我查看了所有呈现的guihere但我不相信...所以:我找不到Cocoa和Qt的正确文档吗?(我希望它是答案!)是否有任何基于
关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭8年前。ImprovethisquestionRuby是编写(可能和部分)平台独立桌面应用程序的好选择吗?是否有任何支持的库可以为windows、Linux、Mac操作系统编写代码我知道Java可以编写桌面应用程序,那么Ruby呢?
Nvidia显卡在Archlinux上安装桌面环境wayland+hyprland一、安装系统二、安装桌面环境三、安装hyprland四、NVIDIA显卡的配置1、grub.cfg2、修改mkinitcpio.conf3、设置pacmanhook4、添加环境变量五、运行hyprland六登录管理器七一些使用中的发现和配置1、快捷键切换layout脚本2、动画3、键绑定4、一些软件2023.2.10修改环境变量部分2023.2.12修改环境变量部分,另外添加了hyprland的软件网站,有几个非常有意思2023.2.13修改键绑定部分内容,上传一张截图2023.2.16添加github仓库链接2
我正要开始一个开发ruby桌面应用程序的项目。我希望有相当大的规模,我想学习在模块之间划分代码的技术和管理复杂性的其他技术。我看过的大多数大型应用程序都是rails应用程序,但这些应用程序并不是很有帮助,因为大部分工作都是由rails自己完成的。你建议我看看什么源代码?我对库或rails应用程序不感兴趣,因为我了解它们是如何工作的。cli应用程序还可以,但我主要对gui应用程序感兴趣(我正在使用gtk+,但我可以从使用其他gui工具包的应用程序中学到同样多的东西)。 最佳答案 freebase插件管理器系统被设计成一种在gui应用程
按照目前的情况,这个问题不适合我们的问答形式。我们希望答案得到事实、引用或专业知识的支持,但这个问题可能会引发辩论、争论、投票或扩展讨论。如果您觉得这个问题可以改进并可能重新打开,visitthehelpcenter指导。关闭10年前。我正在寻找类似于Rails的桌面应用程序框架:良好的ORMMVC默认目录结构查看助手/DSL优雅开源有趣的语言相当成熟
文章目录1.安装WindowsServer20192.开启WLAN服务3.固定IP地址4.开启远程桌面服务4.1添加远程桌面服务4.2激活服务器4.3安装许可证5.配置远程桌面服务5.1配置许可证服务器和授权模式5.2配置连接模式5.3启用计算机的远程功能5.4设置用户能使用简单密码6.配置CUDA环境6.1更新驱动6.2安装CUDA6.3安装cuDNN6.4配置环境变量7.配置Anaconda+Pycharm环境7.1安装Anaconda7.2安装Pycharm8.配置Tensorflow+Pytorch环境8.1创建环境8.2配置pip和conda国内下载源8.3安装Tensorflow-