自定義控件

自定義控件

在開(kāi)發(fā)中羹铅,開(kāi)發(fā)者常常會(huì)因?yàn)橄旅嫠膫€(gè)主要原因去自定義 View:

  1. 讓界面有特定的顯示風(fēng)格霞捡、效果坐漏;
  2. 讓控件具有特殊的交互方式;
  3. 優(yōu)化布局;
  4. 封裝赊琳;

1讓界面有特定的顯示風(fēng)格街夭、效果

在開(kāi)發(fā)中,Android SDK提供了很多控件慨畸,但有時(shí)莱坎,這些控件并不能滿足業(yè)務(wù)需求衣式。例如寸士,想要用一個(gè)折線圖來(lái)展示一組數(shù)據(jù),這時(shí)如果用系統(tǒng)提供的 View 就不能實(shí)現(xiàn)了碴卧,只能通過(guò)自定義 View 來(lái)實(shí)現(xiàn)弱卡。

2 讓控件具有特殊的交互方式

Android SDK提供的控件都有屬于它們自己的特定的交互方式,但有時(shí)住册,控件的默認(rèn)交互方式并不能滿足業(yè)務(wù)的需求婶博。例如,開(kāi)發(fā)者想要縮放 ImageView 中的圖片內(nèi)容荧飞,這時(shí)如果用系統(tǒng)提供的 ImageView 就不能實(shí)現(xiàn)了凡人,只能通過(guò)自定義 ImageView 來(lái)實(shí)現(xiàn)。

3 優(yōu)化布局

有時(shí)叹阔,有些布局如果用系統(tǒng)提供的控件實(shí)現(xiàn)起來(lái)相當(dāng)復(fù)雜挠轴,需要各種嵌套,雖然最終也能實(shí)現(xiàn)了想要的效果耳幢,但性能極差岸晦,此時(shí)就可以通過(guò)自定義 View 來(lái)減少嵌套層級(jí)、優(yōu)化布局睛藻。

4 封裝

有些控件可能在多個(gè)地方使用启上,如大多數(shù) App 里面的底部 Tab,頂部的標(biāo)題欄店印,像這樣的經(jīng)常被用到的控件就可以通過(guò)自定義 View 將它們封裝起來(lái)冈在,以便在多個(gè)地方使用。

自定義ViewGroup

以TopBar為例按摘,講解如何自定義ViewGroup來(lái)實(shí)現(xiàn)封裝的目的包券。可以使用xml文件院峡,也可以全部使用Java代碼兴使。

簡(jiǎn)單案例實(shí)現(xiàn)

首先說(shuō)明這個(gè)TopBar的功能。TopBar是作為放在屏幕最上方的標(biāo)題欄使用的照激。最主要的功能有兩個(gè)发魄,一個(gè)是左側(cè)的返回按鈕,一個(gè)是中心的文本,顯示當(dāng)前界面的標(biāo)題励幼。右側(cè)的功能按鈕或文字在不同的界面有不同的樣式汰寓,所以這里不管。

布局文件代碼

下面就是對(duì)應(yīng)的布局文件的代碼苹粟,

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="48dp"
    android:background="@color/white"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent">

    <ImageView
        android:id="@+id/btn_back"
        android:layout_width="34dp"
        android:layout_height="28dp"
        android:layout_marginStart="5dp"
        android:background="?attr/selectableItemBackgroundBorderless"
        android:padding="5dp"
        android:src="@drawable/ic_back_nav"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/tv_title"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="80dp"
        android:layout_marginEnd="80dp"
        android:gravity="center"
        android:lines="1"
        android:textColor="@color/text_333333"
        android:textSize="18sp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        tools:text="標(biāo)題" />

</androidx.constraintlayout.widget.ConstraintLayout>

下面就是簡(jiǎn)單的對(duì)應(yīng)的Java代碼有滑。

