Android实现桌面小部件:今天吃什么

今天吃什么桌面小部件 是专为解决日常选择困难症而设计的安卓小部件,它通过轻松愉快的滚动机制,帮助玩家在诸如"今天吃什么"这样的日常琐事中迅速做出决定。

不知道吃什么?别担心,点我帮你选择!

创建各个UI背景

app_widget_background.xml

XML 复制代码
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
    <corners android:radius="?attr/appWidgetRadius" />
    <solid android:color="#BF000000" />
    <gradient android:startColor="#66CA740B" android:endColor="#99EACB10" android:angle="80"/>
</shape>

shape_eat_background.xml

XML 复制代码
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
    <size
        android:width="110dp"
        android:height="50dp" />
    <corners android:radius="5dp" />
    <solid android:color="#FFD154" />
    <stroke
        android:width="5dp"
        android:color="#FFD154" />
</shape>

shape_eat_foreground.xml

XML 复制代码
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
    <size
        android:width="110dp"
        android:height="50dp" />
    <corners android:radius="5dp" />
    <stroke
        android:width="5dp"
        android:color="#FFE58A" />
</shape>

shape_start_btn.xml

XML 复制代码
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
    <size android:width="110dp" android:height="50dp"/>
    <solid android:color="#E63A75FF"/>
    <corners android:radius="10dp"/>
    <gradient android:startColor="#fd7147" android:endColor="#FFB39C" android:angle="50"/>
    <stroke android:color="#33FFFFFF" android:width="12dp"/>
</shape>

创建小部件布局layout_eat_app_widget.xml

XML 复制代码
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:tools="http://schemas.android.com/tools"
    tools:layout_height="150dp"
    tools:layout_width="150dp"
    android:orientation="vertical">

    <RelativeLayout
        style="@style/Widget.TodayEatWhat.AppWidget.Container"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/Theme.TodayEatWhat.AppWidgetContainer">

        <ImageView
            android:id="@+id/iv_background"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:adjustViewBounds="true"
            android:contentDescription="@null"
            android:scaleType="centerCrop"
            android:src="@drawable/app_widget_background" />

        <RelativeLayout
            style="@style/Widget.TodayEatWhat.AppWidget.ContainerInnerView"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_alignTop="@id/iv_background"
            android:layout_alignBottom="@id/iv_background">

            <ImageView
                android:layout_width="wrap_content"
                android:layout_height="50dp"
                android:layout_alignStart="@id/fl_container"
                android:layout_alignTop="@id/fl_container"
                android:layout_alignEnd="@id/fl_container"
                android:layout_marginTop="5dp"
                android:background="@drawable/shape_eat_foreground"
                android:backgroundTint="@color/white"
                android:contentDescription="@null" />

            <FrameLayout
                android:id="@+id/fl_container"
                android:layout_width="match_parent"
                android:layout_height="50dp"
                android:background="@drawable/shape_eat_background" />

            <ImageView
                android:layout_width="wrap_content"
                android:layout_height="50dp"
                android:layout_alignStart="@id/fl_container"
                android:layout_alignTop="@id/fl_container"
                android:layout_alignEnd="@id/fl_container"
                android:background="@drawable/shape_eat_foreground"
                android:contentDescription="@null" />

            <TextView
                android:id="@+id/tv_last_food"
                android:layout_width="match_parent"
                android:layout_height="50dp"
                android:gravity="center"
                android:shadowColor="#FFE58A"
                android:shadowDy="1"
                android:shadowRadius="1"
                android:text="吃点什么?"
                android:textColor="@color/white"
                android:textSize="14sp" />

            <TextView
                android:id="@+id/go_btn"
                android:layout_width="match_parent"
                android:layout_height="50dp"
                android:layout_alignParentBottom="true"
                android:background="@drawable/shape_start_btn"
                android:gravity="center"
                android:text="吃点别的"
                android:textColor="@color/white"
                android:textSize="13sp"
                android:textStyle="bold|italic" />
        </RelativeLayout>
    </RelativeLayout>

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:layout_margin="8dp"
        android:text="@string/app_widget_label"
        android:textColor="@color/white"
        android:textSize="12sp" />
</LinearLayout>

创建layoutAnimation动画widget_food_animation.xml

XML 复制代码
<?xml version="1.0" encoding="utf-8"?>
<layoutAnimation xmlns:android="http://schemas.android.com/apk/res/android"
    android:animation="@anim/widget_food_animation"
    android:interpolator="@android:anim/overshoot_interpolator" />

创建set>translate动画widget_food_animation.xml

XML 复制代码
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:fillAfter="true"
    android:interpolator="@android:anim/overshoot_interpolator">
    <translate
        android:duration="800"
        android:fromYDelta="0%"
        android:toYDelta="-900%p" />
</set>

创建动画布局layout_eat_app_widget_textview.xml

XML 复制代码
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/layContent"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layoutAnimation="@anim/widget_food_animation_controller"
    android:orientation="vertical">

    <TextView
        android:id="@+id/text1"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:gravity="center"
        android:textColor="@color/white"
        android:shadowColor="#FFE58A"
        android:shadowDy="1"
        android:shadowRadius="1"
        android:textSize="14sp" />

    <TextView
        android:id="@+id/text2"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:gravity="center"
        android:textColor="@color/white"
        android:shadowColor="#FFE58A"
        android:shadowDy="1"
        android:shadowRadius="1"
        android:textSize="14sp" />

    <TextView
        android:id="@+id/text3"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:gravity="center"
        android:textColor="@color/white"
        android:shadowColor="#FFE58A"
        android:shadowDy="1"
        android:shadowRadius="1"
        android:textSize="14sp" />

    <TextView
        android:id="@+id/text4"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:gravity="center"
        android:textColor="@color/white"
        android:shadowColor="#FFE58A"
        android:shadowDy="1"
        android:shadowRadius="1"
        android:textSize="14sp" />

    <TextView
        android:id="@+id/text5"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:gravity="center"
        android:textColor="@color/white"
        android:shadowColor="#FFE58A"
        android:shadowDy="1"
        android:shadowRadius="1"
        android:textSize="14sp" />

    <TextView
        android:id="@+id/text6"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:gravity="center"
        android:textColor="@color/white"
        android:shadowColor="#FFE58A"
        android:shadowDy="1"
        android:shadowRadius="1"
        android:textSize="14sp" />

    <TextView
        android:id="@+id/text7"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:gravity="center"
        android:textColor="@color/white"
        android:shadowColor="#FFE58A"
        android:shadowDy="1"
        android:shadowRadius="1"
        android:textSize="14sp" />

    <TextView
        android:id="@+id/text8"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:gravity="center"
        android:textColor="@color/white"
        android:shadowColor="#FFE58A"
        android:shadowDy="1"
        android:shadowRadius="1"
        android:textSize="14sp" />

    <TextView
        android:id="@+id/text9"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:gravity="center"
        android:textColor="@color/white"
        android:shadowColor="#FFE58A"
        android:shadowDy="1"
        android:shadowRadius="1"
        android:textSize="14sp" />

    <TextView
        android:id="@+id/text10"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:gravity="center"
        android:textColor="@color/white"
        android:shadowColor="#FFE58A"
        android:shadowDy="1"
        android:shadowRadius="1"
        android:textSize="14sp" />

    <TextView
        android:id="@+id/text11"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:gravity="center"
        android:textColor="@color/white"
        android:shadowColor="#FFE58A"
        android:shadowDy="1"
        android:shadowRadius="1"
        android:textSize="14sp" />

</LinearLayout>

创建EatAppWidget.kt 继承 AppWidgetProvider

Kotlin 复制代码
package com.actionbar.todayeatwhat

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.os.Bundle
import android.util.Log
import android.widget.RemoteViews
import java.util.*

/**
 * Implementation of App Widget functionality.
 */
