RemoteViews的作用和工作原理

本文详细介绍了Android中的RemoteViews组件,包括其基本概念、应用场景(如自定义通知栏和桌面小部件)、内部机制及如何通过一系列set方法更新View界面。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

RemoteViews的基本概念

什么是RemoteViews呢?RemoteViews是一种远程View,它提供了一组基础的操作用于跨进程更新页面。RemoteViews主要有两个应用场景:自定义通知栏和桌面小部件。

1 自定义通知栏使用

首先我们看系统默认的通知栏的是怎么实现的:

    Notification.Builder builder;
    Intent intent;
    NotificationManager manager;
    Notification notification;
  builder=new Notification.Builder(this);
                builder.setSmallIcon(R.mipmap.ic_launcher);
                builder.setTicker("hello world");
                builder.setContentText("测试");
                builder.setWhen(System.currentTimeMillis());
                 intent=new Intent(this,DemoActivity_2.class);
                PendingIntent pendingIntent=PendingIntent.getActivity(this,0,intent,PendingIntent.FLAG_UPDATE_CURRENT);
                builder.setContentIntent(pendingIntent);

                 manager=(NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
                 notification=builder.build();

                manager.notify(sId,notification);

运行效果如下:

点击会打开一个DemoActivity_2的activity。

在正常开发中我们需要对通知栏显示样式进行自定义,这时候就要用到RemoteViews,下面看RemoteViews自定义通知栏:

    Notification.Builder builder;
    Intent intent;
    NotificationManager manager;
    Notification notification;
  builder=new Notification.Builder(this);
                builder.setSmallIcon(R.mipmap.ic_launcher);
                builder.setTicker("hello world");
                builder.setWhen(System.currentTimeMillis());

                 intent=new Intent(this,DemoActivity_1.class);
                intent.putExtra("sid", "" + sId);
                PendingIntent pendingIntent2=PendingIntent.getActivity(this,0,intent,PendingIntent.FLAG_UPDATE_CURRENT);
                System.out.println(pendingIntent2);
                RemoteViews remoteViews1=new RemoteViews(getPackageName(),R.layout.layout_notification);
                remoteViews1.setTextViewText(R.id.msg,"zhe shi xinxi de "+sId);
                remoteViews1.setImageViewResource(R.id.icon,R.mipmap.ic_launcher);
                PendingIntent pendingIntent1=PendingIntent.getActivity(this,0,new Intent(this,DemoActivity_2.class),PendingIntent.FLAG_UPDATE_CURRENT);

                remoteViews1.setOnClickPendingIntent(R.id.open_activity2,pendingIntent1);

                builder.setContent(remoteViews1);
                builder.setContentIntent(pendingIntent2);
                 manager=(NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
                notification=builder.build();
                manager.notify(sId,notification);

layout_notification.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:background="@color/light_green"
    android:padding="10dp"
    android:gravity="center"
    android:orientation="horizontal" >


    <ImageView
        android:id="@+id/icon"
        android:background="#000000"
        android:layout_width="40dp"
        android:scaleType="centerInside"
        android:layout_height="40dp" />

    <LinearLayout
        android:id="@+id/layout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="right"
        android:orientation="vertical" >

        <TextView
            android:id="@+id/msg"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginBottom="5dp"
            android:textColor="@android:color/white"
            android:visibility="visible"
            android:text="TextView" />

        <TextView
            android:id="@+id/open_activity2"
            android:padding="2dp"
            android:background="@drawable/common_bkg"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textColor="@android:color/white"
            android:text="open DemoActivity_2" />

    </LinearLayout>

</LinearLayout>

运行效果如下:

RemoteViews使用很简单,只需要提供当前应用的包名和布局文件的资源ID就可以创建一个RemoteViews对象。在更新RemoteViews时,由于无法直接访问到RemoteViews里面的View,因为通知栏试运行在SystemServer进程中,所以如果需要更新RemoteViews必须要使用RemoteViews提供的一系列方法来更新View。具体的方法在下面会讲到。

2 桌面小部件的实现

Android提供AppWidgetProvider用于桌面小部件的实现,其本质就是一个广播。下面是一个具体的实现步骤:

第一、定义小部件页面:

widget.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">
<ImageView
    android:layout_width="wrap_content"
    android:id="@+id/imageView1"
    android:src="@mipmap/ic_launcher"
    android:layout_height="wrap_content" />

</LinearLayout>

里面放了一个小图片。

第二、定义小部件配置信息

配置信息放在res/xml下面:

<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
    android:initialLayout="@layout/widget"
     android:minHeight="84dp"

    android:minWidth="84dp"
    android:minResizeHeight="84dp"
    android:minResizeWidth="85dp"
    android:updatePeriodMillis="86400000"
    >

</appwidget-provider>

配置信息主要作用:1.initialLayout用于绑定对应的需要初始化的布局;2.minHeight minWidth用于定义小部件的最小尺寸;3.updatePeriodMillis定义小部件自动更新的周期;4.minResizeWidth 和 minResizeHeight指定了 widget 的最小绝对尺寸。也就是说,如果 widget 小于该尺寸,便会因为变得模糊、看不清或不可用。 使用这两个属性,可以允许用户重新调整 widget 的大小,使 widget 的大小可以小于 minWidth 和 minHeight。

3 定义小部件的实现类:

public class MyAppWidgetProvider extends AppWidgetProvider {
    public static final String TAG="MyAppWidgetProvider";
    public static final  String CLICK_ACTION="com.my.learn.code.remoteviews.CLOCK";
    public MyAppWidgetProvider(){
        super();
    }

    /**
     * 这是广播的内置方法,用于犯法具体的事件给其他方法
     * @param context
     * @param intent
     */
    @Override
    public void onReceive(final Context context, final Intent intent) {
        super.onReceive(context, intent);
        Log.v(TAG,"onReceive :action="+intent.getAction());

        //这里判断是自己的ACTION,做自己的事情,比如小部件被点击后要做什么,这里是一个动画效果
        if (intent.getAction().equals(CLICK_ACTION)){
            Toast.makeText(context,"clicked it",Toast.LENGTH_LONG).show();
            new Thread(new Runnable() {
                @Override
                public void run() {
                    Bitmap srcbBitmap= BitmapFactory.decodeResource(context.getResources(), R.mipmap.ic_launcher);
                    AppWidgetManager appWidgetManager=AppWidgetManager.getInstance(context);
                    for (int i=0;i<37;i++){
                        float degree=(i *10)%360;
                        RemoteViews remoteViews=new RemoteViews(context.getPackageName(),R.layout.widget);

                        remoteViews.setImageViewBitmap(R.id.imageView1,rotateBitmap(context,srcbBitmap,degree));

                        Intent intentClick=new Intent();
                        intentClick.setAction(CLICK_ACTION);

                        PendingIntent pendingIntent=PendingIntent.getBroadcast(context,0,intentClick,0);
                        remoteViews.setOnClickPendingIntent(R.id.imageView1,pendingIntent);
                        appWidgetManager.updateAppWidget(new ComponentName(context,MyAppWidgetProvider.class),remoteViews);

                        SystemClock.sleep(30);

                    }

                }
            }).start();
        }
    }

    private Bitmap rotateBitmap(Context context,Bitmap srcbBitmap,float degree){
        Matrix matrix=new Matrix();
        matrix.reset();
        matrix.setRotate(degree);
        Bitmap tmpBitmap=Bitmap.createBitmap(srcbBitmap,0,0,srcbBitmap.getWidth(),srcbBitmap.getHeight(),matrix,true);
        return tmpBitmap;
    }

    /**
     * 桌面小部件更新
     * @param context
     * @param appWidgetManager
     * @param appWidgetId
     */
    private void onWIdgetUpdate(Context context,AppWidgetManager appWidgetManager,int appWidgetId){
        Log.v(TAG,"appWidgetId="+appWidgetId);
        RemoteViews remoteViews=new RemoteViews(context.getPackageName(),R.layout.widget);

        //桌面小部件单击事件发送的Intent广播
        Intent intentClick=new Intent();
        intentClick.setAction(CLICK_ACTION);
        PendingIntent pendingIntent=PendingIntent.getBroadcast(context,0,intentClick,0);
        remoteViews.setOnClickPendingIntent(R.id.imageView1,pendingIntent);
        appWidgetManager.updateAppWidget(appWidgetId,remoteViews);
    }

    /**
     * 每次桌面小部件更新时都调用一次该方法
     *zhexie lianpifu doumeiyoud e huo
     *
     * 小部件的更新时机由updatePeriodMillis来指定,每个周期小部件都会自动更新一次
     * @param context
     * @param appWidgetManager
     * @param appWidgetIds
     */
    @Override
    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
        super.onUpdate(context, appWidgetManager, appWidgetIds);
        Log.v(TAG, "onUpdate");
        final int counter=appWidgetIds.length;
        Log.v(TAG, "counter = "+counter);
        for (int i =0;i<counter;i++ ){
            int appWidgetId=appWidgetIds[i];
            onWIdgetUpdate(context,appWidgetManager,appWidgetId);
        }
    }

    /**
     * 当该窗口小部件第一次添加到桌面时调用该方法,可添加多次但只在第一次调用
     * @param context
     */
    @Override
    public void onEnabled(Context context) {
        super.onEnabled(context);
    }

    /**
     * 没删除一次桌面小部件就调用一次
     * @param context
     * @param appWidgetIds
     */
    @Override
    public void onDeleted(Context context, int[] appWidgetIds) {
        super.onDeleted(context, appWidgetIds);
    }


    /**
     * 当最后一个该类型的桌面小部件被删除时调用该方法,注意是最后一个
     * @param context
     */
    @Override
    public void onDisabled(Context context) {
        super.onDisabled(context);
    }
}

4 在AndroidManifest.xml中声明小部件

        <!--声明小部件-->
        <receiver android:name="com.my.learn.code.remoteviews.MyAppWidgetProvider">
            <meta-data
                android:name="android.appwidget.provider"
                android:resource="@xml/appwidget_provider_info"></meta-data>
            <intent-filter

                >
                <!--用于识别小部件的单击行为-->
                <action android:name="com.my.learn.code.remoteviews.CLOCK" />
                <!--作为小部件的标识必须存在-->
                <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />

            </intent-filter>

        </receiver>

RemoteViews的内部机制

RemoteViews作用是在其他进程中显示并更新View界面,RemoteViews构造方法:  public RemoteViews(String packageName, int layoutId),需要两个参数:当前应用包名以及待加载的布局文件。目前RemoteViews并不能支持所有View类型,支持的View类型如下:

Layout

FrameLayout、LinearLayout、RelativeLayout、GridLayout

View

Button、ImageButton、ImageView、TextView、ListView、GridView、AnalogClock、Chronometer、ProgressBar、ViewFlipper、StackView、AdapterViewFlipper、ViewStub。

更新RemoteViews必须使用RemoteViews提供的一系列的set方法:

最后用一张图来说明RemoteViews的内部机制

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

倚栏静望

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值