構(gòu)造方法的詳細(xì)說(shuō)明見(jiàn)下面的小節(jié),這里只要覆寫前兩個(gè)構(gòu)造方法即可嵌削。

public class TopBar extends ConstraintLayout {

    private ImageView mIvBack;

    public TopBar(@NonNull Context context) {
        this(context, null);
    }

    public TopBar(@NonNull Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        // 這行代碼的意思就是加載layout_top_bar這個(gè)xml布局文件到TopBar這個(gè)對(duì)象中毛好。
        // 就是將xml代碼與這個(gè)實(shí)例建立聯(lián)系。
        LayoutInflater.from(context).inflate(R.layout.layout_top_bar, this);
        // init方法是進(jìn)行一些初始化操作
        init(attrs);
    }

    /**
     * 一般都會(huì)有的方法苛秕,進(jìn)行初始化操作
     *
     * @param attrs 初始時(shí)可能用到的參數(shù)肌访,可以用來(lái)調(diào)用一些系統(tǒng)的方法
     */
    private void init(AttributeSet attrs) {
        // 統(tǒng)一處理設(shè)置左上角按鈕的返回點(diǎn)擊事件,如果是Activity艇劫,就調(diào)用onBackPressed方法
        mIvBack = findViewById(R.id.btn_back);
        mIvBack.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View view) {
                Context context = getContext();
                if (context instanceof Activity) {
                    ((Activity) context).onBackPressed();
                }
            }
        });
    }
}

到目前為止吼驶,這個(gè)TopBar其實(shí)只有一個(gè)功能,就是點(diǎn)擊了返回按鈕店煞,能夠返回上一個(gè)界面蟹演。

如何使用

和我們以前使用其他控件一樣使用就好,比如我想讓這個(gè)標(biāo)題欄顷蟀,放在某個(gè)界面的上面酒请,直接在xml文件中使用即可。

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".module.animation.TweenActivity">

    <cn.com.fkw.test.view.TopBar
        android:id="@+id/top_bar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
    
</androidx.constraintlayout.widget.ConstraintLayout>

在xml中的預(yù)覽效果:

標(biāo)題欄效果

到這里我們會(huì)發(fā)現(xiàn)一個(gè)問(wèn)題衩椒,就是標(biāo)題欄的標(biāo)題的文本似乎無(wú)法更改蚌父。

自定義屬性

其實(shí)就這樣使用,強(qiáng)行通過(guò)view.findViewById()的方式毛萌,也能修改自定義內(nèi)部的控件的屬性苟弛,但是不方便。我么可以通過(guò)自定義屬性的形式來(lái)更加方便的定制我們自己想要的屬性阁将。

比如我想在TopBar中添加一個(gè)叫centerText的屬性膏秫,來(lái)指定中間TextView顯示的文字。

實(shí)現(xiàn)方式

  1. 在values目錄下新建arrts.xml文件做盅。添加成為如下的代碼
<?xml version="1.0" encoding="utf-8"?>
<resources>

    <!-- 首先哪個(gè)類要自定義屬性 -->
    <declare-styleable name="TopBar">
        <!-- 內(nèi)部是一個(gè)個(gè)標(biāo)簽缤削,name是寫在布局文件中的控件的屬性的名字 format是屬性值的格式 -->
        <!-- 這個(gè)意思就是TopBar可以有一個(gè)叫centerText的屬性,值是字符串或者字符串引用 -->
        <attr name="centerText" format="string" />
    </declare-styleable>

</resources>
  1. 在init方法中獲取屬性值吹榴,然后設(shè)置進(jìn)TextView中
        // 通過(guò)attrs獲取xml中寫的屬性值亭敢。這里只有centerText一個(gè)屬性
        TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.TopBar);
        String centerText = typedArray.getString(R.styleable.TopBar_centerText);
        // 這里必須調(diào)用typedArray.recycle()方法
        typedArray.recycle();
        
        // 然后將屬性設(shè)置給TextView
        TextView tv_title = findViewById(R.id.tv_title);
        tv_title.setText(centerText);
  1. 使用。

    在布局文件中图筹,使用對(duì)應(yīng)的屬性帅刀。這樣運(yùn)行之后让腹,我們就能看到標(biāo)題欄的文字是微信兩個(gè)字了

