Android

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目录截图.png

RotateAppWidget.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一下,很可能发现没有效果?为什么呢?

  1. 确定运行的设备的安卓版本,如果是安卓8及以上设备,确实不好使,原因后面说。
  2. 如果运行的是安卓8以下的设备,那么确定一下onReceive方法里,是不是有super.onReceive(context, intent),必须要有这一句,否则不起作用。

解释一下安卓8以上不好使的原因——静态广播,解决方法,使用service,用动态广播去实现就可以了,我已经在安卓10的版本上实现了。

说一下要注意的地方,widget实现动画效果主要是依靠LayoutAnimation,用它来控制子view显示时的动画效果,所以需要在使用动画的地方,将原有的View删掉,然后将带有动画的布局添加进来,从而适用动画效果。掌握了这个方法,widget中使用动画就不再是难题了,基本可以满足设计人员的要求,至于更加炫酷的,还是使用替换图片,

上一篇下一篇

猜你喜欢

热点阅读