class EatAppWidget : AppWidgetProvider() {
    companion object {
        private const val TAG = "EatAppWidget"
        private const val ACTION_EAT_WHAT_UPDATE =
            "com.actionbar.todayeatwhat.action.EAT_WHAT_UPDATE"
        private val foodList = arrayListOf(
            "螺蛳粉",
            "酸菜鱼",
            "黄焖鸡米饭",
            "麻辣烫",
            "新疆炒米粉",
            "花甲米线",
            "酸辣粉",
            "鸭血粉丝汤",
            "兰州拉面",
            "炸酱面",
            "南昌拌粉",
            "云吞面",
            "麻辣香锅",
            "蛋炒饭",
            "沙县小吃",
            "卤肉饭",
            "炸鸡汉堡",
            "水饺馄饨",
            "烧烤烤串",
            "烤冷面",
            "北京烤鸭",
            "蒸饺小笼包",
            "披萨",
            "串串香",
            "钵钵鸡",
            "意大利面",
            "寿司饭团",
            "拉面",
            "煎饼",
            "手抓饼",
            "肉夹馍",
            "过桥米线",
            "慕斯蛋糕",
            "羊肉串",
            "烤鱼",
            "墨西哥卷饼",
            "蒜蓉扇贝",
            "小龙虾",
            "剁椒鱼头",
            "盖浇饭",
            "凉面凉皮",
            "手撕兔",
            "豆腐脑",
            "土豆泥",
            "糯米饭",
            "臭豆腐",
            "大盘鸡",
            "辣卤卤菜",
            "曹氏鸭脖",
            "轻食沙拉",
            "湖南米粉",
            "烤苕皮豆干",
            "章鱼小丸子",
            "吐司三明治",
            "春卷",
            "铁板烤鱿鱼",
            "牛排",
            "韩式部队锅",
            "烤肠热狗",
            "水煮肉片",
            "生煎包",
            "锅包肉",
            "糖醋排骨",
            "金汤肥牛",
            "叉烧饭",
            "广东肠粉",
            "热干面",
            "潮汕牛肉火锅",
            "豉汁蒸排骨",
            "酥皮蛋挞",
            "麻辣脆皮鸭",
            "福鼎肉片",
            "东北大拉皮",
            "小炒黄牛肉",
            "羊肉泡馍",
            "油泼面",
            "酱香饼",
            "铁板烧",
            "甜不辣",
            "沙茶面",
            "牛肉炒河粉",
            "台湾炸鸡排",
            "鲜肉蛋堡",
            "捞汁海鲜",
            "冬阴功海鲜",
            "猪扒包",
            "里脊夹饼",
            "菠萝海鲜炒饭",
            "油炸串串",
            "蛋烘糕",
            "糯米鸡",
            "酸汤鱼",
            "鸡汤饭",
            "梅菜扣肉饼",
            "油炸串串",
            "口水鸡",
            "椒麻鸡",
            "烤肉",
            "柴火鸡",
            "葱油饼",
            "辣炒年糕",
            "驴肉火烧",
            "麻辣火锅",
            "重庆小面",
            "烤鳗鱼饭",
            "盐水鸭",
            "烧腊",
            "猪脚面"
        )
    }

    private var lastFood: String = "吃点什么?"

    override fun onReceive(context: Context?, intent: Intent?) {
        super.onReceive(context, intent)
        Log.d(TAG, "onReceive() called with: context = $context, intent = $intent")
        context ?: return
        val action = intent?.action ?: return
        when (ACTION_EAT_WHAT_UPDATE) {
            action -> {
                val componentName = ComponentName(context, EatAppWidget::class.java)
                val appWidgetManager = AppWidgetManager.getInstance(context)
                val appWidgetIds = appWidgetManager.getAppWidgetIds(componentName)
                val views = RemoteViews(context.packageName, R.layout.layout_eat_app_widget)
                doAnimation(context, views, foodList.shuffled())
                appWidgetManager.partiallyUpdateAppWidget(appWidgetIds, views)
            }
        }
    }

    override fun onUpdate(
        context: Context,
        appWidgetManager: AppWidgetManager,
        appWidgetIds: IntArray
    ) {
        Log.d(
            TAG,
            "onUpdate() called with: context = $context, appWidgetManager = $appWidgetManager, appWidgetIds = $appWidgetIds"
        )
        for (appWidgetId in appWidgetIds) {
            updateAppWidget(context, appWidgetManager, appWidgetId)
        }
    }

    override fun onEnabled(context: Context) {
        Log.d(TAG, "onEnabled() called with: context = $context")
    }

    override fun onDisabled(context: Context) {
        Log.d(TAG, "onDisabled() called with: context = $context")
    }

    override fun onAppWidgetOptionsChanged(
        context: Context?,
        appWidgetManager: AppWidgetManager?,
        appWidgetId: Int,
        newOptions: Bundle?
    ) {
        super.onAppWidgetOptionsChanged(context, appWidgetManager, appWidgetId, newOptions)
        Log.d(
            TAG,
            "onAppWidgetOptionsChanged() called with: context = $context, appWidgetManager = $appWidgetManager, appWidgetId = $appWidgetId, newOptions = $newOptions"
        )
    }

    private fun updateAppWidget(
        context: Context,
        appWidgetManager: AppWidgetManager,
        appWidgetId: Int
    ) {
        val views = RemoteViews(context.packageName, R.layout.layout_eat_app_widget)
        views.removeAllViews(R.id.fl_container)
        views.setTextViewText(R.id.tv_last_food, lastFood)
        val pendingIntent = PendingIntent.getBroadcast(
            context,
            UUID.randomUUID().hashCode(),
            Intent(context, EatAppWidget::class.java).setAction(ACTION_EAT_WHAT_UPDATE),
            PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
        )
        views.setOnClickPendingIntent(R.id.go_btn, pendingIntent)
        appWidgetManager.updateAppWidget(appWidgetId, views)
    }

