写点什么

Android ShapeableImageView 使用详解,告别 shape、三方库 (2)

用户头像
Android架构
关注
发布于: 1 小时前

android:layout_margin="10dp"


android:padding="2dp"


android:src="@mipmap/ic_avatar"


app:shapeAppearance="@style/RhombusStyle"


app:strokeColor="@color/red"


app:strokeWidth="4dp" />


<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" />


<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" />


<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" />


<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>


author:[yechaoa](


)


[](


)属性


=============================================================


关于 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 = MaterialS


《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》
浏览器打开:qq.cn.hn/FTe 免费领取
复制代码


hapeUtils.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);


}


}


...


}


...


}


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

[](

)说明

用户头像

Android架构

关注

还未添加个人签名 2021.10.31 加入

还未添加个人简介

评论

发布
暂无评论
Android ShapeableImageView使用详解,告别shape、三方库(2)