当前位置:首页 > 谈天说地

Android中ShapeableImageView使用实例详解(告别shape、三方库)

34资源网2022-09-05353

效果

前言

先来看一下shapeableimageview是什么

由上图可以看到shapeableimageview也没有什么神秘的,不过是imageview的一个子类而已,但是从效果图来看,在不写shape、不引入三方库的情况下,还是挺容易实现预期效果的,而且扩展性良好。

使用

引入material包

implementation 'com.google.android.material:material:1.2.1'

常规

<com.google.android.material.imageview.shapeableimageview
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_margin="10dp"
    android:src="@mipmap/ic_avatar" />

和imageview正常使用没有区别

圆角

<com.google.android.material.imageview.shapeableimageview
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_margin="10dp"
    android:src="@mipmap/ic_avatar"
    app:shapeappearance="@style/roundedstyle" />
<!--shapeableimageview 圆角-->
<style name="roundedstyle">
    <item name="cornerfamily">rounded</item>
    <item name="cornersize">10dp</item>
</style>
  • 没有直接设置圆角的属性,需要用到app:shapeappearance,后面会说
  • cornerfamily 角的处理方式,rounded圆角,cut裁剪
  • cornersize 圆角大小

<com.google.android.material.imageview.shapeableimageview
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_margin="10dp"
    android:src="@mipmap/ic_avatar"
    app:shapeappearance="@style/circlestyle" />
<!--shapeableimageview 圆 -->
<style name="circlestyle">
    <item name="cornerfamily">rounded</item>
    <item name="cornersize">50%</item>
</style>

圆角的大小可以用百分比,也可以自己计算,比如宽高100dp,圆角50dp

描边

<com.google.android.material.imageview.shapeableimageview
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_margin="10dp" 
    android:padding="2dp"
    android:src="@mipmap/ic_avatar"
    app:shapeappearance="@style/circlestyle"
    app:strokecolor="@color/red"
    app:strokewidth="4dp" />
  • app:strokecolor 描边颜色
  • app:strokewidth 描边宽度
  • 注意这里padding的数值是描边宽度的一半,后面会说

切角

<com.google.android.material.imageview.shapeableimageview
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_margin="10dp" 
    android:padding="2dp"
    android:src="@mipmap/ic_avatar"
    app:shapeappearance="@style/cutstyle"
    app:strokecolor="@color/red"
    app:strokewidth="4dp" />
<!--shapeableimageview 切角 -->
<style name="cutstyle">
    <item name="cornerfamily">cut</item>
    <item name="cornersize">10dp</item>
</style>

cornerfamily:cut 处理模式变为裁剪

菱形

<com.google.android.material.imageview.shapeableimageview
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_margin="10dp" 
    android:padding="2dp"
    android:src="@mipmap/ic_avatar"
    app:shapeappearance="@style/rhombusstyle"
    app:strokecolor="@color/red"
    app:strokewidth="4dp" />
<!--shapeableimageview 菱形 -->
<style name="rhombusstyle">
    <item name="cornerfamily">cut</item>
    <item name="cornersize">50%</item>
</style>

同样,裁剪模式下圆角大小也可以计算

叶子

<com.google.android.material.imageview.shapeableimageview
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_margin="10dp" 
    android:padding="2dp"
    android:src="@mipmap/ic_avatar"
    app:shapeappearance="@style/leafstyle"
    app:strokecolor="@color/red"
    app:strokewidth="4dp" />
<!--shapeableimageview 叶子 -->
<style name="leafstyle">
    <item name="cornerfamily">rounded</item>
    <item name="cornersizetopleft">50%</item>
    <item name="cornersizebottomright">50%</item>
</style>
  • cornersizetopleft 左上圆角
  • cornersizebottomright 右下圆角
  • 以此类推,左上、左下、右上、右下等

半圆

<com.google.android.material.imageview.shapeableimageview
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_margin="10dp" 
    android:padding="2dp"
    android:src="@mipmap/ic_avatar"
    app:shapeappearance="@style/semicirclestyle"
    app:strokecolor="@color/red"
    app:strokewidth="4dp" />
<!--shapeableimageview 半圆 -->
<style name="semicirclestyle">
    <item name="cornerfamily">rounded</item>
    <item name="cornersizetopleft">50%</item>
    <item name="cornersizetopright">50%</item>
</style>

六边形

<com.google.android.material.imageview.shapeableimageview
    android:layout_width="wrap_content"
    android:layout_height="50dp"
    android:layout_margin="10dp" 
    android:padding="2dp"
    android:scaletype="centercrop"
    android:src="@mipmap/ic_avatar"
    app:shapeappearance="@style/hexagonstyle"
    app:strokecolor="@color/red"
    app:strokewidth="4dp" />
