現代人的崩潰是一種默不吭聲的崩潰,看起來很正常,會說笑,會打鬧,會社交,表面平靜,實際上內心的糟心事已經積累到必定程度了。不會摔門砸東西,不會流眼淚或歇斯心底,但可能某一秒忽然就積累到極致,也不說話,也不真的崩潰,也不太想活着。也不敢去死。java
這裏引用了「張帥B」的一段話,說的很貼切,咱們這一代年輕人到底怎麼了?android
先來看看效果圖: git
衆所周知,Android 中主流的圖片加載框架有 Picasso,Glide,Fresco。Picasso 加載 gif 圖沒有動畫效果,Glide 與 Fresco 支持 gif 動效圖。Fresco 自帶的控件 SimpleDraweeView 支持圓角屬性,Glide 須要手動給 ImageView 設置 shape,那麼 Glide,Fresco 是否支持 gif 圖圓角?github
先來看一個例子,Glide 加載 gif 圓角,先看看 xml 佈局:canvas
<ImageView
android:id="@+id/iv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="64dp"
android:background="@drawable/corners_bg"
android:src="@mipmap/gif_01"
/>
複製代碼
圓角文件 corners_bg :數組
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<corners android:radius="8dp"></corners>
</shape>
複製代碼
Glide 加載動圖:框架
Glide.with(this).load(R.mipmap.gif_01)
.asGif()
.override(720, 512)
.into(mImageView);
複製代碼
效果圖: ide
你們都知道 gif 是由多張靜態圖組合而成,若是處理單張圖片效率將會極其低下,既然不能對源圖片進行處理,那麼就只能在顯示控件上想辦法了。佈局
通過初步分析有如下三種可行方案:性能
方案一裁剪控件,改變圖片顯示區域,但裁剪的效率並不高,顧排除方案一;方案二,ui 切圓角圖片,若是在換膚的狀況下,ui 須要切多套圓角圖片,可擴展性太差,同時覆蓋的圓角圖片加載會消耗性能,排除方案二;那就只有第三方案了,在性能與可擴展性方面優於前兩種方案。
/** * Set the path's fill type. This defines how "inside" is computed. * * @param ft The new fill type for this path */
public void setFillType(FillType ft) {
// 調用 c 層的 jni 方法
}
複製代碼
設置路徑的填充樣式,定義了 "內部" 是如何計算的。 參數 ft
是個枚舉值,有 4 種類型:
/** * Enum for the ways a path may be filled. */
public enum FillType {
// these must match the values in SkPath.h
/** * Specifies that "inside" is computed by a non-zero sum of signed * edge crossings. */
WINDING (0),
/** * Specifies that "inside" is computed by an odd number of edge * crossings. */
EVEN_ODD (1),
/** * Same as {@link #WINDING}, but draws outside of the path, rather than inside. */
INVERSE_WINDING (2),
/** * Same as {@link #EVEN_ODD}, but draws outside of the path, rather than inside. */
INVERSE_EVEN_ODD(3);
}
複製代碼
默認值是 WINDING (0) ,咱們一塊兒來探究下這幾個值產生的效果,先來看看下面這段代碼:
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.save();
mPath.addCircle(600, 400, 200, Path.Direction.CW);
mPath.addCircle(900, 400, 200, Path.Direction.CW);
canvas.drawPath(mPath, mPaint);
canvas.restore();
}
複製代碼
繪製兩相交圓,並兩圓的方向都是順時針繪製。效果圖是這樣的:
mPath.addCircle(600, 400, 200, Path.Direction.CW);
mPath.addCircle(900, 400, 200, Path.Direction.CCW);
複製代碼
效果圖以下:
其實WINDING表示非零環繞原則,從任意一點發射一條線,默認值是 0,遇到順時針交點則 +1,遇到逆時針交點則 -1,最終若是不等於 0,則認爲這個點是圖形內部的點,則須要繪製顏色;反之,若是這個值是 0,則認爲這個點不在圖形內部,則不須要繪製顏色。
正好解釋上圖相交的部分沒有繪製顏色,相交的部分首先是繪製順時針方向的圓 +1 ,而後繪製逆時針方向的圓 -1 ,正好等於 0,不須要繪製顏色。
這個值和 WINDING 不一樣,WINDING 要求每一個圖形都是有方向的。EVEN_ODD 並不要求圓的方向,看看下面一段代碼:
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.save();
mPath.setFillType(Path.FillType.EVEN_ODD);
// 改爲 Path.Direction.CCW 的效果同樣
mPath.addCircle(600, 400, 200, Path.Direction.CW);
mPath.addCircle(900, 400, 200, Path.Direction.CW);
canvas.drawPath(mPath, mPaint);
canvas.restore();
}
複製代碼
效果圖以下:
英文單詞中 EVEN 是偶數,ODD 是奇數的意思。這個原則也被稱爲奇偶原則。從任意一點射出一條線,與圖形的交線是奇數,則認爲這個點在圖形內部,須要繪製顏色;反之若是是偶數,則認爲這個點在圖形外部,不須要繪製顏色。
inverse 表示反轉的意思,相同的一段代碼,繪製出來的結果是相反的。以下代碼:
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.save();
mPath.setFillType(Path.FillType.INVERSE_WINDING);
mPath.addCircle(600, 400, 200, Path.Direction.CW);
mPath.addCircle(900, 400, 200, Path.Direction.CW);
canvas.drawPath(mPath, mPaint);
canvas.restore();
}
複製代碼
圓的區域都不須要繪製顏色,非圓區域繪製顏色。效果圖以下:
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.save();
mPath.setFillType(Path.FillType.INVERSE_WINDING);
mPath.addCircle(600, 400, 200, Path.Direction.CW);
mPath.addCircle(900, 400, 200, Path.Direction.CCW);
canvas.drawPath(mPath, mPaint);
canvas.restore();
}
複製代碼
兩圓相交的部分須要繪製顏色,非圓區域繪製顏色。效果圖一覽:
INVERSE_EVEN_ODD 模式與 INVERSE_WINDING 模式的不一樣的繪製方向效果同樣,代碼以下:
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.save();
mPath.setFillType(Path.FillType.INVERSE_EVEN_ODD);
mPath.addCircle(600, 400, 200, Path.Direction.CW);
mPath.addCircle(900, 400, 200, Path.Direction.CCW);
canvas.drawPath(mPath, mPaint);
canvas.restore();
}
複製代碼
效果圖:
經過上文原理的介紹,gif 圖圓角控件的代碼就很是簡單。
接地氣的名字,可以讓人眼前一亮,就叫 CornersGifView吧。
核心代碼以下(3 行代碼):
mPath.reset();
// add round rect
mPath.setFillType(Path.FillType.INVERSE_EVEN_ODD);
mPath.addRoundRect(new RectF(0, 0, w, h), mCorners, Path.Direction.CCW);
複製代碼
相關參數:
你們可能已經注意到了,非圓角矩形區域的顏色,爲 mPaint 畫筆的顏色,那麼就須要保證畫筆的顏色與父控件的背景顏色一致,若是父控件顏色爲透明,那麼就須要取父控件的父控件顏色,以此類推,遞歸獲取父控件的顏色,請參考如下兩個方法:
/** * @param vp parent view * @return paint color */
private int getPaintColor(ViewParent vp) {
if (null == vp) {
return Color.TRANSPARENT;
}
if (vp instanceof View) {
View parentView = (View) vp;
int color = getViewBackgroundColor(parentView);
if (Color.TRANSPARENT != color) {
return color;
} else {
getPaintColor(parentView.getParent());
}
}
return Color.TRANSPARENT;
}
複製代碼
經過反射獲取 View 的背景顏色值:
/** * @param view * @return */
private int getViewBackgroundColor(View view) {
Drawable drawable = view.getBackground();
if (null != drawable) {
Class<Drawable> drawableClass = (Class<Drawable>) drawable.getClass();
if (null == drawableClass) {
return Color.TRANSPARENT;
}
try {
Field field = drawableClass.getDeclaredField("mColorState");
field.setAccessible(true);
Object colorState = field.get(drawable);
Class colorStateClass = colorState.getClass();
Field colorStateField = colorStateClass.getDeclaredField("mUseColor");
colorStateField.setAccessible(true);
int viewColor = (int) colorStateField.get(colorState);
if (Color.TRANSPARENT != viewColor) {
return viewColor;
}
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
return Color.TRANSPARENT;
}
複製代碼
不只支持 gif 圖圓角,還支持靜態圖圓角,最終效果圖以下:
引發的思考,**FillType 可以支持圓角矩形,那麼是否是還能夠支持圓形,橢圓,任意的 Path 呢?答案是確定的,那麼又改怎麼去實現?**這裏以擴展圓形爲例:
新建 Path 先逆時針繪製控件大小的矩形,再繪製圓形(以控件中心爲圓點),接着設置 FillType 爲 WINDING ,最後添加路徑到原始路徑上 addPath ,代碼就像這樣:
private void addCirclePath() {
int w = getWidth();
int h = getHeight();
Path addPath = new Path();
addPath.addRect(new RectF(0, 0, w, h), Path.Direction.CCW);
addPath.addCircle(w / 2, h / 2, Math.min(w, h) / 2, Path.Direction.CW);
setPath(addPath);
}
複製代碼
private void setPath(Path path) {
mPath.reset();
mPath.setFillType(Path.FillType.WINDING);
mPath.addPath(path);
invalidate();
}
複製代碼
效果圖以下:
「張帥B」的那段話,概述的很是經典。都說 80 後的在忙着掙錢;00 後在忙着談戀愛;只有咱們 90 的在忙着賺錢又談戀愛,最後錢沒掙到,戀愛也沒談到。
最後再囉嗦一點,生活都不容易,一直堅持作一件事情更不容易,還但願各位朋友,可以多多支持,小編新開的公衆號「控件人生」,有大家的相伴,纔有寫下去的動力。小編還會不按期發放現金紅包,發放現金紅包,發放現金紅包。
源碼地址: