建立自己的王国:Android 自定义封装View(1)

国际惯例,效果图


效果图1
效果图2

产品需求是做一个控制台,按照不同的状态显示(图片/数量),可以用XML很容易就实现,但是众所周知,XML解析比代码慢,还得嵌套很多View(ConstraintLayout->LinearLayout->TextView->View),这样,在View的Measure,Layout,Draw 过程中的时间超过了16毫秒。

步骤1

实现View的构造方法。

class HomeHierarchyView : ConstraintLayout {

    constructor(context: Context) : super(context) {
      //默认方法,初始化时。
    }
    constructor(context:Context,attributeSet: AttributeSet):super(context,attributeSet){
        //获取资源
    }

    constructor(cn: Context,attributeSet: AttributeSet,style:Int):super(cn,attributeSet,style){
        //api>21时被调用
    }
}

步骤2

定义attrs属性。

   <!--要跟定义的View类名要相同-->
    <declare-styleable name="HomeHierarchyView">
        <!--是否文字,true 文字,false 是图片.-->
        <attr name="texture" format="boolean"/>
            <!--待量房-->
        <attr name="waitingMeasure" format="string"/>
    </declare-styleable>

步骤3

获取自定义属性变量(在类下面)以及绑定(第二个构造体内)

/**
     *  文字还是背景图
     */
    var textOrImage:Boolean=false

    /**
     *  待量房
     */
    var waitingMeasure:String?=""
    val ta = cn.obtainStyledAttributes(attributeSet, R.styleable.HomeHierarchyView)
    textOrImage = ta.getBoolean(R.styleable.HomeHierarchyView_texture, true)
   //最后保证,能被回收。
    ta.recycle()

步骤4

构建View.
主体是ConstraintLayout,在里面代码的形式加载有关的子View.
主要结构:
*HomeHierarchyView(ConstraintLayout)
**LinearLayout
***TextView(显示数量/图片)
***TextView(固定文案)

在这里LinearLayout一直重复着添加到主View里。LinearLayou内包含两个TextView,
有ConstraintLayout特性,位置关系需要提供View ID,所以把View ID 统统写在values->ids.xml文件里。

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <item name="ll_1" type="id"/>
    <item name="tv_top1" type="id"/>
</resources>

从内到外,从下到上添加View到主体内。
其中一个TextView

val textView=TextView(cn)
textView.id=R.id.tv_top1 //这个要跟定义的对应。
  textView.layoutParams = LinearLayout.LayoutParams(-2, DisplayUtils.dip2px(30F)) 
//-2代表WRAP_CONTENT, -1 代表MATCH_PARENT, 由于高度值需要Pixel,所以需要转换。
        textView.textSize = 22F
        textView.typeface = Typeface.DEFAULT_BOLD
        textView.textColor = Color.parseColor("#0166B3")
        textView.gravity = Gravity.CENTER
        textView.text = ""

相似的添加标签代码

        val label=TextView(cn)
        label.layoutParams = LinearLayout.LayoutParams(-2, -2)
        label.text = "待量房"
        label.textSize = 12F
        label.textColor = Color.parseColor("#3e3e3e")
      //注:会被加载到LinearLayout里,所以LayoutParams 是LinearLayout的,请注意。
        (label.layoutParams as LinearLayout.LayoutParams).topMargin = DisplayUtils.dip2px(5F)

把这两个TextView加载LinearLayou里。

      //ll1
        val l1 = LinearLayout(cn)
        l1.id = R.id.ll_1
        //这里不需要写ConstraintLayout.LayoutParams, 因为主体就是。
        val l1Params = LayoutParams(0, DisplayUtils.dip2px(80F))
        l1.gravity = Gravity.CENTER
        l1.orientation = LinearLayout.VERTICAL
        l1Params.leftToLeft = R.id.cl_parent
        l1Params.topToTop = R.id.cl_parent
        l1Params.matchConstraintPercentWidth = 0.33F
       l1.layoutParams = l1Params
        l1.addView(textView)
        l1.addView(label)

最后添加到主体里。

 this.addView(l1)

步骤5

