当前位置:首页 > 谈天说地 > 正文内容

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

34资源网2022年09月05日 09:42237

效果

前言

先来看一下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使用内容请搜索萬仟网以前的文章或继续浏览下面的相关文章希望大家以后多多支持萬仟网!

看完文章,还可以用支付宝扫描下面的二维码领取一个支付宝红包,目前可领1-88元不等

支付宝红包二维码

除了扫码可以领取之外,大家还可以(复制 720087999 打开✔支付宝✔去搜索, h`o`n.g.包哪里来,动动手指就能领)。

看下图所示是好多参与这次活动领取红包的朋友:

支付宝红包

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

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

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

分享给朋友:

相关文章

经典语录分享:这城市风很大,孤独的人总是晚回家
经典语录分享:这城市风很大,孤独的人总是晚回家

1、不怕变成自己厌恶的人,我怕的是,过的还不如他们。2、无论受了多少委屈。我只会把它憋在心里。不是不想说,只是不知道该怎么说,能和谁说。3、思念很长,所以一日如两年,时间很短,所以两年如一日。4、你是不是又在苦心翻找一句话,只为给那个人看。...

联想新手机什么时候上市(联想2021即将上市新款笔记本)
联想新手机什么时候上市(联想2021即将上市新款笔记本)

11月8日,联想中国区手机业务部总经理发布了一则新机预告:摩托罗拉edge X的发布已进入倒计时阶段,在骁龙898处理器即将发布之际预告新机,很大可能预示着首发权已到手。而在双十一当天,陈劲又发布了一则微博,暗示摩托罗拉的全新手机已到手,“...

融资丨「PPIO边缘云」完成亿元A1轮融资,刷新边缘云领域融资记录
融资丨「PPIO边缘云」完成亿元A1轮融资,刷新边缘云领域融资记录

创业邦获悉,近日,边缘云公司PPIO宣布完成过亿元A1轮融资,由创世伙伴、张江科投、磐霖资本等多家机构联合投资,Pre-A轮投资方蓝驰创投、沸点资本及华业天成继续追加投资,光源资本担任融资财务顾问。这是公司半年内再次获投资人支持,公司A2...

网易云:生而破发,我很抱歉
网易云:生而破发,我很抱歉

图源:摄图网 编者按:本文来自微信公众号财经新知(ID:caijingxinzhi),创业邦经授权转载 云村村长磊磊昨天好忙哦,搞了一个元宇宙的敲钟仪式,但还是阻挡不了破发的命运。 村长昨天敲钟分享的四首歌还记得吗:四次敲锣,四种心境。...

火了12年,“假洋鬼子”终于翻车了
火了12年,“假洋鬼子”终于翻车了

图源:摄图网 编者按:本文来自微信公众号格隆汇财经热点(ID:glh_finance),作者:万连山,创业邦经授权转载 你肯定看到过这样一幅广告:一个与乔布斯颇有几分相似的神秘洋老头,戴着金丝眼镜,有时叼着烟斗,有时没有,出现在各大高铁...

已覆盖70%前十大快递/快运客户,商用车后市场玩家「大车队长」眼中的轮胎“生命力”
已覆盖70%前十大快递/快运客户,商用车后市场玩家「大车队长」眼中的轮胎“生命力”

2020年,商用车后市场头部创业公司「大车队长」正式完成了数千万元人民币A轮融资,由经纬中国领投。融资后的一年里,大车队长成长迅速,还发布了全新的“5113”战略,即5年服务100万台车、1000万个轮位、完成300亿元营收。 截至目前,...