自定義View 1-2Paint詳解

Paint 屬性分類

  • 顏色
  • 效果
  • drawText() 相關
  • 初始化

1 顏色

Canvas 繪制的內容,有三層對顏色的處理:


image.png

1.1 基本顏色

1.1.1 直接設置顏色

1.1.1.1 setColor(int color)
paint.setColor(Color.parseColor("#009688"));  
canvas.drawRect(30, 30, 230, 180, paint);

paint.setColor(Color.parseColor("#FF9800"));  
canvas.drawLine(300, 30, 450, 180, paint);

paint.setColor(Color.parseColor("#E91E63"));  
canvas.drawText("HenCoder", 500, 130, paint);  
image.png
1.1.1.2 setARGB(int a, int r, int g, int b)

和 setColor(color) 都是一樣一樣兒的,只是它的參數用的是更直接的三原色與透明度的值。實際運用中,setColor() 和 setARGB() 哪個方便和順手用哪個吧。

1.1.2 setShader(Shader shader) 設置 Shader

  • 著色器不是 Android 獨有的,它是圖形領域里一個通用的概念,它和直接設置顏色的區別是,著色器設置的是一個顏色方案,或者說是一套著色規則。
  • 當設置了 Shader 之后,Paint 在繪制圖形和文字時就不使用 setColor/ARGB() 設置的顏色了,而是使用 Shader 的方案中的顏色。
  • 在 Android 的繪制里使用 Shader ,并不直接用 Shader 這個類,而是用它的幾個子類。具體來講有 LinearGradient RadialGradient SweepGradient BitmapShader ComposeShader 這么幾個:
1.1.2.1 LinearGradient 線性漸變

設置兩個點和兩種顏色,以這兩個點作為端點,使用兩種顏色的漸變來繪制顏色。

就像這樣:

Shader shader = new LinearGradient(100, 100, 500, 500, Color.parseColor("#E91E63"),  
        Color.parseColor("#2196F3"), Shader.TileMode.CLAMP);
paint.setShader(shader);

...

canvas.drawCircle(300, 300, 200, paint);  
image.png

構造方法:
LinearGradient(float x0, float y0, float x1, float y1, int color0, int color1, Shader.TileMode tile) 。

參數:
x0 y0 x1 y1:漸變的兩個端點的位置
color0 color1 是端點的顏色
tile:端點范圍之外的著色規則,類型是 TileMode。TileMode 一共有 3 個值可選: CLAMP, MIRROR 和 REPEAT。CLAMP (夾子模式???算了這個詞我不會翻)會在端點之外延續端點處的顏色;MIRROR 是鏡像模式;REPEAT 是重復模式。具體的看一下例子就明白。

CLAMP:


image.png

MIRROR:


image.png

REPEAT:


image.png
1.1.2.2 RadialGradient 輻射漸變

輻射漸變很好理解,就是從中心向周圍輻射狀的漸變

Shader shader = new RadialGradient(300, 300, 200, Color.parseColor("#E91E63"),  
        Color.parseColor("#2196F3"), Shader.TileMode.CLAMP);
paint.setShader(shader);

...

canvas.drawCircle(300, 300, 200, paint);  
image.png

構造方法:
RadialGradient(float centerX, float centerY, float radius, int centerColor, int edgeColor, TileMode tileMode)。

參數:
centerX centerY:輻射中心的坐標
radius:輻射半徑
centerColor:輻射中心的顏色
edgeColor:輻射邊緣的顏色
tileMode:輻射范圍之外的著色模式。

TileMode 類型和LinearGradient的TitleMode是一樣的

1.1.2.3 SweepGradient 掃描漸變

掃描漸變

Shader shader = new SweepGradient(300, 300, Color.parseColor("#E91E63"),  
        Color.parseColor("#2196F3"));
paint.setShader(shader);

...

canvas.drawCircle(300, 300, 200, paint);  
image.png

構造方法:
SweepGradient(float cx, float cy, int color0, int color1)

參數:
cx cy :掃描的中心
color0:掃描的起始顏色
color1:掃描的終止顏色

1.1.2.4 BitmapShader

用 Bitmap 來著色(終于不是漸變了)。其實也就是用 Bitmap 的像素來作為圖形或文字的填充

Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.batman);  
Shader shader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);  
paint.setShader(shader);