<cn.com.fkw.test.view.TopBar
    android:id="@+id/top_bar"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:centerText="微信"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent" />

詳細(xì)解釋

  1. attrs.xml文件

在這個(gè)文件中都是自定義屬性相關(guān)的內(nèi)容。格式和案例一樣就行扣溺。需要額外說(shuō)明的是format的種類骇窍。

format 類型 說(shuō)明
string 字符串
integer 整數(shù)
float 小數(shù)
color 顏色
boolean 布爾值
dimension 尺寸
enum 枚舉 在幾個(gè)選項(xiàng)中選擇一個(gè)
reference 參考的某一資源ID
flag 位或運(yùn)算 在幾個(gè)選項(xiàng)中選擇多個(gè)
fraction 百分?jǐn)?shù)

位或運(yùn)算

<!-- 聲明 -->
<attr name="gravity">
    <flag name="top" value="0x30" />
    <flag name="bottom" value="0x50" />
    <flag name="left" value="0x03" />
    <flag name="right" value="0x05" />
    <flag name="center_vertical" value="0x10" />
</attr>

<!-- 使用 -->
<TextView android:gravity="bottom|left"/>

通過(guò)代碼設(shè)置屬性

在init方法中,findViewById找到控件,將控件設(shè)置為成員變量,然后提供一些應(yīng)有的方法即可杭跪。

View的構(gòu)造方法

一共有4個(gè),我們一般情況下會(huì)復(fù)寫前兩個(gè)構(gòu)造方法挑随,且互相調(diào)用

/**
 * 一般情況下我們都是覆寫 前兩個(gè)構(gòu)造方法
 */
public class CircleView extends View {
    /**
     * 在Java中創(chuàng)建一個(gè)新的CircleView對(duì)象時(shí)使用。
     * 如果你需要在Java中新建這個(gè)View,必須覆寫這個(gè)構(gòu)造方法
     *
     * @param context
     */
    public CircleView(Context context) {
        // 這里直接調(diào)用參數(shù)較多的方法,保證自定義View的某些初始化動(dòng)作一定執(zhí)行
        this(context, null);
    }

    /**
     * 在xml中添加的控件蛔钙,被渲染成View時(shí),會(huì)調(diào)用這個(gè)方法荠医。
     * 所以如果想要在xml中使用這個(gè)控件,這個(gè)構(gòu)造方法必須復(fù)寫桑涎。
     *
     * @param context
     * @param attrs   如果有自定義的屬性彬向,需要用到這個(gè)參數(shù)
     */
    public CircleView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    /**
     * 與主題相關(guān),如果你不需要當(dāng)前的View隨主題的變化而有更改攻冷,就不需要復(fù)寫這個(gè)構(gòu)造方法娃胆。
     */
    public CircleView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    /**
     * 與主題相關(guān),如果你不需要當(dāng)前的View隨主題的變化而有更改等曼,就不需要復(fù)寫這個(gè)構(gòu)造方法里烦。
     */
    public CircleView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }
}

自定義View

用一個(gè)最簡(jiǎn)單的案例演示如何繼承View,實(shí)現(xiàn)自定義View禁谦。

畫一個(gè)圓

Android的View的繪制流程

我們一般需要處理三個(gè)方法胁黑。View顯示在屏幕上,也是經(jīng)歷三個(gè)過(guò)程州泊,測(cè)量丧蘸、布局和繪制。

測(cè)量:onMeasure

在 View 的測(cè)量階段會(huì)執(zhí)行兩個(gè)方法(在測(cè)量階段遥皂,View 的父 View 會(huì)通過(guò)調(diào)用 View 的 measure() 方法將父 View 對(duì) View 尺寸要求傳進(jìn)來(lái)力喷。緊接著 View 的 measure() 方法會(huì)做一些前置和優(yōu)化工作,然后調(diào)用 View 的 onMeasure() 方法演训,并通過(guò) onMeasure() 方法將父 View 對(duì) View 的尺寸要求傳入弟孟。在自定義 View 中,只有需要修改 View 的尺寸的時(shí)候才需要重寫 onMeasure() 方法样悟。在 onMeasure() 方法中根據(jù)業(yè)務(wù)需求進(jìn)行相應(yīng)的邏輯處理拂募,并在最后通過(guò)調(diào)用 setMeasuredDimension() 方法告知父 View 自己的期望尺寸)。

onMeasure() 計(jì)算 View 期望尺寸方法如下:

  1. 參考父 View 的對(duì) View 的尺寸要求和實(shí)際業(yè)務(wù)需求計(jì)算出 View 的期望尺寸:
  • 解析 widthMeasureSpec;
  • 解析 heightMeasureSpec没讲;
  • 將「根據(jù)實(shí)際業(yè)務(wù)需求計(jì)算出 View 的尺寸」根據(jù)「父 View 的對(duì) View 的尺寸要求」進(jìn)行相應(yīng)的>修正得出 View 的期望尺寸(通過(guò)調(diào)用 resolveSize() 方法)眯娱;
  1. 通過(guò) setMeasuredDimension() 保存 View 的期望尺寸(實(shí)際上是通過(guò) setMeasuredDimension() 告知父 View 自己的期望尺寸);

具體的測(cè)量過(guò)程很復(fù)雜。不再贅述爬凑。想要了解徙缴,請(qǐng)看Android自定義View的測(cè)量過(guò)程詳解。我們需要了解的知識(shí)有下面這些:

MeasureSpec,有人叫它測(cè)量規(guī)格嘁信,我更喜歡把它描述成為測(cè)量過(guò)程中必不可少的工具——尺子于样。
這個(gè)尺子有兩種用法,橫著用就叫做widthMeasureSpec潘靖,用來(lái)測(cè)量寬度穿剖,豎著用就叫做heightMeasureSpec,用來(lái)測(cè)量高度的,不管你的自定義View是什么千奇百怪的形狀卦溢,他都是要放在一個(gè)矩形中進(jìn)行包裹展示的糊余,那么為什么會(huì)有這兩個(gè)測(cè)量方式也就不難理解了。
這個(gè)尺子有兩個(gè)重要的功能单寂,第一個(gè)功能自然是測(cè)量值了(Size)贬芥,第二個(gè)功能是測(cè)量的模式(Mode),這兩個(gè)參數(shù)通過(guò)二進(jìn)制將其打包成一個(gè)int(32位)值來(lái)減少對(duì)內(nèi)存的分配,其高2位(31,32位)存放的是測(cè)量模式宣决,而低30位則存儲(chǔ)的是其測(cè)量值蘸劈。

測(cè)量模式(specMode)