整理代码。
由于两个TextView 除了ID,text 不一样意外,其他属性都一样。那么可以封装个方法。

  private fun addTextView(textView: TextView, id: Int): TextView {
        textView.layoutParams = LinearLayout.LayoutParams(-2, DisplayUtils.dip2px(30F))
        textView.textSize = 22F
        textView.typeface = Typeface.DEFAULT_BOLD
        textView.textColor = Color.parseColor("#0166B3")
        textView.id = id
        textView.gravity = Gravity.CENTER
        textView.text = ""
        return textView
    }

//这个textView不需要设置ID, 文案写死就ok。
    private fun addLabel(s: String): TextView {
        val textView = TextView(cn)
        textView.layoutParams = LinearLayout.LayoutParams(-2, -2)
        textView.text = s
        textView.textSize = 12F
        textView.textColor = Color.parseColor("#3e3e3e")
        (textView.layoutParams as LinearLayout.LayoutParams).topMargin = DisplayUtils.dip2px(5F)
        return textView
    }

步骤6

在XML里使用。

            <HomeHierarchyView
                android:id="@+id/hhv_controlPanel"
                android:layout_width="match_parent"
                android:layout_height="@dimen/name_dp160"
                android:layout_marginStart="15dp"
                app:texture="false"
                app:waitingMeasure="0"
                android:layout_marginTop="@dimen/dp_40"
                android:layout_marginEnd="@dimen/name_dp15" />

在自定义View里添加个方法,为了不同的状态展示文案或图片。
用Texture属性来控制展示图片或文案。

   fun updateStatus() {
        if (textOrImage) {
            tv_top1.background = null
            tv_top1.text = waitingMeasure
        } else {
            tv_top1.text = ""
//资源用的是UI切图的SVG, 怎么倒入请百度(谷歌也行)
            tv_top1.background = ContextCompat.getDrawable(cn, R.drawable.ic_main_measure)
        }
    }

最后完整的代码:

HomeHierarchyView.kt

/**
 * @author hugo
 * @time 2021-5-24
 * Think twice, Code once.
 * 主页控制台
 */
class HomeHierarchyView : ConstraintLayout {

    /**
     *  文字还是背景图
     */
    var textOrImage: Boolean = false

    /**
     *  待量房
     */
    var waitingMeasure: String? = ""

    /**
     * 待预交底
     */
    var waitingSubmit: String? = ""

    /**
     *待签约
     */
    var waitingSign: String? = ""

    /**
     * 未开工
     */
    var statUnStart: String? = ""

    /**
     * 施工中
     */
    var statInProgress: String? = ""

    /**
     * 已竣工
     */
    var statOnFinished: String? = ""

    /**
     * 已结算
     */
    var statClearing: String? = ""

    /**
     * 文案1
     */
    lateinit var tv_top1: TextView
    lateinit var tv_top2: TextView
    lateinit var tv_top3: TextView
    lateinit var tv_top4: TextView
    lateinit var tv_top5: TextView
    lateinit var tv_top6: TextView
    lateinit var tv_top7: TextView

    var cn: Context = context

    constructor(context: Context) : super(context) {
        initView()
    }

    constructor(context: Context, attributeSet: AttributeSet) : super(context, attributeSet) {
        //获取资源
        initParams(attributeSet)
        initView()
    }

    constructor(cn: Context, attributeSet: AttributeSet, style: Int) : super(
        cn,
        attributeSet,
        style
    ) {
        //获取资源
        initParams(attributeSet)
        initView()
    }

    private fun initParams(attributeSet: AttributeSet) {
        val ta = cn.obtainStyledAttributes(attributeSet, R.styleable.HomeHierarchyView)
        textOrImage = ta.getBoolean(R.styleable.HomeHierarchyView_texture, true)
        waitingMeasure = ta.getString(R.styleable.HomeHierarchyView_waitingMeasure)
        waitingSubmit = ta.getString(R.styleable.HomeHierarchyView_waitingSubmit)
        waitingSign = ta.getString(R.styleable.HomeHierarchyView_waitingSign)
        statInProgress = ta.getString(R.styleable.HomeHierarchyView_statInProgress)
        statUnStart = ta.getString(R.styleable.HomeHierarchyView_statUnStart)
        statOnFinished = ta.getString(R.styleable.HomeHierarchyView_statOnFinished)
        statClearing = ta.getString(R.styleable.HomeHierarchyView_statClearing)
        ta.recycle()
    }