...

canvas.drawCircle(300, 300, 200, paint);  
image.png

看著跟 Canvas.drawBitmap() 好像啊?事實上也是一樣的效果。如果你想繪制圓形的 Bitmap,就別用 drawBitmap() 了,改用 drawCircle() + BitmapShader 就可以了(其他形狀同理)。
構造方法:
BitmapShader(Bitmap bitmap, Shader.TileMode tileX, Shader.TileMode tileY)

參數:
bitmap:用來做模板的 Bitmap 對象
tileX:橫向的 TileMode
tileY:縱向的 TileMode。

1.1.2.5 ComposeShader 混合著色器

兩個 Shader 一起使用

// 第一個 Shader:頭像的 Bitmap
Bitmap bitmap1 = BitmapFactory.decodeResource(getResources(), R.drawable.batman);  
Shader shader1 = new BitmapShader(bitmap1, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);

// 第二個 Shader:從上到下的線性漸變(由透明到黑色)
Bitmap bitmap2 = BitmapFactory.decodeResource(getResources(), R.drawable.batman_logo);  
Shader shader2 = new BitmapShader(bitmap2, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);

// ComposeShader:結合兩個 Shader
Shader shader = new ComposeShader(shader1, shader2, PorterDuff.Mode.SRC_OVER);  
paint.setShader(shader);

...

canvas.drawCircle(300, 300, 300, paint);  

ComposeShader() 在硬件加速下是不支持兩個相同類型的 Shader 的,所以這里也需要關閉硬件加速才能看到效果。


image.png

構造方法:ComposeShader(Shader shaderA, Shader shaderB, PorterDuff.Mode mode)

參數:
shaderA, shaderB:兩個相繼使用的 Shader
mode: 兩個 Shader 的疊加模式,即 shaderA 和 shaderB 應該怎樣共同繪制。它的類型是 PorterDuff.Mode

PorterDuff.Mode效果,講解

1.2 setColorFilter(ColorFilter colorFilter)

為繪制設置顏色過濾。顏色過濾的意思,就是為繪制的內容設置一個統一的過濾策略,然后 Canvas.drawXXX() 方法會對每個像素都進行過濾后再繪制出來。

舉幾個現實中比較常見的顏色過濾的例子:

  • 有色光照射:


    image.png
  • 有色玻璃透視:


    image.png
  • 膠卷:


    image.png

在 Paint 里設置 ColorFilter ,使用的是 Paint.setColorFilter(ColorFilter filter) 方法。
ColorFilter 并不直接使用,而是使用它的子類。它共有三個子類:LightingColorFilter PorterDuffColorFilter 和 ColorMatrixColorFilter。

1.2.1 LightingColorFilter

這個 LightingColorFilter 是用來模擬簡單的光照效果的。

LightingColorFilter 的構造方法是 LightingColorFilter(int mul, int add) ,參數里的 mul 和 add 都是和顏色值格式相同的 int 值,其中 mul 用來和目標像素相乘,add 用來和目標像素相加:

R' = R * mul.R / 0xff + add.R  
G' = G * mul.G / 0xff + add.G  
B' = B * mul.B / 0xff + add.B  

效果:


image.png

image.png

1.2.2 PorterDuffColorFilter

作用是使用一個指定的顏色和一種指定的 PorterDuff.Mode 來與繪制對象進行合成。

它的構造方法是 PorterDuffColorFilter(int color, PorterDuff.Mode mode) 其中的 color 參數是指定的顏色, mode 參數是指定的 Mode。同樣也是 PorterDuff.Mode ,不過和 ComposeShader 不同的是,PorterDuffColorFilter 作為一個 ColorFilter,只能指定一種顏色作為源,而不是一個 Bitmap。

1.2.3 ColorMatrixColorFilter

ColorMatrixColorFilter 使用一個 ColorMatrix 來對顏色進行處理。 ColorMatrix 這個類,內部是一個 4x5 的矩陣:

[ a, b, c, d, e,
  f, g, h, i, j,
  k, l, m, n, o,
  p, q, r, s, t ]

通過計算, ColorMatrix 可以把要繪制的像素進行轉換。
效果:


matrixColorFilter.gif

