熊熊出没 发表于 2024-10-10 00:09:44

RemoteView(kotlin)

使用场景:关照栏&桌面部件

自界说关照栏


[*]关照权限申请
manifest设置
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
权限动态申请
package com.example.kotlinlearn.Common;

import android.Manifest;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.provider.Settings;
import android.widget.Toast;

import androidx.activity.ComponentActivity;
import androidx.activity.result.ActivityResult;
import androidx.activity.result.ActivityResultCallback;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.core.content.ContextCompat;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

public class PermissionUtils {
    private static PermissionUtils permissionUtils;
    private String[] permissions = {
            Manifest.permission.POST_NOTIFICATIONS
    };

    private List<String> permissionList = new ArrayList<>();

    private ActivityResultLauncher<String[]> permissionLauncher;

    public static synchronized PermissionUtils getInstance() {
      if (permissionUtils == null) {
            permissionUtils = new PermissionUtils();
      }
      return permissionUtils;
    }

    private PermissionUtils() {
    }

    public void checkPermission(ComponentActivity activity) {
      permissionList.clear(); // Clear previous permission requests

      // Initialize the launcher if not already initialized
      if (permissionLauncher == null) {
            initLaunchers(activity);
      }


      for (String permission : permissions) {
            if (ContextCompat.checkSelfPermission(activity, permission) != PackageManager.PERMISSION_GRANTED) {
                permissionList.add(permission);
            }
      }
      permissionLauncher.launch(permissionList.toArray(new String));

    }

    private void initLaunchers(ComponentActivity activity) {
      // Initialize the launcher for requesting permissions
      permissionLauncher = activity.registerForActivityResult(
                new ActivityResultContracts.RequestMultiplePermissions(),
                new ActivityResultCallback<Map<String, Boolean>>() {
                  @Override
                  public void onActivityResult(Map<String, Boolean> result) {
                  }
                }
      );
    }
}

[*]实现关照
package com.example.kotlinlearn.RemoteView

import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.graphics.Color
import android.os.Build
import android.widget.RemoteViews
import androidx.core.app.NotificationCompat
import com.example.kotlinlearn.R

object NotificationUtil {
    private const val CHANNEL_ID = "my_channel_id"
    private const val CHANNEL_NAME = "My Channel"

    fun showCustomNotification(context: Context) {
      // 创建通知渠道(仅适用于 Android O 及以上版本)
      if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            val channel = NotificationChannel(
                CHANNEL_ID, CHANNEL_NAME,
                NotificationManager.IMPORTANCE_DEFAULT
            ).apply {
                enableLights(true)
                lightColor = Color.RED
                enableVibration(true)
            }
            val notificationManager = context.getSystemService(NotificationManager::class.java)
            notificationManager.createNotificationChannel(channel)
      }

      // 创建 RemoteView
      val remoteViews = RemoteViews(context.packageName, R.layout.notification_layout).apply {
            setTextViewText(R.id.notification_title, "自定义通知标题")
            setTextViewText(R.id.notification_content, "这是自定义通知内容")
      }

      // 设置点击通知的行为
      val intent = Intent(context, RemoteViewActivity::class.java)
      val pendingIntent = PendingIntent.getActivity(
            context,
            0,
            intent,
            PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
      )

      val notification = NotificationCompat.Builder(context, CHANNEL_ID)
            .setSmallIcon(R.drawable.ic_launcher_foreground)
            .setCustomContentView(remoteViews)
            .setContentIntent(pendingIntent)
            .build()

      val manager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
      manager.notify(1, notification)
    }
}

[*]activity中申请权限后直接调用就行
package com.example.kotlinlearn.RemoteView

import android.os.Bundle
import android.widget.Button
import androidx.appcompat.app.AppCompatActivity
import com.example.kotlinlearn.Common.PermissionUtils
import com.example.kotlinlearn.R


class RemoteViewActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
      super.onCreate(savedInstanceState)
      PermissionUtils.getInstance().checkPermission(this)
      setContentView(R.layout.activity_remote_view)
      var button = findViewById<Button>(R.id.button)
      button.setOnClickListener {
            NotificationUtil.showCustomNotification(this);
      }
    }
}

效果
https://i-blog.csdnimg.cn/direct/5e14d828377d481fb166e3ea9470485b.png
自界说桌面小组件


[*]界说组件的布局样式widget_layout.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:padding="16dp">

    <TextView
      android:id="@+id/widget_text"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:text="Hello Widget"
      android:textSize="18sp" />

    <Button
      android:id="@+id/widget_button"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:text="Update" />
</LinearLayout>


[*]MyWidgetProvider,必要继续自AppWidgetProvider
package com.example.kotlinlearn.RemoteView

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
import com.example.kotlinlearn.R
import java.util.Random

class MyWidgetProvider : AppWidgetProvider() {
    override fun onUpdate(
      context: Context,
      appWidgetManager: AppWidgetManager,
      appWidgetIds: IntArray
    ) {
      for (appWidgetId in appWidgetIds) {
            val views = RemoteViews(context.packageName, R.layout.widget_layout)
            val intent = Intent(context, MyWidgetProvider::class.java)
            intent.setAction(BUTTON_CLICKED)
            val pendingIntent = PendingIntent.getBroadcast(
                context,
                0,
                intent,
                PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
            )
            views.setOnClickPendingIntent(R.id.widget_button, pendingIntent)
            appWidgetManager.updateAppWidget(appWidgetId, views)
      }
    }

    override fun onReceive(context: Context, intent: Intent) {
      super.onReceive(context, intent)
      if (BUTTON_CLICKED == intent.action) {
            val appWidgetManager = AppWidgetManager.getInstance(context)
            val views = RemoteViews(context.packageName, R.layout.widget_layout)
            views.setTextViewText(R.id.widget_text, "Updated!" + Random().nextInt())
            val componentName = ComponentName(context, MyWidgetProvider::class.java)
            appWidgetManager.updateAppWidget(componentName, views)
      }
    }

    companion object {
      private const val BUTTON_CLICKED = "com.example.BUTTON_CLICKED"
    }
}


[*]在res/xml下创建组件的属性文件my_widget_info.xml,包罗大小等值
<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
    android:minWidth="125dp"
    android:minHeight="50dp"
    android:updatePeriodMillis="86400000"
    android:initialLayout="@layout/widget_layout" />


[*]在manifest中设置receiver,与activity同级
      <receiver android:name=".RemoteView.MyWidgetProvider" android:exported="true">
            <intent-filter>
                <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
            </intent-filter>

            <meta-data
                android:name="android.appwidget.provider"
                android:resource="@xml/my_widget_info" />
      </receiver>
效果
https://i-blog.csdnimg.cn/direct/dbb40443bf004c479328c4683aa8aaf4.png
https://i-blog.csdnimg.cn/direct/d2a6f56daea8488a8ff3e34f225147f5.png
点击后text会显示随机的数字。
原理


[*]可以很简单的看到,RemoteViews实现了Parcelable,所以是可序列化的,可以在进程之间传递。
https://i-blog.csdnimg.cn/direct/e9ebbbccfafc4b26a96ed1c933f0fe68.png
[*]在官网可以看到,RemoteViews只支持基础的view,不支持自界说view,支持的布局以及组件如下所示
https://i-blog.csdnimg.cn/direct/b64d60406b144755858eab1a401d4088.png
[*]从上面的代码示例中可以知道,在更改组件属性时使用的是setTextViewText,而不是findById。
https://i-blog.csdnimg.cn/direct/8e3c8c8ee3764e6998d5ed5484d4a886.png
从以上的调用链可以知道,view的设置被封装在反射对象,存在mActions中,是在接收者进行真正的设置。
可以在Action的子类中找到getMethod(view, this.methodName, param, false /* async */).invoke(view, value);,接收者正是通过反射的方式调用action中封装的view设置方法。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: RemoteView(kotlin)