    private fun addTextView(textView: TextView, id: Int): TextView {
        textView.layoutParams = LinearLayout.LayoutParams(-2, DisplayUtils.dip2px(30F))
        textView.textSize = 22F
        textView.typeface = Typeface.DEFAULT_BOLD
        textView.textColor = Color.parseColor("#0166B3")
        textView.id = id
        textView.gravity = Gravity.CENTER
        textView.text = ""
        return textView
    }

    private fun addLabel(s: String): TextView {
        val textView = TextView(cn)
        textView.layoutParams = LinearLayout.LayoutParams(-2, -2)
        textView.text = s
        textView.textSize = 12F
        textView.textColor = Color.parseColor("#3e3e3e")
        (textView.layoutParams as LinearLayout.LayoutParams).topMargin = DisplayUtils.dip2px(5F)
        return textView
    }

    fun initView() {

        //初始化整个textview系列
        tv_top1 = TextView(cn)
        tv_top2 = TextView(cn)
        tv_top3 = TextView(cn)
        tv_top4 = TextView(cn)
        tv_top5 = TextView(cn)
        tv_top6 = TextView(cn)
        tv_top7 = TextView(cn)

        this.background = ContextCompat.getDrawable(cn, R.drawable.round_corder_w)
        val parentParams = ViewGroup.LayoutParams(-1, DisplayUtils.dip2px(160F))
        this.layoutParams = parentParams

        //ll1
        val l1 = LinearLayout(cn)
        l1.id = R.id.ll_1
        val l1Params = LayoutParams(0, DisplayUtils.dip2px(80F))
        l1.gravity = Gravity.CENTER
        l1.orientation = LinearLayout.VERTICAL
        l1Params.leftToLeft = R.id.cl_parent
        l1Params.topToTop = R.id.cl_parent
        l1Params.matchConstraintPercentWidth = 0.33F
        l1.addView(addTextView(tv_top1, R.id.tv_top1))
        l1.addView(addLabel("待量房"))
        l1.layoutParams = l1Params

        //ll2
        val l2 = LinearLayout(cn)
        l2.id = R.id.ll_2
        val l2Params = LayoutParams(0, DisplayUtils.dip2px(80F))
        l2.gravity = Gravity.CENTER
        l2.orientation = LinearLayout.VERTICAL
        l2Params.leftToRight = R.id.ll_1
        l2Params.topToTop = R.id.cl_parent
        l2Params.matchConstraintPercentWidth = 0.33F
        l2.addView(addTextView(tv_top2, R.id.tv_top2))
        l2.addView(addLabel("待预交底"))
        l2.layoutParams = l2Params


        //ll3
        val l3 = LinearLayout(cn)
        l3.id = R.id.ll_3
        val l3Params = LayoutParams(0, DisplayUtils.dip2px(80F))
        l3.gravity = Gravity.CENTER
        l3.orientation = LinearLayout.VERTICAL
        l3Params.leftToRight = R.id.ll_2
        l3Params.topToTop = R.id.cl_parent
        l3Params.matchConstraintPercentWidth = 0.33F
        l3.addView(addTextView(tv_top3, R.id.tv_top3))
        l3.addView(addLabel("待签约"))
        l3.layoutParams = l3Params

        //v1
        val v1 = View(cn)
        v1.id = R.id.v_1
        v1.layoutParams = LayoutParams(DisplayUtils.dip2px(1F), DisplayUtils.dip2px(60F))
        v1.backgroundColor = Color.parseColor("#EDEDED")
        (v1.layoutParams as LayoutParams).leftToRight = R.id.ll_1
        (v1.layoutParams as LayoutParams).topToTop = R.id.cl_parent
        (v1.layoutParams as LayoutParams).bottomToBottom = R.id.ll_1

        //v2
        val v2 = View(cn)
        v1.id = R.id.v_2
        v2.layoutParams = LayoutParams(DisplayUtils.dip2px(1F), DisplayUtils.dip2px(60F))
        v2.backgroundColor = Color.parseColor("#EDEDED")
        (v2.layoutParams as LayoutParams).leftToRight = R.id.ll_2
        (v2.layoutParams as LayoutParams).topToTop = R.id.cl_parent
        (v2.layoutParams as LayoutParams).bottomToBottom = R.id.ll_2

        //v3
        val v3 = View(cn)
        v1.id = R.id.v_3
        v3.layoutParams = LayoutParams(-1, DisplayUtils.dip2px(1F))
        v3.backgroundColor = Color.parseColor("#EDEDED")
        (v3.layoutParams as LayoutParams).marginEnd = DisplayUtils.dip2px(20F)
        (v3.layoutParams as LayoutParams).marginStart = DisplayUtils.dip2px(20F)
        (v3.layoutParams as LayoutParams).topToBottom = R.id.ll_1


        //v4
        val l4 = LinearLayout(cn)
        l4.id = R.id.ll_4
        val l4Params = LayoutParams(0, DisplayUtils.dip2px(80F))
        l4.gravity = Gravity.CENTER
        l4.orientation = LinearLayout.VERTICAL
        l4Params.leftToLeft = R.id.cl_parent
        l4Params.bottomToBottom = R.id.cl_parent
        l4Params.topToBottom = R.id.v_3
        l4Params.matchConstraintPercentWidth = 0.25F
        l4.addView(addTextView(tv_top4, R.id.tv_top4))
        l4.addView(addLabel("未开工"))
        l4.layoutParams = l4Params

        //ll5
        val l5 = LinearLayout(cn)
        l5.id = R.id.ll_5
        val l5Params = LayoutParams(0, DisplayUtils.dip2px(80F))
        l5.gravity = Gravity.CENTER
        l5.orientation = LinearLayout.VERTICAL
        l5Params.leftToRight = R.id.ll_4
        l5Params.bottomToBottom = R.id.cl_parent
        l5Params.topToBottom = R.id.v_3
        l5Params.matchConstraintPercentWidth = 0.25F
        l5.addView(addTextView(tv_top5, R.id.tv_top5))
        l5.addView(addLabel("施工中"))
        l5.layoutParams = l5Params

        //ll6
        val l6 = LinearLayout(cn)
        l6.id = R.id.ll_6
        val l6Params = LayoutParams(0, DisplayUtils.dip2px(80F))
        l6.gravity = Gravity.CENTER
        l6.orientation = LinearLayout.VERTICAL
        l6Params.bottomToBottom = R.id.cl_parent
        l6Params.leftToRight = R.id.ll_5
        l6Params.topToBottom = R.id.v_3
        l6Params.matchConstraintPercentWidth = 0.25F
        l6.addView(addTextView(tv_top6, R.id.tv_top6))
        l6.addView(addLabel("已竣工"))
        l6.layoutParams = l6Params

        //ll7
        val l7 = LinearLayout(cn)
        l7.id = R.id.ll_7
        val l7Params = LayoutParams(0, DisplayUtils.dip2px(80F))
        l7.gravity = Gravity.CENTER
        l7.orientation = LinearLayout.VERTICAL
        l7Params.bottomToBottom = R.id.cl_parent
        l7Params.leftToRight = R.id.ll_6
        l7Params.topToBottom = R.id.v_3
        l7Params.matchConstraintPercentWidth = 0.25F
        l7.addView(addTextView(tv_top7, R.id.tv_top7))
        l7.addView(addLabel("已结算"))
        l7.layoutParams = l7Params

        //v4
        val v4 = View(cn)
        v4.layoutParams = LayoutParams(DisplayUtils.dip2px(1F), DisplayUtils.dip2px(60F))
        v4.backgroundColor = Color.parseColor("#EDEDED")
        (v4.layoutParams as LayoutParams).leftToRight = R.id.ll_4
        (v4.layoutParams as LayoutParams).topToTop = R.id.ll_4
        (v4.layoutParams as LayoutParams).bottomToBottom = R.id.ll_4

        //v5
        val v5 = View(cn)
        v5.layoutParams = LayoutParams(DisplayUtils.dip2px(1F), DisplayUtils.dip2px(60F))
        v5.backgroundColor = Color.parseColor("#EDEDED")
        (v5.layoutParams as LayoutParams).leftToRight = R.id.ll_5
        (v5.layoutParams as LayoutParams).topToTop = R.id.ll_5
        (v5.layoutParams as LayoutParams).bottomToBottom = R.id.ll_5

        //v6
        val v6 = View(cn)
        v6.layoutParams = LayoutParams(DisplayUtils.dip2px(1F), DisplayUtils.dip2px(60F))
        v6.backgroundColor = Color.parseColor("#EDEDED")
        (v6.layoutParams as LayoutParams).leftToRight = R.id.ll_6
        (v6.layoutParams as LayoutParams).topToTop = R.id.ll_6
        (v6.layoutParams as LayoutParams).bottomToBottom = R.id.ll_6

        this.addView(l1)
        this.addView(l2)
        this.addView(l3)

        this.addView(v1)
        this.addView(v2)
        this.addView(v3)
        this.addView(v4)
        this.addView(v5)
        this.addView(v6)

        this.addView(l4)
        this.addView(l5)
        this.addView(l6)
        this.addView(l7)

        //注:切记,都加完了才去写入值的操作,不然会空指针。

        if (textOrImage) {
            tv_top1.background = null
            tv_top2.background = null
            tv_top3.background = null
            tv_top4.background = null
            tv_top5.background = null
            tv_top6.background = null
            tv_top7.background = null
            tv_top1.text = waitingMeasure
            tv_top2.text = waitingSubmit
            tv_top3.text = waitingSign
            tv_top4.text = statUnStart
            tv_top5.text = statInProgress
            tv_top6.text = statOnFinished
            tv_top7.text = statClearing
        } else {
            tv_top1.text = ""
            tv_top2.text = ""
            tv_top3.text = ""
            tv_top4.text = ""
            tv_top5.text = ""
            tv_top6.text = ""
            tv_top7.text = ""
            tv_top1.background = ContextCompat.getDrawable(cn, R.drawable.ic_main_measure)
            tv_top2.background = ContextCompat.getDrawable(cn, R.drawable.ic_main_send)
            tv_top3.background = ContextCompat.getDrawable(cn, R.drawable.ic_main_sign)
            tv_top4.background = ContextCompat.getDrawable(cn, R.drawable.ic_main_notstart)
            tv_top5.background = ContextCompat.getDrawable(cn, R.drawable.ic_main_doing)
            tv_top6.background = ContextCompat.getDrawable(cn, R.drawable.ic_main_done)
            tv_top7.background = ContextCompat.getDrawable(cn, R.drawable.ic_main_money)
        }

        //下面的是对应的点击事件
        l1.singleClick {
            cn.startActivity<NewProjectListActivity>("status" to "2,3,4")
        }
        //
        l2.singleClick {
            cn.startActivity<NewProjectListActivity>("status" to "5,6,7,8")
        }

        l3.singleClick {
            cn.startActivity<NewProjectListActivity>("status" to "9")
        }

        l4.singleClick {
            cn.startActivity<NewProjectListActivity>("status" to "10,11")
        }

        l5.singleClick {
            cn.startActivity<NewProjectListActivity>("status" to "12")
        }

        l6.singleClick {
            cn.startActivity<NewProjectListActivity>("status" to "13")
        }

        l7.singleClick {
            cn.startActivity<NewProjectListActivity>("status" to "14")
        }
    }