    private fun doAnimation(context: Context, remoteViews: RemoteViews, shuffled: List<String>) {
        // 根据layout_eat_app_widget_textview布局里面的TextView的数量来取
        val foodList = shuffled.subList(0, 11)
        remoteViews.removeAllViews(R.id.fl_container)
        // 清空
        lastFood = ""
        remoteViews.setTextViewText(R.id.tv_last_food, lastFood)
        // 下标9是动画结束后显示的结果
        lastFood = foodList[9]
        val views = RemoteViews(context.packageName, R.layout.layout_eat_app_widget_textview)
        foodList.forEachIndexed { index: Int, food: String ->
            val id = context.resources.getIdentifier("text${index + 1}", "id", context.packageName)
            if (id > 0) {
                // 如果是第一条的话,将下标9的内容显示在第一条,防止用户长按小部件拖动时会显示第一条数据的问题
                if (index == 0) {
//                    views.setTextViewText(id, lastFood+index.toString())
                    views.setTextViewText(id, lastFood)
                } else {
//                    views.setTextViewText(id, food+index.toString())
                    views.setTextViewText(id, food)
                }
            }
        }
        remoteViews.addView(R.id.fl_container, views)
    }
}

strings.xml

XML 复制代码
<resources>
    <string name="app_name">TodayEatWhat</string>
    <string name="appwidget_text">EXAMPLE</string>
    <string name="app_widget_label">今天吃什么</string>
    <string name="app_widget_description">不知道吃什么?别担心,点我帮你选择!</string>
</resources>

res/xml资源目录中创建eat_app_widget_info.xml

XML 复制代码
<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:description="@string/app_widget_description"
    android:initialKeyguardLayout="@layout/layout_eat_app_widget"
    android:initialLayout="@layout/layout_eat_app_widget"
    android:minWidth="110dp"
    android:minHeight="110dp"
    android:previewImage="@drawable/eat_appwidget_preview"

    android:previewLayout="@layout/layout_eat_app_widget"
    android:targetCellWidth="2"
    android:targetCellHeight="2"
    android:updatePeriodMillis="1800000"
    android:widgetCategory="home_screen"
    tools:targetApi="s" />

配置AndroidManifest.xml

XML 复制代码
<receiver
    android:name=".EatAppWidget"
    android:enabled="true"
    android:label="@string/app_widget_label"
    android:description="@string/app_widget_description"
    android:exported="false">
    <intent-filter>
        <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
        <action android:name="${applicationId}.action.EAT_WHAT_UPDATE" />
    </intent-filter>

    <meta-data
        android:name="android.appwidget.provider"
        android:resource="@xml/eat_app_widget_info" />
</receiver>

在themes.xml中加入

XML 复制代码
<style name="Theme.TodayEatWhat.AppWidgetContainerParent" parent="@android:style/Theme.DeviceDefault">
    <!-- 用于制作圆角的小部件外部边界的半径 -->
    <item name="appWidgetRadius">16dp</item>
    <!--
    内部视图的小部件边界的半径,以形成圆角。它需要是8dp或小于appWidgetRadius的值
    -->
    <item name="appWidgetInnerRadius">8dp</item>
</style>

<style name="Theme.TodayEatWhat.AppWidgetContainer" parent="Theme.TodayEatWhat.AppWidgetContainerParent">
    <!-- 应用填充以避免小部件的内容与圆角碰撞 -->
    <item name="appWidgetPadding">10dp</item>
</style>

在styles.xml中加入

XML 复制代码
<style name="Widget.TodayEatWhat.AppWidget.Container" parent="android:Widget">
    <item name="android:id">@android:id/background</item>
    <item name="android:background">@drawable/app_widget_background</item>
</style>

<style name="Widget.TodayEatWhat.AppWidget.ContainerInnerView" parent="android:Widget">
    <item name="android:padding">?attr/appWidgetPadding</item>
</style>
相关推荐
清风徐来辽1 小时前
Kotlin学习:1.7.语言基础之空安全
开发语言·kotlin
红米饭配南瓜汤1 小时前
Android显示系统(04)- OpenGL ES - Shader绘制三角形
android·音视频·媒体
码农老张Zy1 小时前
【PHP小课堂】学习PHP中的变量处理相关操作
android·开发语言·学习·php
尹中文1 小时前
Android ConstraintLayout 约束布局的使用手册
android
苗壮.1 小时前
Android 俩个主题的不同之处 “Theme.AppCompat vs android:Theme.Material.Light.NoActionBar”
android·gitee·appcompat
努力进修2 小时前
【Java-数据结构篇】Java 中栈和队列:构建程序逻辑的关键数据结构基石
android·java·数据结构
闲暇部落4 小时前
OpenGL ES详解——文字渲染
android·freetype·文字渲染·位图字体
画个太阳作晴天9 小时前
Android10 设备死机的问题分析和解决
android·framework·anr
SHUIPING_YANG11 小时前
Typora设置自动上传图片到图床
android
Ai 编码助手11 小时前
php多进程那点事,用 swoole 如何去解决呢
android·php·swoole