1.3 setXfermode(Xfermode xfermode)

  • "Xfermode" 其實就是 "Transfer mode",用 "X" 來代替 "Trans" 是一些美國人喜歡用的簡寫方式
  • 以繪制的內容作為源圖像,以 View 中已有的內容作為目標圖像,選取一個 PorterDuff.Mode 作為繪制內容的顏色處理方案。

效果:


image.png

創建 Xfermode 的時候其實是創建的它的子類 PorterDuffXfermode。而事實上,Xfermode 也只有這一個子類。所以在設置 Xfermode 的時候不用多想,直接用 PorterDuffXfermode 吧。
「只有一個子類???什么設計?」
實在更早的 Android 版本中,Xfermode 還有別的子類,但別的子類現在已經 deprecated 了,如今只剩下了 PorterDuffXfermode。所以目前它的使用看起來好像有點啰嗦,但其實是由于歷史遺留問題。

Xfermode 注意事項

1. 使用離屏緩沖(Off-screen Buffer)
  • Canvas.saveLayer()
    saveLayer() 可以做短時的離屏緩沖。使用方法很簡單,在繪制代碼的前后各加一行代碼,在繪制之前保存,繪制之后恢復:
int saved = canvas.saveLayer(null, null, Canvas.ALL_SAVE_FLAG);


canvas.drawBitmap(rectBitmap, 0, 0, paint); // 畫方
paint.setXfermode(xfermode); // 設置 Xfermode
canvas.drawBitmap(circleBitmap, 0, 0, paint); // 畫圓
paint.setXfermode(null); // 用完及時清除 Xfermode


canvas.restoreToCount(saved);
2. 控制好透明區域

使用 Xfermode 來繪制的內容,除了注意使用離屏緩沖,還應該注意控制它的透明區域不要太小,要讓它足夠覆蓋到要和它結合繪制的內容,否則得到的結果很可能不是你想要的。

2 效果

效果類的 API ,指的就是抗鋸齒、填充/輪廓、線條寬度等等這些。

2.1 setAntiAlias (boolean aa) 設置抗鋸齒

抗鋸齒在上一節已經講過了,話不多說,直接上圖:


image.png

另外,除了 setAntiAlias(aa) 方法,打開抗鋸齒還有一個更方便的方式:構造方法。創建 Paint 對象的時候,構造方法的參數里加一個 ANTI_ALIAS_FLAG 的 flag,就可以在初始化的時候就開啟抗鋸齒。

2.2 setStyle(Paint.Style style)

setStyle(style) 也在上一節講過了,用來設置圖形是線條風格還是填充風格的(也可以二者并用):

paint.setStyle(Paint.Style.FILL); // FILL 模式,填充  
canvas.drawCircle(300, 300, 200, paint);  
image.png

STROKE


image.png

FILL_AND_STROKE


image.png

2.3 線條形狀

設置線條形狀的一共有 4 個方法:setStrokeWidth(float width), setStrokeCap(Paint.Cap cap), setStrokeJoin(Paint.Join join), setStrokeMiter(float miter) 。

2.3.1 setStrokeWidth(float width)

image.png

2.3.2 setStrokeCap(Paint.Cap cap)

設置線頭的形狀。線頭形狀有三種:BUTT 平頭、ROUND 圓頭、SQUARE 方頭。默認為 BUTT。

image.png

2.3.3 setStrokeJoin(Paint.Join join)

設置拐角的形狀。有三個值可以選擇:MITER 尖角、 BEVEL 平角和 ROUND 圓角。默認為 MITER。

image.png

2.3.4 setStrokeMiter(float miter)

這個方法是對于 setStrokeJoin() 的一個補充,它用于設置 MITER 型拐角的延長線的最大值。所謂「延長線的最大值」,是這么一回事:

當線條拐角為 MITER 時,拐角處的外緣需要使用延長線來補償:


image.png

而這種補償方案會有一個問題:如果拐角的角度太小,就有可能由于出現連接點過長的情況。比如這樣:

image.png

所以為了避免意料之外的過長的尖角出現, MITER 型連接點有一個額外的規則:當尖角過長時,自動改用 BEVEL 的方式來渲染連接點。例如上圖的這個尖角,在默認情況下是不會出現的,而是會由于延長線過長而被轉為 BEVEL 型連接點:


image.png

2.4 色彩優化

Paint 的色彩優化有兩個方法: setDither(boolean dither) 和 setFilterBitmap(boolean filter) 。它們的作用都是讓畫面顏色變得更加「順眼」,但原理和使用場景是不同的。

2.4.1 setDither(boolean dither)

設置圖像的抖動。

所謂抖動(注意,它就叫抖動,不是防抖動,也不是去抖動,有些人在翻譯的時候自作主張地加了一個「防」字或者「去」字,這是不對的),是指把圖像從較高色彩深度(即可用的顏色數)向較低色彩深度的區域繪制時,在圖像中有意地插入噪點,通過有規律地擾亂圖像來讓圖像對于肉眼更加真實的做法。

例子:
比如向 1 位色彩深度的區域中繪制灰色,由于 1 位深度只包含黑和白兩種顏色,在默認情況下,即不加抖動的時候,只能選擇向上或向下選擇最接近灰色的白色或黑色來繪制,那么顯示出來也只能是一片白或者一片黑。而加了抖動后,就可以繪制出讓肉眼識別為灰色的效果了:

瞇著眼看這幅圖


image.png
image.png
2.4.2 setFilterBitmap(boolean filter)

設置是否使用雙線性過濾來繪制 Bitmap 。

圖像在放大繪制的時候,默認使用的是最近鄰插值過濾,這種算法簡單,但會出現馬賽克現象;而如果開啟了雙線性過濾,就可以讓結果圖像顯得更加平滑。效果依然盜維基百科的圖:

image.png

2.5 setPathEffect(PathEffect effect)

使用 PathEffect 來給圖形的輪廓設置效果。對 Canvas 所有的圖形繪制有效,也就是 drawLine() drawCircle() drawPath() 這些方法。大概像這樣:

PathEffect pathEffect = new DashPathEffect(new float[]{10, 5}, 10);  
paint.setPathEffect(pathEffect);

...

canvas.drawCircle(300, 300, 200, paint);  
image.png

下面就具體說一下 Android 中的 6 種 PathEffect。PathEffect 分為兩類,單一效果的 CornerPathEffect DiscretePathEffect DashPathEffect PathDashPathEffect ,和組合效果的 SumPathEffect ComposePathEffect。

2.5.1 CornerPathEffect

把所有拐角變成圓角。

PathEffect pathEffect = new CornerPathEffect(20);

image.png

它的構造方法 CornerPathEffect(float radius) 的參數 radius 是圓角的半徑。

2.5.2 DiscretePathEffect

把線條進行隨機的偏離,讓輪廓變得亂七八糟。亂七八糟的方式和程度由參數決定。
PathEffect pathEffect = new DiscretePathEffect(20, 5);

image.png

DiscretePathEffect 具體的做法是,把繪制改為使用定長的線段來拼接,并且在拼接的時候對路徑進行隨機偏離。它的構造方法 DiscretePathEffect(float segmentLength, float deviation) 的兩個參數中, segmentLength 是用來拼接的每個線段的長度, deviation 是偏離量。這兩個值設置得不一樣,顯示效果也會不一樣,具體的你自己多試幾次就明白了,這里不再貼更多的圖。

2.5.3 DashPathEffect

使用虛線來繪制線條。


image.png

它的構造方法 DashPathEffect(float[] intervals, float phase) 中, 第一個參數 intervals 是一個數組,它指定了虛線的格式:數組中元素必須為偶數(最少是 2 個),按照「畫線長度、空白長度、畫線長度、空白長度」……的順序排列,例如上面代碼中的 20, 5, 10, 5 就表示虛線是按照「畫 20 像素、空 5 像素、畫 10 像素、空 5 像素」的模式來繪制;第二個參數 phase 是虛線的偏移量。

2.5.4 PathDashPathEffect

這個方法比 DashPathEffect 多一個前綴 Path ,所以顧名思義,它是使用一個 Path 來繪制「虛線」。具體看圖吧:

image.png

它的構造方法 PathDashPathEffect(Path shape, float advance, float phase, PathDashPathEffect.Style style) 中, shape 參數是用來繪制的 Path ; advance 是兩個相鄰的 shape 段之間的間隔,不過注意,這個間隔是兩個 shape 段的起點的間隔,而不是前一個的終點和后一個的起點的距離; phase 和 DashPathEffect 中一樣,是虛線的偏移;最后一個參數 style,是用來指定拐彎改變的時候 shape 的轉換方式。style 的類型為 PathDashPathEffect.Style ,是一個 enum ,具體有三個值:

  • TRANSLATE:位移
  • ROTATE:旋轉
  • MORPH:變體


    image.png