測(cè)量模式分為三種:

  • UNSPECIFIED模式:本質(zhì)就是不限制模式,父視圖不對(duì)子View進(jìn)行任何約束尊沸,View想要多大要多大威沫,想要多長(zhǎng)要多長(zhǎng),這個(gè)在我們寫自定義View中的時(shí)候非常少見(jiàn)洼专,一般都是系統(tǒng)內(nèi)部在設(shè)置ListView或者是ScrollView的時(shí)候才會(huì)用到棒掠。
  • EXACTLY模式:該模式其實(shí)對(duì)應(yīng)的場(chǎng)景就是match_parent或者是一個(gè)具體的數(shù)據(jù)(50dp或80px),父視圖為子View指定一個(gè)確切的大小壶熏,無(wú)論子View的值設(shè)置多大句柠,都不能超出父視圖的范圍。
  • AT_MOST模式:這個(gè)模式對(duì)應(yīng)的場(chǎng)景就是wrap_content棒假,其內(nèi)容就是父視圖給子View設(shè)置一個(gè)最大尺寸溯职,子View只要不超過(guò)這個(gè)尺寸即可。
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    
    // 獲取寬
    // 方式一:
    int width1 = MeasureSpec.getSize(widthMeasureSpec);
    // 方式二:
    int width2 = getMeasuredWidth();

    // 獲取寬
    // 方式一:
    int height1 = MeasureSpec.getSize(heightMeasureSpec);
    // 方式二:
    int height2 = getMeasuredHeight();
    
    // 上面補(bǔ)充自己的邏輯帽哑,更改控件的寬高谜酒,最后記得調(diào)用 setMeasuredDimension 將測(cè)量的寬高設(shè)置回去
    setMeasuredDimension(100,100);
}
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int width = resolveSize(getSuggestedMinimumWidth(), widthMeasureSpec);
        int height = resolveSize(getSuggestedMinimumHeight(), heightMeasureSpec);
        setMeasuredDimension(width, height);
    }


    public static int resolveSize(int size, int measureSpec) {
        int result = size;

        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize = MeasureSpec.getSize(measureSpec);

        switch (specMode) {
            case MeasureSpec.UNSPECIFIED:
                result = size;
                break;
            case MeasureSpec.AT_MOST:
            case MeasureSpec.EXACTLY:
                result = specSize;
                break;
        }
        return result;

    }

布局:onLayout

layout() : 保存 View 的實(shí)際尺寸。調(diào)用 setFrame() 方法保存 View 的實(shí)際尺寸妻枕,調(diào)用 onSizeChanged() 通知開(kāi)發(fā)者 View 的尺寸更改了僻族,并最終會(huì)調(diào)用 onLayout() 方法讓子 View 布局(如果有子 View 的話粘驰。因?yàn)樽远x View 中沒(méi)有子 View,所以自定義 View 的 onLayout() 方法是一個(gè)空實(shí)現(xiàn))述么;

onLayout() : 空實(shí)現(xiàn)蝌数,什么也不做,因?yàn)樗鼪](méi)有子 View度秘。如果是 ViewGroup 的話顶伞,在 onLayout() 方法中需要調(diào)用子 View 的 layout() 方法,將子 View 的實(shí)際尺寸傳給它們剑梳,讓子 View 保存自己的實(shí)際尺寸唆貌。因此,在自定義 View 中垢乙,不需重寫此方法锨咙,在自定義 ViewGroup 中,需重寫此方法追逮。

繪制:onDraw

在 View 的繪制階段會(huì)執(zhí)行一個(gè)方法——draw()酪刀,draw() 是繪制階段的總調(diào)度方法,在其中會(huì)調(diào)用繪制背景的方法 drawBackground()羊壹、繪制主體的方法 onDraw()蓖宦、繪制子 View 的方法 dispatchDraw() 和 繪制前景的方法 onDrawForeground():

  • draw()

draw() : 繪制階段的總調(diào)度方法,在其中會(huì)調(diào)用繪制背景的方法 drawBackground()油猫、繪制主體的方法 onDraw()、繪制子 View 的方法 dispatchDraw() 和 繪制前景的方法 onDrawForeground()柠偶;

drawBackground() : 繪制背景的方法情妖,不能重寫,只能通過(guò) xml 布局文件或者 setBackground() 來(lái)設(shè)置或修改背景诱担;

onDraw() : 繪制 View 主體內(nèi)容的方法毡证,通常情況下,在自定義 View 的時(shí)候蔫仙,只用實(shí)現(xiàn)該方法即可料睛;

