AppWidget 添加动画效果
2020-09-15 本文已影响0人
Tom_Ji
最近的UI给的效果图要求在widget上显示动画效果,怕我看不明白特意做了一个视频,因为软件还未发布,视频就不贴出来了。这里把实现过程记录一下,以及自己写的demo,有相关需求的小伙伴们可以少走点弯路了。
demo的效果如图:
2020-09-15 16.39.08.gif效果图只截取了widget的部分,其它的没有截取。图片只用到了箭头,可以自己从网上随便下载一个,命名为rotate_img
废话不多说,直接上代码。
先创建动画文件,如下文件放在anim
文件夹中。
rotate_up.xml
<?xml version="1.0" encoding="utf-8"?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="300"
android:fillAfter="true"
android:fromDegrees="180"
android:pivotX="50%"
android:pivotY="50%"
android:toDegrees="360" />
rotate_down.xml
<?xml version="1.0" encoding="utf-8"?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="300"
android:fillAfter="true"
android:fromDegrees="0"
android:pivotX="50%"
android:pivotY="50%"
android:toDegrees="180" />
layout_rotate_up.xml
<?xml version="1.0" encoding="utf-8"?>
<layoutAnimation xmlns:android="http://schemas.android.com/apk/res/android"
android:animation="@anim/rotate_up" />
layout_rotate_down.xml
<?xml version="1.0" encoding="utf-8"?>
<layoutAnimation xmlns:android="http://schemas.android.com/apk/res/android"
android:animation="@anim/rotate_down" />
接着创建布局文件,以下文件放到layout文件夹中。
rotate_app_widget.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#09C"
android:orientation="vertical"
android:padding="@dimen/widget_margin">
<LinearLayout
android:id="@+id/replace"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_margin="20dp">
<ImageView
android:layout_width="40dp"
android:layout_height="40dp"
android:scaleType="centerInside"
android:src="@drawable/rotate_img" />
</LinearLayout>
<Button
android:id="@+id/down"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="向下"
tools:ignore="HardcodedText" />
<Button
android:id="@+id/up"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="向上"
tools:ignore="HardcodedText"/>
<Button
android:id="@+id/default_state"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="恢复默认"
tools:ignore="HardcodedText"/>
</LinearLayout>
layout_rotate_default.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
>
<ImageView
android:layout_width="40dp"
android:layout_height="40dp"
android:scaleType="centerInside"
android:src="@drawable/rotate_img"/>
</LinearLayout>
layout_rotate_down.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:layoutAnimation="@anim/layout_rotate_down"
>
<ImageView
android:layout_width="40dp"
android:layout_height="40dp"
android:scaleType="centerInside"
android:src="@drawable/rotate_img"/>
</LinearLayout>
layout_rotate_up.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:layoutAnimation="@anim/layout_rotate_up"
>
<ImageView
android:layout_width="40dp"
android:layout_height="40dp"
android:scaleType="centerInside"
android:src="@drawable/rotate_img" />
</LinearLayout>
res目录如下:
res目录截图.pngRotateAppWidget.kt
package com.tom.rotatewidgetdemo
import android.app.PendingIntent
import android.appwidget.AppWidgetManager
import android.appwidget.AppWidgetProvider
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.widget.RemoteViews
/**
* Implementation of App Widget functionality.
*/
class RotateAppWidget : AppWidgetProvider() {
companion object {
internal const val ACTION_UP = "com.tom.rotate.ACTION_UP"
internal const val ACTION_DOWN = "com.tom.rotate.ACTION_DOWN"
internal const val ACTION_DEFAULT = "com.tom.rotate.ACTION_DEFAULT"
}
override fun onReceive(context: Context, intent: Intent) {
super.onReceive(context, intent)
val action = intent.action
val views = RemoteViews(context.packageName, R.layout.rotate_app_widget)
var replaceView: RemoteViews? = null
when (action) {
ACTION_UP -> replaceView = RemoteViews(context.packageName, R.layout.layout_rotate_up)
ACTION_DOWN -> replaceView =
RemoteViews(context.packageName, R.layout.layout_rotate_down)
ACTION_DEFAULT -> replaceView =
RemoteViews(context.packageName, R.layout.layout_rotate_default)
else -> {
}
}
if (replaceView != null) {
views.removeAllViews(R.id.replace)
views.addView(R.id.replace, replaceView)
val manager = AppWidgetManager.getInstance(context)
manager.updateAppWidget(ComponentName(context, RotateAppWidget::class.java), views)
}
}
override fun onUpdate(
context: Context,
appWidgetManager: AppWidgetManager,
appWidgetIds: IntArray
) {
for (appWidgetId in appWidgetIds) {
updateAppWidget(context, appWidgetManager, appWidgetId)
}
}
override fun onEnabled(context: Context) {
}
override fun onDisabled(context: Context) {
}
}
internal fun updateAppWidget(
context: Context, appWidgetManager: AppWidgetManager, appWidgetId: Int
) {
val downIntent = Intent(RotateAppWidget.ACTION_DOWN)
.let {
PendingIntent.getBroadcast(
context, 0, it, PendingIntent.FLAG_UPDATE_CURRENT
)
}
val upIntent = Intent(RotateAppWidget.ACTION_UP)
.let {
PendingIntent.getBroadcast(
context, 0, it, PendingIntent.FLAG_UPDATE_CURRENT
)
}
val defaultIntent = Intent(RotateAppWidget.ACTION_DEFAULT)
.let {
PendingIntent.getBroadcast(
context, 0, it, PendingIntent.FLAG_UPDATE_CURRENT
)
}
val views =
RemoteViews(context.packageName, R.layout.rotate_app_widget)
.apply {
setOnClickPendingIntent(R.id.down, downIntent)
setOnClickPendingIntent(R.id.up, upIntent)
setOnClickPendingIntent(R.id.default_state, defaultIntent)
}
appWidgetManager.updateAppWidget(appWidgetId, views)
}
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.tom.rotatewidgetdemo">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<receiver android:name=".RotateAppWidget">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
<action android:name="com.tom.rotate.ACTION_UP" />
<action android:name="com.tom.rotate.ACTION_DOWN" />
<action android:name="com.tom.rotate.ACTION_DEFAULT" />
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/rotate_app_widget_info" />
</receiver>
</application>
</manifest>
代码完事,run一下,很可能发现没有效果?为什么呢?
- 确定运行的设备的安卓版本,如果是安卓8及以上设备,确实不好使,原因后面说。
- 如果运行的是安卓8以下的设备,那么确定一下
onReceive
方法里,是不是有super.onReceive(context, intent)
,必须要有这一句,否则不起作用。
解释一下安卓8以上不好使的原因——静态广播,解决方法,使用service,用动态广播去实现就可以了,我已经在安卓10的版本上实现了。
说一下要注意的地方,widget实现动画效果主要是依靠LayoutAnimation
,用它来控制子view显示时的动画效果,所以需要在使用动画的地方,将原有的View删掉,然后将带有动画的布局添加进来,从而适用动画效果。掌握了这个方法,widget中使用动画就不再是难题了,基本可以满足设计人员的要求,至于更加炫酷的,还是使用替换图片,