    fun updateStatus() {
        if (textOrImage) {
            tv_top1.background = null
            tv_top2.background = null
            tv_top3.background = null
            tv_top4.background = null
            tv_top5.background = null
            tv_top6.background = null
            tv_top7.background = null
            tv_top1.text = waitingMeasure
            tv_top2.text = waitingSubmit
            tv_top3.text = waitingSign
            tv_top4.text = statUnStart
            tv_top5.text = statInProgress
            tv_top6.text = statOnFinished
            tv_top7.text = statClearing
        } else {
            tv_top1.text = ""
            tv_top2.text = ""
            tv_top3.text = ""
            tv_top4.text = ""
            tv_top5.text = ""
            tv_top6.text = ""
            tv_top7.text = ""
            tv_top1.background = ContextCompat.getDrawable(cn, R.drawable.ic_main_measure)
            tv_top2.background = ContextCompat.getDrawable(cn, R.drawable.ic_main_send)
            tv_top3.background = ContextCompat.getDrawable(cn, R.drawable.ic_main_sign)
            tv_top4.background = ContextCompat.getDrawable(cn, R.drawable.ic_main_notstart)
            tv_top5.background = ContextCompat.getDrawable(cn, R.drawable.ic_main_doing)
            tv_top6.background = ContextCompat.getDrawable(cn, R.drawable.ic_main_done)
            tv_top7.background = ContextCompat.getDrawable(cn, R.drawable.ic_main_money)
        }
    }
}