dispatchDraw() : 繪制子 View 的方法。同 onLayout() 方法一樣摇邦,在自定義 View 中它是空實(shí)現(xiàn)恤煞,什么也不做。但在自定義 ViewGroup 中施籍,它會(huì)調(diào)用 ViewGroup.drawChild() 方法居扒,在 ViewGroup.drawChild() 方法中又會(huì)調(diào)用每一個(gè)子 View 的 View.draw() 讓子 View 進(jìn)行自我繪制;

onDrawForeground() : 繪制 View 前景的方法丑慎,也就是說(shuō)喜喂,想要在主體內(nèi)容之上繪制東西的時(shí)候就可以在該方法中實(shí)現(xiàn)瓤摧。

注意:
Android 里面的繪制都是按順序的,先繪制的內(nèi)容會(huì)被后繪制的蓋住玉吁。

Paint和Canvas

參考資料

android 自定義屬性

android 自定義屬性值類型的詳解

Android自定義View|溫故而知新

Android自定義View的測(cè)量過(guò)程詳解

詳解安卓MeasureSpec及其和match_parent照弥、wrap_content的關(guān)系

match_parent、wrap_parent进副、具體值 和 MeasureSpec 類中 mode 的對(duì)應(yīng)關(guān)系

Android BitmapShader 實(shí)戰(zhàn) 實(shí)現(xiàn)圓形这揣、圓角圖片——hongyang

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市敢会,隨后出現(xiàn)的幾起案子曾沈,更是在濱河造成了極大的恐慌,老刑警劉巖鸥昏,帶你破解...
    沈念sama閱讀 207,113評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件塞俱,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡吏垮,警方通過(guò)查閱死者的電腦和手機(jī)障涯,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)膳汪,“玉大人唯蝶,你說(shuō)我怎么就攤上這事∫潘裕” “怎么了粘我?”我有些...
    開(kāi)封第一講書人閱讀 153,340評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)痹换。 經(jīng)常有香客問(wèn)我征字,道長(zhǎng),這世上最難降的妖魔是什么娇豫? 我笑而不...
    開(kāi)封第一講書人閱讀 55,449評(píng)論 1 279
  • 正文 為了忘掉前任匙姜,我火速辦了婚禮,結(jié)果婚禮上冯痢,老公的妹妹穿的比我還像新娘氮昧。我一直安慰自己,他們只是感情好浦楣,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布袖肥。 她就那樣靜靜地躺著,像睡著了一般椒振。 火紅的嫁衣襯著肌膚如雪昭伸。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書人閱讀 49,166評(píng)論 1 284
  • 那天澎迎,我揣著相機(jī)與錄音庐杨,去河邊找鬼选调。 笑死,一個(gè)胖子當(dāng)著我的面吹牛灵份,可吹牛的內(nèi)容都是我干的仁堪。 我是一名探鬼主播,決...
    沈念sama閱讀 38,442評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼填渠,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼弦聂!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起氛什,我...
    開(kāi)封第一講書人閱讀 37,105評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤莺葫,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后枪眉,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體捺檬,經(jīng)...
    沈念sama閱讀 43,601評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評(píng)論 2 325
  • 正文 我和宋清朗相戀三年贸铜,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了堡纬。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,161評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡蒿秦,死狀恐怖烤镐,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情棍鳖,我是刑警寧澤炮叶,帶...
    沈念sama閱讀 33,792評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站渡处,受9級(jí)特大地震影響悴灵,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜骂蓖,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望川尖。 院中可真熱鬧登下,春花似錦、人聲如沸叮喳。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 30,352評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)馍悟。三九已至畔濒,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間锣咒,已是汗流浹背侵状。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 31,584評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工赞弥, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人趣兄。 一個(gè)月前我還...
    沈念sama閱讀 45,618評(píng)論 2 355
  • 正文 我出身青樓绽左,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親艇潭。 傳聞我的和親對(duì)象是個(gè)殘疾皇子拼窥,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評(píng)論 2 344

推薦閱讀更多精彩內(nèi)容