2.5.5 SumPathEffect

這是一個組合效果類的 PathEffect 。它的行為特別簡單,就是分別按照兩種 PathEffect 分別對目標進行繪制。

PathEffect dashEffect = new DashPathEffect(new float[]{20, 10}, 0);  
PathEffect discreteEffect = new DiscretePathEffect(20, 5);  
pathEffect = new SumPathEffect(dashEffect, discreteEffect);

...

canvas.drawPath(path, paint);  
image.png

2.5.6 ComposePathEffect

這也是一個組合效果類的 PathEffect 。不過它是先對目標 Path 使用一個 PathEffect,然后再對這個改變后的 Path 使用另一個 PathEffect。

PathEffect pathEffect = new DashPathEffect(new float[]{10, 5}, 10);  
paint.setPathEffect(pathEffect);

...

canvas.drawCircle(300, 300, 200, paint);  
image.png

它的構造方法 ComposePathEffect(PathEffect outerpe, PathEffect innerpe) 中的兩個 PathEffect 參數, innerpe 是先應用的, outerpe 是后應用的。所以上面的代碼就是「先偏離,再變虛線」。而如果把兩個參數調換,就成了「先變虛線,再偏離」。至于具體的視覺效果……我就不貼圖了,你自己試試看吧!

總結

上面這些就是 Paint 中的 6 種 PathEffect。它們有的是有獨立效果的,有的是用來組合不同的 PathEffect 的,功能各不一樣。

注意: PathEffect 在有些情況下不支持硬件加速,需要關閉硬件加速才能正常使用:
Canvas.drawLine() 和 Canvas.drawLines() 方法畫直線時,setPathEffect() 是不支持硬件加速的;
PathDashPathEffect 對硬件加速的支持也有問題,所以當使用 PathDashPathEffect 的時候,最好也把硬件加速關了。

2.6 setShadowLayer(float radius, float dx, float dy, int shadowColor)

在之后的繪制內容下面加一層陰影。

paint.setShadowLayer(10, 0, 0, Color.RED);

...

canvas.drawText(text, 80, 300, paint);  
image.png

效果就是上面這樣。方法的參數里, radius 是陰影的模糊范圍; dx dy 是陰影的偏移量; shadowColor 是陰影的顏色。

如果要清除陰影層,使用 clearShadowLayer() 。

注意:

  • 在硬件加速開啟的情況下, setShadowLayer() 只支持文字的繪制,文字之外的繪制必須關閉硬件加速才能正常繪制陰影。
  • 如果 shadowColor 是半透明的,陰影的透明度就使用 shadowColor 自己的透明度;而如果 shadowColor 是不透明的,陰影的透明度就使用 paint 的透明度。

2.7 setMaskFilter(MaskFilter maskfilter)

上一個方法 setShadowLayer() 是設置的在繪制層下方的附加效果;而這個 MaskFilter 和它相反,設置的是在繪制層上方的附加效果。

到現在已經有兩個 setXxxFilter(filter) 了。前面有一個 setColorFilter(filter) ,是對每個像素的顏色進行過濾;而這里的 setMaskFilter(filter) 則是基于整個畫面來進行過濾。

MaskFilter 有兩種: BlurMaskFilter 和 EmbossMaskFilter。

2.7.1 BlurMaskFilter

模糊效果的 MaskFilter。

paint.setMaskFilter(new BlurMaskFilter(50, BlurMaskFilter.Blur.NORMAL));

...

canvas.drawBitmap(bitmap, 100, 100, paint);  
image.png

它的構造方法 BlurMaskFilter(float radius, BlurMaskFilter.Blur style) 中, radius 參數是模糊的范圍, style 是模糊的類型。一共有四種:

  • NORMAL: 內外都模糊繪制
  • SOLID: 內部正常繪制,外部模糊
  • INNER: 內部模糊,外部不繪制
  • OUTER: 內部不繪制,外部模糊(什么鬼?)
image.png