attrs.xml

  <declare-styleable name="HomeHierarchyView">
        <!--是否文字,true 文字,false 是图片.-->
        <attr name="texture" format="boolean"/>
            <!--待量房-->
        <attr name="waitingMeasure" format="string"/>
<!--待预交底-->
        <attr name="waitingSubmit" format="string"/>
        <!--        待签约-->
        <attr name="waitingSign" format="string"/>
<!--        未开工-->
        <attr name="statUnStart" format="string"/>
<!--       施工中-->
        <attr name="statInProgress" format="string"/>
<!--        已竣工-->
        <attr name="statOnFinished" format="string"/>
<!--        已结算-->
        <attr name="statClearing" format="string"/>

    </declare-styleable>

布局内使用。

            <HomeHierarchyView
                android:id="@+id/hhv_controlPanel"
                android:layout_width="match_parent"
                android:layout_height="@dimen/name_dp160"
                android:layout_marginStart="15dp"
                app:texture="false"
                app:waitingSign="0"
                app:statClearing="0"
                app:statInProgress="0"
                app:statOnFinished="0"
                app:statUnStart="0"
                app:waitingMeasure="0"
                app:waitingSubmit="0"
                android:layout_marginTop="@dimen/dp_40"
                android:layout_marginEnd="@dimen/name_dp15" />