<!--shapeableimageview 六边形 -->
<style name="hexagonstyle">
    <item name="cornerfamily">cut</item>
    <item name="cornersizetopleft">50%</item>
    <item name="cornersizetopright">50%</item>
    <item name="cornersizebottomleft">50%</item>
    <item name="cornersizebottomright">50%</item>
</style>

属性

关于xml属性,我也做了一个整理,属性不多,只有4个

属性 描述
strokewidth 描边宽度
strokecolor 描边颜色
shapeappearance 外观样式
shapeappearanceoverlay 同上,叠加层

扩展

前面为了整体的排版,埋了几个伏笔,下面来一一解答。

会涉及到源码,但是经过去繁从简,看起来也非常轻松的。

shapeappearance

shape appearance overlay style reference for shapeableimageview.
shapeableimageview的形状外观覆盖样式参考。

前面可以看到我们设置圆角其实是用的style,那为什么不直接用attrs呢,不是更加直观方便吗,带着疑问来看看源码是怎么处理的。

直接看shapeableimageview的次构造方法:

public class shapeableimageview extends appcompatimageview implements shapeable {

  ...

  public shapeableimageview(context context, @nullable attributeset attrs, int defstyle) {
    super(wrap(context, attrs, defstyle, def_style_res), attrs, defstyle);
    // ensure we are using the correctly themed context rather than the context that was passed in.
    context = getcontext();

    clearpaint = new paint();
    clearpaint.setantialias(true);
    clearpaint.setcolor(color.white);
    clearpaint.setxfermode(new porterduffxfermode(mode.dst_out));
    destination = new rectf();
    maskrect = new rectf();
    maskpath = new path();
    typedarray attributes =
        context.obtainstyledattributes(
            attrs, r.styleable.shapeableimageview, defstyle, def_style_res);

    strokecolor =
        materialresources.getcolorstatelist(
            context, attributes, r.styleable.shapeableimageview_strokecolor);

    strokewidth = attributes.getdimensionpixelsize(r.styleable.shapeableimageview_strokewidth, 0);

    borderpaint = new paint();
    borderpaint.setstyle(style.stroke);
    borderpaint.setantialias(true);
    shapeappearancemodel =
        shapeappearancemodel.builder(context, attrs, defstyle, def_style_res).build();
    shadowdrawable = new materialshapedrawable(shapeappearancemodel);
    if (version.sdk_int >= version_codes.lollipop) {
      setoutlineprovider(new outlineprovider());
    }
  }
}

常规操作,获取自定义属性。

关键的两行代码:

    shapeappearancemodel = shapeappearancemodel.builder(context, attrs, defstyle, def_style_res).build();
    shadowdrawable = new materialshapedrawable(shapeappearancemodel);

也就是说我们给shapeappearance设置的style,并不是shapeableimageview自己来处理的,而是由shapeappearancemodel来构建的,然后又交给materialshapedrawable来绘制的。

shapeappearancemodel

这个类就厉害了,有点像flutter中的decoration,可以构建出花里胡哨的效果。

来看shapeappearancemodel部分源码:

public class shapeappearancemodel {

  /** builder to create instances of {@link shapeappearancemodel}s. */
  public static final class builder {

    @nonnull
    private cornertreatment topleftcorner = materialshapeutils.createdefaultcornertreatment();

    @nonnull
    private cornertreatment toprightcorner = materialshapeutils.createdefaultcornertreatment();

    @nonnull
    private cornertreatment bottomrightcorner = materialshapeutils.createdefaultcornertreatment();

    @nonnull
    private cornertreatment bottomleftcorner = materialshapeutils.createdefaultcornertreatment();

    @nonnull private cornersize topleftcornersize = new absolutecornersize(0);
    @nonnull private cornersize toprightcornersize = new absolutecornersize(0);
    @nonnull private cornersize bottomrightcornersize = new absolutecornersize(0);
    @nonnull private cornersize bottomleftcornersize = new absolutecornersize(0);

    @nonnull private edgetreatment topedge = materialshapeutils.createdefaultedgetreatment();
    @nonnull private edgetreatment rightedge = materialshapeutils.createdefaultedgetreatment();
    @nonnull private edgetreatment bottomedge = materialshapeutils.createdefaultedgetreatment();
    @nonnull private edgetreatment leftedge = materialshapeutils.createdefaultedgetreatment();

    public builder() {}
    ...
  }
  ...
}

可以看到有各种边和角的属性,这里注意两个点:

  • materialshapeutils.createdefaultcornertreatment() 创建默认角的处理方式
  • materialshapeutils.createdefaultedgetreatment() 创建默认边的处理方式

也就意味着,边和角除了默认,是可以自定义的,这就有极大的想象空间了,

比如这样:

