有什么好的网站查做外贸出口的企业,如何建设网站zy258,开发者软件下载,wordpress4.7.8Glance#xff0c;官方对其解释是使用 Jetpack Compose 样式的 API 构建远程 Surface 的布局#xff0c;通俗的讲就是使用Compose风格的API来搭建小插件布局#xff0c;其最新版本是2022年2月23日更新的1.0.0-alpha03。众所周知#xff0c;Compose样式的API与原生差别不小官方对其解释是使用 Jetpack Compose 样式的 API 构建远程 Surface 的布局通俗的讲就是使用Compose风格的API来搭建小插件布局其最新版本是2022年2月23日更新的1.0.0-alpha03。众所周知Compose样式的API与原生差别不小至于widget这块改动如何接下来让我们来一探究竟。
声明依赖项
第一步肯定要添加对应依赖相应的都是在build.gradle中添加如果你的工程还没支持Compose要先添加
android {buildFeatures {compose true}composeOptions {kotlinCompilerExtensionVersion 1.1.0-beta03}kotlinOptions {jvmTarget 1.8}
}如果已经支持上述依赖可以省略但下述依赖不能省略继续添加
dependencies {implementation(androidx.glance:glance-appwidget:1.0.0-alpha03)implementation(androidx.glance:glance-wear-tiles:1.0.0-alpha03)
}以上是官方的标准依赖方式同样以下面这种方式依赖也可以
implementation androidx.glance:glance-appwidget:
implementation androidx.glance:glance:
implementation androidx.glance:glance-appwidget:1.0.0-alpha03创建对应 widget
首先编写对应布局放在对应/layout/xml目录下
widget_info.xml
?xml version1.0 encodingutf-8?
appwidget-providerxmlns:androidhttp://schemas.android.com/apk/res/androidandroid:descriptionstring/app_nameandroid:minWidth150dpandroid:minHeight66dpandroid:resizeModehorizontal|verticalandroid:targetCellWidth3android:targetCellHeight2android:widgetCategoryhome_screen/我在上一篇介绍widget的文章中说过widget其实就是个广播广播属于四大组件而四大组件都要在AndroidManifest清单文件中注册
receiverandroid:name.CounterWidgetReceiverandroid:enabledbool/glance_appwidget_availableandroid:exportedfalseintent-filteraction android:nameandroid.appwidget.action.APPWIDGET_UPDATE //intent-filtermeta-dataandroid:nameandroid.appwidget.providerandroid:resourcexml/widget_info /
/receiver对应CounterWidgetReceiver代码为
import androidx.glance.appwidget.GlanceAppWidget
import androidx.glance.appwidget.GlanceAppWidgetReceiver
import com.ktfly.comapp.ui.theme.CounterWidgetclass CounterWidgetReceiver : GlanceAppWidgetReceiver(){override val glanceAppWidget: GlanceAppWidget CounterWidget()
}可能看到这里你就迷惑了widget对应广播类不是要继承AppWidgetProvider然后实现相应方法的吗其实Glance提供的GlanceAppWidgetReceiver类就已经继承了AppWidgetProvider我们使用Glance需要GlanceAppWidgetReceiver
abstract class GlanceAppWidgetReceiver : AppWidgetProvider() {private companion object {private const val TAG GlanceAppWidgetReceiver}/*** Instance of the [GlanceAppWidget] to use to generate the App Widget and send it to the* [AppWidgetManager]*/abstract val glanceAppWidget: GlanceAppWidgetCallSuperoverride fun onUpdate(context: Context,appWidgetManager: AppWidgetManager,appWidgetIds: IntArray) {if (Build.VERSION.SDK_INT Build.VERSION_CODES.M) {Log.w(TAG,Using Glance in devices with API23 is untested and might behave unexpectedly.)}goAsync {updateManager(context)appWidgetIds.map { async { glanceAppWidget.update(context, appWidgetManager, it) } }.awaitAll()}}CallSuperoverride fun onAppWidgetOptionsChanged(context: Context,appWidgetManager: AppWidgetManager,appWidgetId: Int,newOptions: Bundle) {goAsync {updateManager(context)glanceAppWidget.resize(context, appWidgetManager, appWidgetId, newOptions)}}CallSuperoverride fun onDeleted(context: Context, appWidgetIds: IntArray) {goAsync {updateManager(context)appWidgetIds.forEach { glanceAppWidget.deleted(context, it) }}}private fun CoroutineScope.updateManager(context: Context) {launch {runAndLogExceptions {GlanceAppWidgetManager(context).updateReceiver(thisGlanceAppWidgetReceiver, glanceAppWidget)}}}override fun onReceive(context: Context, intent: Intent) {runAndLogExceptions {if (intent.action Intent.ACTION_LOCALE_CHANGED) {val appWidgetManager AppWidgetManager.getInstance(context)val componentName ComponentName(context.packageName, checkNotNull(javaClass.canonicalName))onUpdate(context,appWidgetManager,appWidgetManager.getAppWidgetIds(componentName))return}super.onReceive(context, intent)}}
}private inline fun runAndLogExceptions(block: () - Unit) {try {block()} catch (ex: CancellationException) {// Nothing to do} catch (throwable: Throwable) {logException(throwable)}
}基本流程方法跟原生widget的差别不大其含义也无差别如果对原生Widget不太了解的同学可以翻阅我上一篇文章这里还有官方注释“Using Glance in devices with API23 is untested and might behave unexpectedly.”。在6.0版本以下的Android系统上使用Glance的情况未经测试可能有出乎意料的情况发生。在开始编写widget代码之前我们先来了解下其使用组件与Compose中的对应组件的些许差别。
差别
根据官方提示可使用的Compose组合项如下Box、Row、Column、Text、Button、LazyColumn、Image、Spacer。原生widget是不支持自定义View的但Compose能通过自定义组件的方式来“自定义”出我们想要的视图这一点来看相对更加灵活。
Compose中使用的修饰符是Modifier这里修饰可组合项的修饰符是GlanceModifier使用方式并无二致其余组件也有些许差异这个我们放到后面来说
Action
以前使用widget跳转页面啥的都离不开PendingIntent但是Glance中则采取另一套方式
actionStartActivity
看函数命名就得知通过Action启动Activity。共有三种使用方式
// 通过包名启动Activity
public fun actionStartActivity(componentName: ComponentName,parameters: ActionParameters actionParametersOf()
): Action StartActivityComponentAction(componentName, parameters)// 直接启动Activity
public fun T : Activity actionStartActivity(activity: ClassT,parameters: ActionParameters actionParametersOf()
): Action StartActivityClassAction(activity, parameters)//调用actionStartActivity启动Activity内联函数
public inline fun reified T : Activity actionStartActivity(parameters: ActionParameters actionParametersOf()
): Action actionStartActivity(T::class.java, parameters)\其对应的使用方式也简单
Button(text Jump, onClick actionStartActivity(ComponentName(com.ktfly.comapp,com.ktfly.comapp.page.ShowActivity)))
Button(text Jump, onClick actionStartActivityShowActivity())
Button(text Jump, onClick actionStartActivity(ShowActivity::class.java))actionRunCallback
顾名思义此函数是通过Action执行Callback以下是官方提供的使用说明\
fun T : ActionCallback actionRunCallback(callbackClass: ClassT, parameters: ActionParameters actionParametersOf()
): Actioninline fun reified T : ActionCallback actionRunCallback(parameters: ActionParameters actionParametersOf()): Action使用方式
先创建一个继承actionRunCallback的回调类
class ActionDemoCallBack : ActionCallback {override suspend fun onRun(context: Context, glanceId: GlanceId, parameters: ActionParameters) {TODO(Not yet implemented)}
}然后在控件中调用
Button(text CallBack, onClick actionRunCallbackActionDemoCallBack())Button(text CallBack, onClick actionRunCallback(ActionDemoCallBack::class.java))\actionStartService
此函数是通过Action启动Service有以下四个使用方式
fun actionStartService(intent: Intent, isForegroundService: Boolean false
): Actionfun actionStartService(componentName: ComponentName, isForegroundService: Boolean false
): Actionfun T : Service actionStartService(service: ClassT, isForegroundService: Boolean false
): Actioninline fun reified T : Service actionStartService(isForegroundService: Boolean false): Action这里的isForegroundService参数含义是此服务是前台服务。在调用之前也需要先创建对应Service
class ActionDemoService : Service() {override fun onBind(intent: Intent?): IBinder? {TODO(Not yet implemented)}
}其在控件中使用方式如下
Button(text start, onClick actionStartServiceActionDemoService())Button(text start, onClick actionStartService(ActionDemoService::class.java))actionStartBroadcastReceiver
此函数是通过Action启动BroadcastReceiver有以下使用方式
fun actionSendBroadcast(action: String, componentName: ComponentName? null
): Actionfun actionSendBroadcast(intent: Intent): Actionfun actionSendBroadcast(componentName: ComponentName): Actionfun T : BroadcastReceiver actionSendBroadcast(receiver: ClassT): Actioninline fun reified T : BroadcastReceiver actionSendBroadcast(): Actionfun actionStartActivity(intent: Intent, parameters: ActionParameters actionParametersOf()
): Action其各函数用法跟actionStartActivity函数差不多这里不做赘述。你会发现以上函数中经常出现ActionParameters。其实ActionParameters就是给Action提供参数这里不做赘述。
创建widget
创建对应的widget类通过GlanceStateDefinition来保留GlanceAppWidget的状态通过点击事件回调自定义的ActionCallBack达到更改widget中数字的目的
import android.content.Context
import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.intPreferencesKey
import androidx.glance.*
import androidx.glance.action.ActionParameters
import androidx.glance.action.actionParametersOf
import androidx.glance.appwidget.GlanceAppWidget
import androidx.glance.appwidget.action.ActionCallback
import androidx.glance.appwidget.action.actionRunCallback
import androidx.glance.appwidget.state.updateAppWidgetState
import androidx.glance.layout.*
import androidx.glance.state.GlanceStateDefinition
import androidx.glance.state.PreferencesGlanceStateDefinition
import androidx.glance.text.Text
import androidx.glance.text.TextAlign
import androidx.glance.text.TextStyle
import androidx.glance.unit.ColorProviderprivate val countPreferenceKey intPreferencesKey(widget-key)
private val countParamKey ActionParameters.KeyInt(widget-key)class CounterWidget : GlanceAppWidget(){override val stateDefinition: GlanceStateDefinition* PreferencesGlanceStateDefinitionComposableoverride fun Content(){val prefs currentStatePreferences()val count prefs[countPreferenceKey] ?: 1Column(horizontalAlignment Alignment.CenterHorizontally,verticalAlignment Alignment.CenterVertically,modifier GlanceModifier.background(Color.Yellow).fillMaxSize()) {Text(text count.toString(),modifier GlanceModifier.fillMaxWidth(),style TextStyle(textAlign TextAlign.Center,color ColorProvider(Color.Blue),fontSize 50.sp))Spacer(modifier GlanceModifier.padding(8.dp))Button(text 变两倍,modifier GlanceModifier.background(Color(0xFFB6C0C9)).size(100.dp,50.dp),onClick actionRunCallbackUpdateActionCallback(parameters actionParametersOf(countParamKey to (count count))))}}
}class UpdateActionCallback : ActionCallback{override suspend fun onRun(context: Context, glanceId: GlanceId,parameters: ActionParameters) {val count requireNotNull(parameters[countParamKey])updateAppWidgetState(context context,definition PreferencesGlanceStateDefinition,glanceId glanceId){ preferences -preferences.toMutablePreferences().apply {this[countPreferenceKey] count}}CounterWidget().update(context,glanceId)}
}运行后效果如下 也许你会发现上述导包与平常Compose导包不一样 控件导的包都是glance包下的当然不仅是Column还有Button、Image等参数都有变化但变化不大例如Image的差异
原Compose中
Image(modifier modifier,painter BitmapPainter(bitmap),contentDescription ,contentScale contentScale)Image(modifier modifier,painter painterResource(资源id),contentDescription ,contentScale contentScale)Glance中
public fun Image(provider: ImageProvider,contentDescription: String?,modifier: GlanceModifier GlanceModifier,contentScale: ContentScale ContentScale.Fit
)其余控件差异大同小异这里不做赘述。