RemoteViews讲解

RemoteViews在日常开发中主要用于通知栏和桌面小部件的开发。它继承了Parcelable,所以可以很好的通过Binder来进行跨进程通信,里面提供了一系列的方法用于跨进程的更新界面。RemoteViews一个最常用的构造方法是RemoteViews(String packageName,int layoutId),第一个参数是包名,第二个参数是待加载的布局文件。RemoteViews支持的控件如下:

Layout:
FrameLayout、LinearLayout、RelativeLayout、GridLayout
View
TextView 、Button、ImageView、ImageButton、ProgressBar、ListView、GridView、ViewFlipper、AdapterViewFlipper 、ViewStub、StackView、AnalogClock、Chronometer

RemoteViews仅仅支持上面的这些控件,不支持自定义View和其他类型的View。

RemoteViews的简单用法

RemoteViews提供了一系列的set方法来跨进程更新View,常见的set方法如下:

setViewVisibility(int viewId, int visibility) 设置某个View是否可见

setTextViewText(int viewId, CharSequence text) 设置TextView文本

setTextViewTextSize(int viewId, int units, float size);设置TextView字体大小

setImageViewUri(int viewId, Uri uri);设置ImageView 图片相当于调用ImageView的setImageURI方法

setImageViewBitmap(int viewId, Bitmap bitmap);设置ImageView图片

setOnClickPendingIntent(int viewId, PendingIntent pendingIntent)为View添加点击事件

setBoolean(int viewId, String methodName, boolean value)反射调用View对象参数为boolean的方法

setString(int viewId, String methodName, String value)反射调用View对象参数为String类型的方法

RemoteViews类中还有很多类似的方法,可以去参考android api。这一系列的set方法大多数都是通过反射来实现的。举个关于自定义通知栏的梨子:

     NotificationCompat.Builder builder8 = new NotificationCompat.Builder(this);

                builder8.setSmallIcon(R.mipmap.me_pressed);
                builder8.setDefaults(NotificationCompat.DEFAULT_ALL);
                builder8.setAutoCancel(true);
                Intent intent_8 = new Intent(this, NotificationActivity.class);
                PendingIntent pendingIntent_8 = PendingIntent.getActivity(this, 0, intent_8, PendingIntent.FLAG_UPDATE_CURRENT);
                builder8.setContentIntent(pendingIntent_8);
                RemoteViews remoteViews = new RemoteViews(getPackageName(), R.layout.layout_remoteviews);
                remoteViews.setImageViewResource(R.id.iv_image, R.mipmap.test_widget_1);
                remoteViews.setTextViewText(R.id.tv_content, "测试内容");
                remoteViews.setTextViewText(R.id.tv_title, "测试标题");
                remoteViews.setTextColor(R.id.tv_content, getResources().getColor(R.color.colorAccent));
                Intent intent8 = new Intent(this, APPWidgetActivity.class);
                PendingIntent pendingIntent8 = PendingIntent.getActivity(this, 0, intent8, PendingIntent.FLAG_UPDATE_CURRENT);
                remoteViews.setOnClickPendingIntent(R.id.tv_content, pendingIntent8);

                builder8.setContent(remoteViews);
                if (notificationManager == null) {
                    notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
                }
                notificationManager.notify(116, builder8.build());

效果图如下:
简单梨子

RemoteViews内部机制

在通知栏或者桌面小部件中,RemoteViews会通过Binder机制和系统进程进行通信,系统会根据RemoteViews中的包名去获取该应用的资源(布局文件,图片等资源)。然后通过LayoutInflater去加载RemoteViews中的布局文件。这时在系统进程中就是一个普通的View了。在应用中我们可以通过一系列的set方法去更新View。这些方法不是立刻去执行的。RemoteViews会把这些操作保存起来。当通过NotificationManager的notify方法或者AppWidgetManager的updateAppWidget方法提交时,这些操作会通过Binder传输到系统进程来进行更新View。在我们的应用中每调用一个set方法,RemoteViews中就会添加一个对应的Action对象,当我们通过NotificationManager和AppWidgetManager提交更新时,这些Action对象就会传输到系统进程中依次执行。通过阅读RemoteViews源码可以发现。远程进程通过RemoteViews的apply方法更新操作。在apply方法里会去遍历一个个Action对象并调用Action的apply方法。所以具体的更新操作是Action的apply方法完成的。除了apply方法还有一个reapply方法可以用来更新界面。他们的区别是apply方法会加载界面并更新界面,而reapply方法只会进行更新界面。

  /**
     * Equivalent to calling TextView.setText
     *
     * @param viewId The id of the view whose text should change
     * @param text The new text for the view
     */
    public void setTextViewText(int viewId, CharSequence text) {
        setCharSequence(viewId, "setText", text);
    }

  /**
     * Call a method taking one CharSequence on a view in the layout for this RemoteViews.
     *
     * @param viewId The id of the view on which to call the method.
     * @param methodName The name of the method to call.
     * @param value The value to pass to the method.
     */
    public void setCharSequence(int viewId, String methodName, CharSequence value) {
        addAction(new ReflectionAction(viewId, methodName, ReflectionAction.CHAR_SEQUENCE, value));
    }
  /**
  * ReflectionAction的apply方法
  */
    @Override
        public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
            final View view = root.findViewById(viewId);
            if (view == null) return;

            Class<?> param = getParameterType();
            if (param == null) {
                throw new ActionException("bad type: " + this.type);
            }

            try {
                getMethod(view, this.methodName, param).invoke(view, wrapArg(this.value));
            } catch (ActionException e) {
                throw e;
            } catch (Exception ex) {
                throw new ActionException(ex);
            }
        }

/**
*RemoteViews的apply方法
*
*/
 public View apply(Context context, ViewGroup parent, OnClickHandler handler) {
        RemoteViews rvToApply = getRemoteViewsToApply(context);

        View result = inflateView(context, rvToApply, parent);
        loadTransitionOverride(context, handler);

        rvToApply.performApply(result, parent, handler);

        return result;
    }

   private void performApply(View v, ViewGroup parent, OnClickHandler handler) {
        if (mActions != null) {
            handler = handler == null ? DEFAULT_ON_CLICK_HANDLER : handler;
            final int count = mActions.size();
            for (int i = 0; i < count; i++) {
                Action a = mActions.get(i);
                a.apply(v, parent, handler);
            }
        }
    }
/**
* RemoteViews的reapply方法
*
*/
    public void reapply(Context context, View v, OnClickHandler handler) {
        RemoteViews rvToApply = getRemoteViewsToApply(context);

        // In the case that a view has this RemoteViews applied in one orientation, is persisted
        // across orientation change, and has the RemoteViews re-applied in the new orientation,
        // we throw an exception, since the layouts may be completely unrelated.
        if (hasLandscapeAndPortraitLayouts()) {
            if ((Integer) v.getTag(R.id.widget_frame) != rvToApply.getLayoutId()) {
                throw new RuntimeException("Attempting to re-apply RemoteViews to a view that" +
                        " that does not share the same root layout id.");
            }
        }

        rvToApply.performApply(v, (ViewGroup) v.getParent(), handler);
    }

总结

1.参考自《Android开发艺术探索》这本书。
2.关于窗口小部件的用法http://blog.csdn.net/liuqz2009/article/details/50922122

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值