// 代码设置 角和边
val shapeappearancemodel2 = shapeappearancemodel.builder().apply {
    setallcorners(roundedcornertreatment())
    setallcornersizes(50f)
    setalledges(triangleedgetreatment(50f, false))
}.build()
val drawable2 = materialshapedrawable(shapeappearancemodel2).apply {
    settint(contextcompat.getcolor(this@shapeableimageviewactivity, r.color.colorprimary))
    paintstyle = paint.style.fill_and_stroke
    strokewidth = 50f
    strokecolor = contextcompat.getcolorstatelist(this@shapeableimageviewactivity, r.color.red)
}
mbinding.text2.settextcolor(color.white)
mbinding.text2.background = drawable2

再比如这样:

// 代码设置 聊天框效果
val shapeappearancemodel3 = shapeappearancemodel.builder().apply {
    setallcorners(roundedcornertreatment())
    setallcornersizes(20f)
    setrightedge(object : triangleedgetreatment(20f, false) {
        // center 位置 , interpolation 角的大小
        override fun getedgepath(length: float, center: float, interpolation: float, shapepath: shapepath) {
            super.getedgepath(length, 35f, interpolation, shapepath)
        }
    })
}.build()
val drawable3 = materialshapedrawable(shapeappearancemodel3).apply {
    settint(contextcompat.getcolor(this@shapeableimageviewactivity, r.color.colorprimary))
    paintstyle = paint.style.fill
}
(mbinding.text3.parent as viewgroup).clipchildren = false // 不限制子view在其范围内
mbinding.text3.settextcolor(color.white)
mbinding.text3.background = drawable3

materialshapedrawable

源码(有删减):

public class materialshapedrawable extends drawable implements tintawaredrawable, shapeable {
...
  @override
  public void draw(@nonnull canvas canvas) {
    fillpaint.setcolorfilter(tintfilter);
    final int prevalpha = fillpaint.getalpha();
    fillpaint.setalpha(modulatealpha(prevalpha, drawablestate.alpha));

    strokepaint.setcolorfilter(stroketintfilter);
    strokepaint.setstrokewidth(drawablestate.strokewidth);

    final int prevstrokealpha = strokepaint.getalpha();
    strokepaint.setalpha(modulatealpha(prevstrokealpha, drawablestate.alpha));

    if (pathdirty) {
      calculatestrokepath();
      calculatepath(getboundsasrectf(), path);
      pathdirty = false;
    }

    maybedrawcompatshadow(canvas);
    if (hasfill()) {
      drawfillshape(canvas);
    }
    if (hasstroke()) {
      drawstrokeshape(canvas);
    }
...
  static final class materialshapedrawablestate extends constantstate {
    ...
    public materialshapedrawablestate(@nonnull materialshapedrawablestate orig) {
      shapeappearancemodel = orig.shapeappearancemodel;
      elevationoverlayprovider = orig.elevationoverlayprovider;
      strokewidth = orig.strokewidth;
      colorfilter = orig.colorfilter;
      fillcolor = orig.fillcolor;
      strokecolor = orig.strokecolor;
      tintmode = orig.tintmode;
      tintlist = orig.tintlist;
      alpha = orig.alpha;
      scale = orig.scale;
      shadowcompatoffset = orig.shadowcompatoffset;
      shadowcompatmode = orig.shadowcompatmode;
      usetintcolorforshadow = orig.usetintcolorforshadow;
      interpolation = orig.interpolation;
      parentabsoluteelevation = orig.parentabsoluteelevation;
      elevation = orig.elevation;
      translationz = orig.translationz;
      shadowcompatradius = orig.shadowcompatradius;
      shadowcompatrotation = orig.shadowcompatrotation;
      stroketintlist = orig.stroketintlist;
      paintstyle = orig.paintstyle;
      if (orig.padding != null) {
        padding = new rect(orig.padding);
      }
    }
	...
  }
...
}

没什么特别的,你只需要知道除了可以设置描边之外,还可以设置背景、阴影等其他属性。

说明

  • shapeappearancemodel只能是实现shapeable接口的view才可以设置,比如chipmaterialbuttom等。
  • materialshapedrawable其实就是drawable,是所有view都可以设置的。

描边问题

这里借github一张图

又是自定义view的常规操作,有一半画笔是在边界外面的,所以需要设置paddingstrokewidth的一半。

默认圆角问题

有细心的同学会发现啊,第一个常规的shapeableimageview还是有一点圆角的,没错,属于默认的,跟踪一下源码来看一下:

<style name="widget.materialcomponents.shapeableimageview" parent="android:widget">
    <item name="strokecolor">@color/material_on_surface_stroke</item>
    <item name="shapeappearance">?attr/shapeappearancemediumcomponent</item>
</style>

第一个是颜色,很明显不是我们要找的,继续看shapeappearancemediumcomponent

<attr format="reference" name="shapeappearancemediumcomponent"/>

只是一个简单的属性,继续查找关联引用

