Android中ShapeableImageView使用實例詳解(告別shape、三方庫)
效果
前言
先來看一下ShapeableImageView
是什么
由上圖可以看到ShapeableImageView
也沒有什么神秘的,不過是ImageView
的一個子類而已,但是從效果圖來看,在不寫shape、不引入三方庫
的情況下,還是挺容易實現(xiàn)預(yù)期效果的,而且擴展性良好。
使用
引入material包
implementation 'com.google.android.material:material:1.2.1'
常規(guī)
<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正常使用沒有區(qū)別
圓角
<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>
- 沒有直接設(shè)置圓角的屬性,需要用到
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的數(shù)值是描邊寬度的一半,后面會說
切角
<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 處理模式變?yōu)椴眉?/p>
菱形
<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>
屬性
關(guān)于xml屬性,我也做了一個整理,屬性不多,只有4個
屬性 | 描述 |
---|---|
strokeWidth | 描邊寬度 |
strokeColor | 描邊顏色 |
shapeAppearance | 外觀樣式 |
shapeAppearanceOverlay | 同上,疊加層 |
擴展
前面為了整體的排版,埋了幾個伏筆,下面來一一解答。
會涉及到源碼,但是經(jīng)過去繁從簡,看起來也非常輕松的。
shapeAppearance
Shape appearance overlay style reference for ShapeableImageView.
ShapeableImageView的形狀外觀覆蓋樣式參考。
前面可以看到我們設(shè)置圓角其實是用的style
,那為什么不直接用attrs
呢,不是更加直觀方便嗎,帶著疑問來看看源碼是怎么處理的。
直接看ShapeableImageView
的次構(gòu)造方法:
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()); } } }
常規(guī)操作,獲取自定義屬性。
關(guān)鍵的兩行代碼:
shapeAppearanceModel = ShapeAppearanceModel.builder(context, attrs, defStyle, DEF_STYLE_RES).build(); shadowDrawable = new MaterialShapeDrawable(shapeAppearanceModel);
也就是說我們給shapeAppearance
設(shè)置的style,并不是ShapeableImageView
自己來處理的,而是由ShapeAppearanceModel
來構(gòu)建的,然后又交給MaterialShapeDrawable
來繪制的。
ShapeAppearanceModel
這個類就厲害了,有點像Flutter
中的Decoration,可以構(gòu)建出花里胡哨的效果。
來看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()
創(chuàng)建默認(rèn)角的處理方式MaterialShapeUtils.createDefaultEdgeTreatment()
創(chuàng)建默認(rèn)邊的處理方式
也就意味著,邊和角除了默認(rèn),是可以自定義的,這就有極大的想象空間了,
比如這樣:
// 代碼設(shè)置 角和邊 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
再比如這樣:
// 代碼設(shè)置 聊天框效果 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在其范圍內(nèi) 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); } } ... } ... }
沒什么特別的,你只需要知道除了可以設(shè)置描邊之外,還可以設(shè)置背景、陰影等其他屬性。
說明
ShapeAppearanceModel
只能是實現(xiàn)Shapeable
接口的View才可以設(shè)置,比如Chip
、MaterialButtom
等。- 而
MaterialShapeDrawable
其實就是Drawable
,是所有View都可以設(shè)置的。
描邊問題
這里借github一張圖
又是自定義view的常規(guī)操作,有一半畫筆是在邊界外面的,所以需要設(shè)置padding
為strokeWidth
的一半。
默認(rèn)圓角問題
有細心的同學(xué)會發(fā)現(xiàn)啊,第一個常規(guī)的ShapeableImageView
還是有一點圓角的,沒錯,屬于默認(rèn)的,跟蹤一下源碼來看一下:
<style name="Widget.MaterialComponents.ShapeableImageView" parent="android:Widget"> <item name="strokeColor">@color/material_on_surface_stroke</item> <item name="shapeAppearance">?attr/shapeAppearanceMediumComponent</item> </style>
第一個是顏色,很明顯不是我們要找的,繼續(xù)看shapeAppearanceMediumComponent
<attr format="reference" name="shapeAppearanceMediumComponent"/>
只是一個簡單的屬性,繼續(xù)查找關(guān)聯(lián)引用
<item name="shapeAppearanceMediumComponent"> @style/ShapeAppearance.MaterialComponents.MediumComponent </item>
又引用了一個style,繼續(xù)看ShapeAppearance.MaterialComponents.MediumComponent這個style
<style name="ShapeAppearance.MaterialComponents.MediumComponent"> <item name="cornerSize">@dimen/mtrl_shape_corner_size_medium_component</item> </style>
哦豁,看到了熟悉的屬性cornerSize
,藏的還挺深,繼續(xù)看看數(shù)值是多少
<dimen name="mtrl_shape_corner_size_medium_component">4dp</dimen>
默認(rèn)4dp
。
那如果不想要這個圓角怎么辦呢,可以學(xué)習(xí)源碼仿寫一個,不過上面也看到了,有點繞,不如直接寫個style搞定:
<!--ShapeableImageView 去圓角--> <style name="Corner0Style"> <item name="cornerSize">0dp</item> </style>
然后引用
app:shapeAppearance="@style/Corner0Style"
效果:
ok,到這里就差不多了,雖然還有很多相關(guān)知識點沒有提及,但是也不少了,不如自己去嘗試一番,慢慢消化。
Github
https://github.com/yechaoa/MaterialDesign
感謝
- ShapeableImageView 官方文檔
- ShapeAppearanceModel 官方文檔
- Android Material組件使用詳解
- Android Notes|玩轉(zhuǎn) ShapeableImageView
- Material Components——Shape的處理
最后
到此這篇關(guān)于Android中ShapeableImageView使用詳解的文章就介紹到這了,更多相關(guān)Android ShapeableImageView使用內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Flutter使用AnimatedBuilder實現(xiàn)動效復(fù)用
Animation和AnimationWidget都是將組件和動畫一起完成的。有些時候,我們只是想動效復(fù)用,而不關(guān)心組件構(gòu)建,這個時候就可以使用 AnimatedBuilder了。本文詳細講解了AnimatedBuilder的使用,需要的可以參考一下2022-04-04Android使用Pull解析器解析xml文件的實現(xiàn)代碼
Android使用Pull解析器解析xml文件的實現(xiàn)代碼,需要的朋友可以參考一下2013-02-02Android 自定義ContentProvider簡單實例
這篇文章主要介紹了Android 自定義ContentProvider簡單實例的相關(guān)資料,需要的朋友可以參考下2017-06-06Android Studio多渠道打包、自定義打包APK名稱
Android Studio為我們提供了簡便的方法,可以多渠道打包,一次打包所有的渠道包。這篇文章主要介紹了Android Studio多渠道打包、自定義打包APK名稱,需要的朋友可以參考下2018-01-01關(guān)于Android實現(xiàn)簡單的微信朋友圈分享功能
這篇文章主要介紹了關(guān)于Android實現(xiàn)簡單的微信朋友圈分享功能,非常不錯,具有參考借鑒價值,需要的的朋友參考下2017-02-02Android用于加載xml的LayoutInflater源碼超詳細分析
今天不想去聊一些Android的新功能,新特性之類的東西,特別想聊一聊這個老生常談的話題:LayoutInflater,感興趣的朋友來看看吧2022-08-08Android使用RotateImageView 旋轉(zhuǎn)ImageView
這篇文章主要介紹了Android使用RotateImageView 旋轉(zhuǎn)ImageView 的相關(guān)資料,需要的朋友可以參考下2016-01-01