ids.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <item name="ll_1" type="id"/>
    <item name="ll_2" type="id"/>
    <item name="ll_3" type="id"/>
    <item name="ll_4" type="id"/>
    <item name="ll_5" type="id"/>
    <item name="ll_6" type="id"/>
    <item name="ll_7" type="id"/>

    <item name="tv_top1" type="id"/>
    <item name="tv_top2" type="id"/>
    <item name="tv_top3" type="id"/>
    <item name="tv_top4" type="id"/>
    <item name="tv_top5" type="id"/>
    <item name="tv_top6" type="id"/>
    <item name="tv_top7" type="id"/>

    <item name="v_1" type="id"/>
    <item name="v_2" type="id"/>
    <item name="v_3" type="id"/>
    <item name="v_4" type="id"/>
    <item name="v_5" type="id"/>
    <item name="v_6" type="id"/>
    <item name="v_7" type="id"/>

</resources>

动态更新

    //注:项目用的是Kotlin-Kapt,不需要findViewByID
                hhv_controlPanel.textOrImage = true
                    with(hhv_controlPanel) {
                        this.waitingMeasure = it.measure.toString()
                        this.waitingSubmit = it.prepaid.toString()
                        this.waitingSign = it.waitSigned.toString()
                        this.statUnStart = it.notStartWork.toString()
                        this.statInProgress = it.construction.toString()
                        this.statOnFinished = it.completion.toString()
                        this.statClearing = it.settlement.toString()
                }
                hhv_controlPanel.updateStatus()

最后DisplayUtils.java

public class DisplayUtils {
    /**
     * dp转px
     *
     * @param dipValue
     * @return
     */
    public static int dip2px(float dipValue) {
        final float scale = Resources.getSystem().getDisplayMetrics().density;
        return (int) (dipValue * scale + 0.5f);
    }


    public static int px2dip(int px) {
        final float scale = Resources.getSystem().getDisplayMetrics().density;
        return (int) (px / scale + 0.5f);
    }
}

注意事项:请不要在View里设置ID,如果被分配了ID,那么在xml里使用的时候的写的ID无效,从而报空指针。

完工!

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,723评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,003评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,512评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,825评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,874评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,841评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,812评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,582评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,033评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,309评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,450评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,158评论 5 341
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,789评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,409评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,609评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,440评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,357评论 2 352

推荐阅读更多精彩内容