    <item name="shapeappearancemediumcomponent">
      @style/shapeappearance.materialcomponents.mediumcomponent
    </item>

又引用了一个style,继续看shapeappearance.materialcomponents.mediumcomponent这个style

<style name="shapeappearance.materialcomponents.mediumcomponent">
    <item name="cornersize">@dimen/mtrl_shape_corner_size_medium_component</item>
</style>

哦豁,看到了熟悉的属性cornersize,藏的还挺深,继续看看数值是多少

<dimen name="mtrl_shape_corner_size_medium_component">4dp</dimen>

默认4dp

那如果不想要这个圆角怎么办呢,可以学习源码仿写一个,不过上面也看到了,有点绕,不如直接写个style搞定:

    <!--shapeableimageview 去圆角-->
    <style name="corner0style">
        <item name="cornersize">0dp</item>
    </style>

然后引用

app:shapeappearance="@style/corner0style"

效果:

ok,到这里就差不多了,虽然还有很多相关知识点没有提及,但是也不少了,不如自己去尝试一番,慢慢消化。

github

https://github.com/yechaoa/materialdesign

感谢

  • shapeableimageview 官方文档
  • shapeappearancemodel 官方文档
  • android material组件使用详解
  • android notes|玩转 shapeableimageview
  • material components——shape的处理

最后

到此这篇关于android中shapeableimageview使用详解的文章就介绍到这了,更多相关android shapeableimageview使用内容请搜索萬仟网以前的文章或继续浏览下面的相关文章希望大家以后多多支持萬仟网!

看完文章,还可以扫描下面的二维码下载快手极速版领4元红包

快手极速版二维码

快手极速版新人见面礼

除了扫码领红包之外,大家还可以在快手极速版做签到,看视频,做任务,参与抽奖,邀请好友赚钱)。

邀请两个好友奖最高196元,如下图所示:

快手极速版邀请好友奖励

扫描二维码推送至手机访问。

版权声明:本文由34楼发布,如需转载请注明出处。

本文链接:https://www.34l.com/post/21675.html

分享给朋友:

相关文章

2021年创业的话,试试这几个点子行不行

2021年创业的话,试试这几个点子行不行

想要在平淡的生活工作中发现一些好的创业项目还真没那么容易。2019年即将过去一半,那么大家有没有发现什么2019年最新创业点子呢?哪些项目的市场前景以及利润可观呢?下面跟随快马小编一起来看看吧。说不定能够激发你的创业灵感!…

天上的孩子电影好看吗?看豆瓣网友如何评价的吧

天上的孩子电影好看吗?看豆瓣网友如何评价的吧

由胡玫监制许磊导演编剧的电影《天上的孩子》。电影主要讲述了来自贵州的夫妻老何、玲霞5岁的独子查出绝症,不久于人世。为了让儿子的名字刻在纪念碑上,夫妻俩决定捐献儿子的器官却困难重重。…

哪家的云主机好(国内五大云主机服务商)

哪家的云主机好(国内五大云主机服务商)

导言:博睿数据(股票代码688229)十余年专注APM领域,已为超过2000余家大型企业提供专业数据服务。依托先进的测评技术及丰富的行业经验,博睿宏远倾力打造了一个公开透明的性能测评栏目——【Bonree指数】。该栏目致力于呈现各行业的整体…

微信开放外链,社交想象力有限

微信开放外链,社交想象力有限

编者按:本文来自新熵,创业邦经授权发布,封面图来自摄图网。 作者|古廿 编辑|伊页 网易云音乐上市,丁磊在现场讲了很多的未来,比如元宇宙。但未来是摸不着看不见的,所以愿意放眼未来的人,多半还要立足脚下。 脚下的立足往往来自两个方面:一…

一场关于元宇宙公司之死的剧本杀

一场关于元宇宙公司之死的剧本杀

编者按:本文来自脑极体,创业邦经授权发布。 2021年,被称作元宇宙元年。这种结合了区块链、虚拟现实、增强现实多种技术的概念,据称能够提供社交、娱乐、电商多种功能。美国彭博社称,元宇宙的市场规模将在2024年将达到8000亿美元。而就在20…

个人微信号怎么更改(设置里的微信密码)

个人微信号怎么更改(设置里的微信密码)

有多少人和我一样,起了一个非常非常中二的名字。当初的年少无知终于可以后悔了。微信号终于!终于!终于可以修改微信号了。(IOS用户请先等等,现在仅支持安卓用户哦) 微信最近推出新功能,允许用户每年修改一次微信号。 TOP1:首先把微信更新到…