2.7.2 EmbossMaskFilter

浮雕效果的 MaskFilter。

paint.setMaskFilter(new EmbossMaskFilter(new float[]{0, 1, 1}, 0.2f, 8, 10));

...

canvas.drawBitmap(bitmap, 100, 100, paint);  
image.png

它的構造方法 EmbossMaskFilter(float[] direction, float ambient, float specular, float blurRadius) 的參數里, direction 是一個 3 個元素的數組,指定了光源的方向; ambient 是環境光的強度,數值范圍是 0 到 1; specular 是炫光的系數; blurRadius 是應用光線的范圍。

2.8 獲取繪制的 Path

根據 paint 的設置,計算出繪制 Path 或文字時的實際 Path。

2.8.1 getFillPath(Path src, Path dst)

所謂實際 Path ,指的就是 drawPath() 的繪制內容的輪廓,要算上線條寬度和設置的 PathEffect。

默認情況下(線條寬度為 0、沒有 PathEffect),原 Path 和實際 Path 是一樣的;而在線條寬度不為 0 (并且模式為 STROKE 模式或 FLL_AND_STROKE ),或者設置了 PathEffect 的時候,實際 Path 就和原 Path 不一樣了:

image.png

通過 getFillPath(src, dst) 方法就能獲取這個實際 Path。方法的參數里,src 是原 Path ,而 dst 就是實際 Path 的保存位置。 getFillPath(src, dst) 會計算出實際 Path,然后把結果保存在 dst 里。

2.8.2 getTextPath(String text, int start, int end, float x, float y, Path path) / getTextPath(char[] text, int index, int count, float x, float y, Path path)

文字的繪制,雖然是使用 Canvas.drawText() 方法,但其實在下層,文字信息全是被轉化成圖形,對圖形進行繪制的。 getTextPath() 方法,獲取的就是目標文字所對應的 Path 。這個就是所謂「文字的 Path」。

image.png

這兩個方法, getFillPath()getTextPath() ,就是獲取繪制的 Path 的方法。之所以把它們歸類到「效果」類方法,是因為它們主要是用于圖形和文字的裝飾效果的位置計算,比如自定義的下劃線效果

image

到此為止, Paint 的第二類方法——效果類,就也介紹完了。

3 drawText() 相關

Paint 有些設置是文字繪制相關的,即和 drawText() 相關的。
比如設置文字大小:


image.png

比如設置文字間隔:


image.png

比如設置各種文字效果:


image.png

除此之外,Paint 還有很多與文字繪制相關的設置或計算的方法,非常詳細。不過由于太詳細了,相關方法太多了(Paint 超過一半的方法都是 drawText() 相關的,算不算多?),如果放在這里講它們的話,內容會顯得有點過量。所以這一節我就不講它們了,把它們放在下一節里單獨講。

4 初始化類

這一類方法很簡單,它們是用來初始化 Paint 對象,或者是批量設置 Paint 的多個屬性的方法。

4.1 reset()

重置 Paint 的所有屬性為默認值。相當于重新 new 一個,不過性能當然高一些啦。

4.2 set(Paint src)

把 src 的所有屬性全部復制過來。相當于調用 src 所有的 get 方法,然后調用這個 Paint 的對應的 set 方法來設置它們。

4.3 setFlags(int flags)

批量設置 flags。相當于依次調用它們的 set 方法。例如:
paint.setFlags(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
這行代碼,和下面這兩行是等價的:

paint.setMaskFilter(new BlurMaskFilter(50, BlurMaskFilter.Blur.NORMAL));

...

canvas.drawBitmap(bitmap, 100, 100, paint);  

參考:

https://hencoder.com/ui-1-2/
注意:本文全部摘自上面的鏈接,本人只是對上面鏈接內容的自我抽取,符合本人閱讀

注意

最后再強調一遍:這期的內容沒必要全部背會,只要看懂、理解,記住有這么個東西就行了。以后在用到的時候,再拐回來翻一翻就行了。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,983評論 6 537
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,772評論 3 422
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,947評論 0 381
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,201評論 1 315
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,960評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,350評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,406評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,549評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,104評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,914評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,089評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,647評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,340評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,753評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,007評論 1 289
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,834評論 3 395
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,106評論 2 375

推薦閱讀更多精彩內容