Slice

简介

Slice 是一种界面模板,可以在 Google 搜索应用中以及 Google 助理中等其他位置显示自己应用中的丰富而动态的互动内容。同时可以帮助用户更快地执行任务。借助Slice,开发者可以根据应用的设计自定义 Slice 的文字、图像等。
![在这里插入图片描述](https://img-blog.csdnimg.cn/08d7318b25944e4bbf8c0c3796be0142.png

使用

首先提供Slice在build.gradle里面添加如下依赖

def slice_version = "1.1.0-alpha01"
implementation "androidx.slice:slice-core:$slice_version"
implementation "androidx.slice:slice-builders:$slice_version"

然后打开Android Studio 项目,右键点击软件包,然后依次选择 【New】–> 【Other】 --> 【Slice Provider】,如下图所示
在这里插入图片描述
这样就创建出来一个MySliceProvider的类,打开AndroidManifest.xml 变化如下
在这里插入图片描述
每个Slice都有一个关联的URI,当其他应用界面想要展示Slice时,他会通过该URI来发送绑定请求,比如上面创建出来的SliceProvider的URI为"content:/com.example.slicedemo/";然后通过SliceProvider的onBindSlice方法处理该请求,并动态构建Slice,界面随后根据情况显示Slice。新建类继承SliceProvider,并重写onBindSlice()方法,在该方法里可以编写Slice展示模块中的相关逻辑代码。打开onBindSlice 方法,代码如下:

 /**
     * Construct the Slice and bind data if available.
     */
    @SuppressLint({"ResourceType", "Slices"})
    public Slice onBindSlice(@NonNull Uri sliceUri) {
        Log.d(TAG, "onBindSlice");
        Context context = getContext();
        SliceAction activityAction = createOpenAction();
        if (context == null) {
            return null;
        }
        if (sliceUri.getPath().equals("/getSlice")) {
            return new ListBuilder(getContext(), sliceUri, ListBuilder.INFINITY)
                    .addRow(new RowBuilder()
                            .setTitle("Get Slice")
                            .addEndItem(IconCompat.createWithResource(getContext(), R.mipmap.ic_more), ListBuilder.SMALL_IMAGE)
                            .setPrimaryAction(activityAction))
                    .build();

        } else {
            // Error: Path not found.
            return new ListBuilder(getContext(), sliceUri, ListBuilder.INFINITY)
                    .addRow(new RowBuilder()
                            .setTitle("URI not found.")
                            .setPrimaryAction(activityAction)).build();
        }
    }

然后在使用slice使用方通过URI发送绑定请求,主要代码如下:

            SliceView sliceView = findViewById(R.id.slice_view);
            Uri uri = Uri.parse("content:/com.example.slicedemo/getSlice");
            Intent intent = new Intent(Intent.ACTION_VIEW, uri);
            LiveData sliceLiveData = SliceLiveData.fromIntent(this, intent);
            sliceLiveData.removeObservers(this);
            sliceLiveData.observe(this, slice -> {
                if (slice == null) {
                    return;
                }
                sliceView.setSlice(slice);
            });

与通知类似,如需处理 Slice 中的点按操作,我们可以附加在用户互动时触发的 PendingIntent 对象,比如点击Slice模块打开宿主App,我们打开MySliceProvider,然后在onBindSlice()方法中调用如下代码

//设置Action
 private SliceAction createOpenAction() {
        ComponentName cn = new ComponentName("com.hryt.hiphiplay", "com.hryt.hiphiplay.view.MainActivity");
        Intent intent = new Intent();
        intent.setComponent(cn);
        return SliceAction.create(PendingIntent.getActivity(getContext(), 0, intent,
                PendingIntent.FLAG_UPDATE_CURRENT), IconCompat.createWithResource(getContext(),
                R.mipmap.ic_more), ListBuilder.ICON_IMAGE, "open HiPhiPlay");
    }

效果如下:

测试效果

如果要对Slice进行测试,可以直接安装Google的Slice Viewer(下载链接)应用,该应用可以模拟Slice最终将如何出现在使用方应用中,下载slice-viewer.apk之后,我们在所在的目录中运行以下命令将 Slice 查看器安装到设备上;

adb install -r -t slice-viewer.apk

启动Slice Viewer之后,就可以输入Uri查看效果

Slice模板

Slice是通过ListBuilder类来创建的,在ListBuilder中,我们可以添加不同类型的行模块在应用中进行展示。

HeaderBuilder

HeaderBuilder 主要为模板设置标头,标头可以支持标题,副标题,摘要副标题

代码如下:

return new ListBuilder(getContext(), sliceUri, ListBuilder.INFINITY)
                    .setHeader(new ListBuilder.HeaderBuilder()
                            .setTitle("Header")
                            .setSubtitle("subTitle"))
                    .addRow(new RowBuilder()
                            .setTitle("Get Slice")
                            .addEndItem(IconCompat.createWithResource(getContext(), R.mipmap.ic_more), ListBuilder.SMALL_IMAGE)
                            .setPrimaryAction(activityAction))
                    .build();

效果如下:

RowBuilder

我们可以使用 RowBuilder 构造一行内容,行可支持标题,副标题,图标等,也可以设置SliceAction;

代码如下:

return new ListBuilder(getContext(), sliceUri, ListBuilder.INFINITY)
                    .setHeader(new ListBuilder.HeaderBuilder()
                            .setTitle("Header")
                            .setSubtitle("subTitle"))
                    .addRow(new RowBuilder()
                            .setTitle("Get Slice")
                            .addEndItem(IconCompat.createWithResource(getContext(), R.mipmap.ic_more), ListBuilder.SMALL_IMAGE)
                            .setPrimaryAction(activityAction))
                    .build();

效果如下:
![在这里插入图片描述](https://img-blog.csdnimg.cn/c2b2d34ad6bd4be0a7a27087692dd988.png

GridBuilder

使用 GridBuilder 构造内容网格,网格单元格是使用 CellBuilder 构造的。一个单元格最多可以支持两行文字和一张图片。

代码如下:

 return new ListBuilder(getContext(), sliceUri, ListBuilder.INFINITY)
        .setHeader(new ListBuilder.HeaderBuilder().setTitle("Title")
                .setSubtitle("subTitle"))
        .addGridRow(new GridRowBuilder()
                .addCell(new GridRowBuilder.CellBuilder()
                        .addText("网格1")
                        .setSliceAction(activityAction)
                        .addImage(IconCompat.createWithResource(getContext(), R.mipmap.demo), ListBuilder.SMALL_IMAGE))
                .addCell(new GridRowBuilder.CellBuilder()
                        .addText("网格2")
                        .setSliceAction(activityAction)
                        .addImage(IconCompat.createWithResource(getContext(), R.mipmap.demo), ListBuilder.SMALL_IMAGE))
                .setPrimaryAction(activityAction))
        .build();

效果如下:
![在这里插入图片描述](https://img-blog.csdnimg.cn/b8387b31817e40abb231da544c602da6.png

原理:

SliceProvider继承于ContentProvider,其APP间数据的传递通过ContentProvider的方式,应用APP向使用方APP提供其对应Slice的Uri,然后将数据封装成Slice对象通过Parcelable序列化的方式实现APP之间的数据传递。
在onBindSlice方法中通过ListBuider构建slice,可以添加各种模板比如RowBuilder,HeaderBuilder等,以addRow为例:

public ListBuilder addRow(@NonNull RowBuilder builder) {
     mImpl.addRow(builder);
     return this;
}

mImpl为ListBuilderImpl,接着看ListBuilderImpl的addRow方法

public void addRow(@NonNull RowBuilder builder) {
RowBuilderImpl impl =new RowBuilderImpl(createChildBuilder()); //括号这里给ListBuilderImpl创建了一个Slice.builder;
    impl.fillFrom(builder);
    ...
    addRow(impl);  //把这一行内容添加到ListBuilder中
}

再看RowBuilderImpl的fillFrom方法:

void fillFrom(RowBuilder builder) {
        if (builder.getUri() != null) {
            setBuilder(new Slice.Builder(builder.getUri()));  //给RowBuilderImpl创建了一个Slice.Buider
        }
        setPrimaryAction(builder.getPrimaryAction()); //给RowBuilderImpl设置Action
        if (builder.getLayoutDirection() != -1) {
            setLayoutDirection(builder.getLayoutDirection()); //给RowBuilderImpl设置布局方向
        }
        if (builder.getTitleAction() != null || builder.isTitleActionLoading()) {
            setTitleItem(builder.getTitleAction(),  builder.isTitleActionLoading());
        } else if (builder.getTitleIcon() != null || builder.isTitleItemLoading()) {
            setTitleItem(builder.getTitleIcon(), builder.getTitleImageMode(),
                    builder.isTitleItemLoading());
        } 
        if (builder.getTitle() != null || builder.isTitleLoading()) {  //设置TiTle
            setTitle(builder.getTitle(), builder.isTitleLoading());
        }
        if (builder.getSubtitle() != null || builder.isSubtitleLoading()) {
            setSubtitle(builder.getSubtitle(), builder.isSubtitleLoading()); //设置subTitle
        }
        if (builder.getContentDescription() != null) {
            setContentDescription(builder.getContentDescription()); //内容描述
        }
        .......
    }

这一步就是将我们在addRow时RowBuilder添加的属性设置给这个RowBuilderImpl,最后将这一行添加到ListBuilder中

@NonNull
public void addRow(@NonNull RowBuilderImpl builder) {
         ...
    getBuilder().addSubSlice(builder.build());//这里的getBuilder就是在上一步addRow中给ListBuilder创建的Slice.Builder
}

RowBuilderIml继承自TemplateBuilderImpl,所以查看TemplateBuilderImpl的build()


       public Slice build() {
           ... 
           apply(mSliceBuilder);//这里的mSliceBuilder就是fillFrom里给RowBuilderImpl创建的Slice.Builder
           return mSliceBuilder.build();
       } //看到在TemplateBuilderImpl的build()中调用了apply方法

        @Override
        public void apply(@NonNull Slice.Builder b) {
            if (mStartItem != null) {
                b.addSubSlice(mStartItem);
            }
            if (mTitleItem != null) {
                b.addItem(mTitleItem);
            }
            if (mSubtitleItem != null) {
                b.addItem(mSubtitleItem);
            }
            for (int i = 0; i < mEndItems.size(); i++) {
                Slice item = mEndItems.get(i);
                b.addSubSlice(item);
            }
            if (mContentDescr != null) {
                b.addText(mContentDescr, SUBTYPE_CONTENT_DESCRIPTION);
            }
            if (mPrimaryAction != null) {
                mPrimaryAction.setPrimaryAction(b);
            }
        }

这一步就是将RowBuilderImpl中fillFrom()的那些属性全部添加到了RowBuilderImpl的Slice.Builder中,而在Slice.builder中有一个items集合
所以RowBuiderImpl中的属性全部添加到了这个items集合中
在这里插入图片描述然后调用Slice.Builder的build方法,把Items封装到一个Slice对象中,这样就创建出了一个Slice
在这里插入图片描述
最后把这个创建出来的Slice作为一个subSlice,封装成SliceItem添加到了ListBuider的SliceBuide的item中,至此addRow过程就完成了;
在这里插入图片描述
在这里插入图片描述

最后调用ListBuilder.build,操作跟上面步骤一样,最终创建了一个Slice,里面有一个Items集合,这个items集合中包含了很多SliceItem,每一个Slice模板,例如RowBuilder,HeaderBuilder相当于一个subSlice,而这些subSlice里面又包含了很多属性,将这些SubSlice最终添加到了这个items中,封装成了一个最终的Slice返回给了使用方;
在这里插入图片描述
而在使用方通过SliceLiveData.fromIntent(this, intent)方法底层会通过ContentProvider去发起绑定请求,并且把返回的Slice通过LiveData发送数据
在这里插入图片描述
在这里插入图片描述在这里插入图片描述
绑定成功之后就会拿到提供方给的Slice,将接收到的slice设置给SliceView,在SliceView中会拿到对应Slice中item中对应的属性,然后绑定UI;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值