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

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

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

效果

前言

先来看一下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、你是不是又在苦心翻找一句话,只为给那个人看。...

分享30句用被刺造句的句子
分享30句用被刺造句的句子

1、黄昏已经谢去,夜幕早已铺开。高高的法国梧桐,被刺眼的白色路灯照亮。在黑色的夜空里镶了一圈又一圈攫绿,有时被拂过的夜风飘动,发出轻轻的沙沙声,只那么一阵,就消失在无限的宁静之中。2、一个人的羞耻心在基本一点上被刺痛,那么,它的余波会在不知...

龚文祥真的破产了吗?
龚文祥真的破产了吗?

图源:摄图网 编者按:本文来自微信公众号十里村(ID:shilipxl),作者:村长住在十里村,创业邦经授权转载 各位村民好,我是村长。 号称团队不超过10个人,一年就能赚五千万的微商教父龚文祥破产了!!! 因此还上了各大平台的热搜榜...

共享系统开发多少钱(分享共享模式系统)
共享系统开发多少钱(分享共享模式系统)

现在,越来越多地商家都开始自己定制软件系统,都想争先抢占自己地私域流量,所以大家最想了解地是系统开发地方案和报价都有哪些,那么今天小编就来给大家讲一下关于系统开发方案和报价地一些知识。 一、系统开发方案1、系统定位首先,在一开始,不光...

诺基亚c2-01手机参数(诺基亚最新款手机)
诺基亚c2-01手机参数(诺基亚最新款手机)

诺基亚C2 Android Go Edition 价格:暂无价格 上市时间:2020年03月 屏幕尺寸:5.7英寸 分辨率:1520×720 触摸屏类型:电容触摸屏,多点触控CPU型号:紫光展锐SC9832E CPU核数:四核 操作系统...