android自定義控件(附例子)

最近閑的很,如是寫下了本篇博客土全。

自定義控件基本步驟如下捎琐,反正基本上我覺得按我這個(gè)套路來寫会涎,基本上都能很快上手。

推薦按順序閱讀瑞凑。末秃。
第一部分,構(gòu)造函數(shù)籽御,我相信這個(gè)大家都應(yīng)該是明白的练慕。初始化和自定義屬性無關(guān)必要的成員變量,mDensity是屏幕密度技掏,將dp轉(zhuǎn)px時(shí)要用到铃将。

/**
     * 第一部分 構(gòu)造函數(shù)  所有的自定義view都可以用下面這段代碼直接copy改下名字就行
     **/

    //當(dāng)不需要使用xml聲明或者不需要使用inflate動(dòng)態(tài)加載時(shí)候,實(shí)現(xiàn)此構(gòu)造函數(shù)即可
    public RulerView(Context context) {
        super(context, null);
    }

    //當(dāng)需要在xml中聲明此控件哑梳,則需要實(shí)現(xiàn)此構(gòu)造函數(shù)劲阎。并且在構(gòu)造函數(shù)中把自定義的屬性與控件的數(shù)據(jù)成員連接起來。
    public RulerView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    //接受一個(gè)style資源
    public RulerView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        //所有的構(gòu)造函數(shù)都會(huì)匯總到這一步,API21的會(huì)走到一個(gè)包含4個(gè)參數(shù)的構(gòu)造函數(shù)涧衙。
        mContext = context;
        mDensity = mContext.getResources().getDisplayMetrics().density;
        initRulerView(attrs);
    }

第二部分哪工,獲取自定義屬性奥此,初始化需要獲取自定義屬性之后的畫筆等等之類的成員變量弧哎。
值得注意的是,這個(gè)地方獲取屬性值的時(shí)候有2種寫法稚虎,一種是我注釋了的撤嫩,一種是沒有注釋的,寫法的區(qū)別是:我注釋過的那段代碼蠢终,如果屬性在使用控件的時(shí)候沒有在xml中使用的話序攘,賦值的方法是不走的。那么就沒有默認(rèn)值寻拂。程奠。我沒注釋的那種寫法是肯定會(huì)走,獲取不到就給默認(rèn)值祭钉。推薦第二種瞄沙,以免出錯(cuò)。

<declare-styleable name="RulerView">
此處的name我看過很多博客慌核,都要么沒說距境,要么說這個(gè)名字隨便寫。其實(shí)不對垮卓,這個(gè)名字垫桂,
代表了在這個(gè)declare-styleable之間的屬性只能在RulerView中使用,其他的空間是看
不到這個(gè)屬性的粟按,同學(xué)們可以去試一下

關(guān)于format值的一些說明

1诬滩,reference :通過@dimen  @color  @drawable @layout 等等獲取屬性值

2叫搁,color : 顏色值  "#ff0" "#ff0000"  "#0ff" "#00ff00000"

3乘寒,boolean :布爾值  true false

4, dimension : 尺寸 50dp  50px 50sp

5,float :浮點(diǎn)值  0.5

6,integer : 整型值  30

7禾锤,string : 字符串  abs

8,fraction :百分?jǐn)?shù) 25%

9科雳,enum :枚舉

10辩越,flag :位或運(yùn)算
 /**
     * 第二部分,獲取自定義屬性
     *
     * @param attrs
     */
    private void initRulerView(AttributeSet attrs) {


        if (null != attrs) {
            TypedArray ta = mContext.obtainStyledAttributes(attrs, R.styleable.RulerView);
//            int count = ta.getIndexCount();
//
//            for (int i = 0; i < count; i++) {
//                int attr = ta.getIndex(i);
//                switch (attr) {
//                    case R.styleable.RulerView_cursorColor:
//                        mCursorColor = ta.getColor(attr, DEFAULT_CURSOR_COLOR);
//                        break;
//
//                }
//            }

            mBorderWidth = ta.getDimension(R.styleable.RulerView_borderWidth, CustomViewUtil.dp2px(mContext, BORDER_WIDTH));
            mCornerRadius = ta.getDimension(R.styleable.RulerView_cornerRadius, CustomViewUtil.dp2px(mContext, CORNER_RADIUS));
            mBorderColor = ta.getColor(R.styleable.RulerView_borderColor, BORDER_COLOR);
            mCursorWidth = ta.getDimension(R.styleable.RulerView_cursorWidth, CustomViewUtil.dp2px(mContext, DEFAULT_CURSOR_WIDTH));

            if (ta.getDrawable(R.styleable.RulerView_cursorDrawable) == null) {
                mCursorColor = ta.getColor(R.styleable.RulerView_cursorColor, DEFAULT_CURSOR_COLOR);
            } else {
                mCursorDrawable = ta.getDrawable(R.styleable.RulerView_cursorDrawable);
            }

            mCalibrationTailsLength = ta.getDimension(R.styleable.RulerView_calibrationTailsLength, CustomViewUtil.dp2px(mContext, CALIBRATION_TAILS_LENGTH));
            mCalibrationTailsWidth = ta.getDimension(R.styleable.RulerView_calibrationTailsWidth, CustomViewUtil.dp2px(mContext, CALIBRATION_TAILS_WIDTH));
            mCalibrationTailsColor = ta.getColor(R.styleable.RulerView_calibrationTailsColor, CALIBRATION_TAILS_COLOR);
            mCalibrationTailsDistance = ta.getDimension(R.styleable.RulerView_calibrationTailsDistance, CustomViewUtil.dp2px(mContext, CALIBRATION_TAILS_DISTANCE));

            mImportantTailsLength = ta.getDimension(R.styleable.RulerView_importantTailsLength, CustomViewUtil.dp2px(mContext, IMPORTANT_TAILS_LENGTH));
            mImportantTailsWidth = ta.getDimension(R.styleable.RulerView_importantTailsWidth, CustomViewUtil.dp2px(mContext, IMPORTANT_TAILS_WIDTH));
            mImportantTailsColor = ta.getColor(R.styleable.RulerView_importantTailsColor, IMPORTANT_TAILS_COLOR);
            mImportantTailsGap = ta.getInteger(R.styleable.RulerView_importantTailsGap, IMPORTANT_TAILS_GAP);

            maxValue = ta.getInteger(R.styleable.RulerView_maxValue, MAX_VALUE);
            minValue = ta.getInteger(R.styleable.RulerView_minValue, MIN_VALUE);
            currentValue = ta.getInteger(R.styleable.RulerView_currentValue, DEFAULT_VALUE);

            mValueDistanceImportantTails = ta.getDimension(R.styleable.RulerView_valueDistanceImportantTails, CustomViewUtil.dp2px(mContext, VALUE_DISTANCE_IMPORTANT_TAILS));
            mValueColor = ta.getColor(R.styleable.RulerView_valueColor, VALUE_COLOR);
            mValueSize = ta.getDimension(R.styleable.RulerView_valueSize, CustomViewUtil.sp2px(mContext, VALUE_SIZE));

            dampNumber = ta.getFloat(R.styleable.RulerView_dampNumber, DAMP_NUMBER);

            ta.recycle();

        }

        if (mCalibrationTailsDistance <= Math.max(mImportantTailsWidth, mCalibrationTailsWidth)) {
            throw new IllegalArgumentException("mCalibrationTailsDistance must bigger than Math.max(mImportantTailsWidth,mCalibrationTailsWidth)");
        }

        mCalibrationTailsPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
        mImportantTailsPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mCursorPaint = new Paint(Paint.ANTI_ALIAS_FLAG);


        mCalibrationTailsPaint.setColor(mCalibrationTailsColor);
        mCalibrationTailsPaint.setStrokeWidth(mCalibrationTailsWidth);

        mImportantTailsPaint.setColor(mImportantTailsColor);
        mImportantTailsPaint.setStrokeWidth(mImportantTailsWidth);

        mCursorPaint.setColor(mCursorColor);
        mCursorPaint.setStrokeWidth(mCursorWidth);

        mTextPaint.setTextSize(mValueSize);
        mTextPaint.setColor(mValueColor);
        mTextPaint.setStyle(Paint.Style.FILL);

        mTouchSlop = ViewConfiguration.get(mContext).getScaledPagingTouchSlop();
        mScroller = new Scroller(mContext);
        mMinVelocity = ViewConfiguration.get(getContext()).getScaledMinimumFlingVelocity();

        rectOfBackGround = new Rect();
        setColors(colors);
    }

第三部分姑裂,測量寬高馋袜,有點(diǎn)同學(xué)這一部分很是艱難,但是不用怕舶斧,賦值一下代碼欣鳖,重寫其中的2個(gè)方法就行了

    /**
     * 第三部分,測量控件大小,一般情況下茴厉,前面的方法可以直接copy過去泽台,只需要自己重寫measureWrapWitdh,measureWrapHeight
     *
     * @param widthMeasureSpec
     * @param heightMeasureSpec
     */
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        //這一部分,可寫可不寫矾缓,如若你不想重寫控件大小怀酷,直接用super就行。如果需要重寫的話套路也很簡單
//        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int measureWidth = measureWitdh(widthMeasureSpec);
        int measureHeight = measureHeight(heightMeasureSpec);
        setMeasuredDimension(measureWidth, measureHeight);
    }

    private int measureWitdh(int widthMeasureSpec) {
        int result = 0;
        int specMode = MeasureSpec.getMode(widthMeasureSpec);
        int specSize = MeasureSpec.getSize(widthMeasureSpec);

        switch (specMode) {
            case MeasureSpec.UNSPECIFIED:
                result = measureWrapWitdh();
                break;
            case MeasureSpec.AT_MOST:
                result = Math.min(measureWrapWitdh(), specSize);
                break;
            case MeasureSpec.EXACTLY:
                result = specSize;
                break;
        }
        return result;

    }

    private int measureWrapWitdh() {
        //測量wrapContent時(shí)寬度,這個(gè)請根據(jù)自己的需求自己計(jì)算嗜闻,我這里默認(rèn)300dp
        return CustomViewUtil.dp2px(mContext, 300);
    }

    private int measureHeight(int heightMeasureSpec) {
        int result = 0;
        int specMode = MeasureSpec.getMode(heightMeasureSpec);
        int specSize = MeasureSpec.getSize(heightMeasureSpec);

        switch (specMode) {
            case MeasureSpec.UNSPECIFIED:
                result = measureWrapHeight();
                break;
            case MeasureSpec.AT_MOST:
                result = Math.min(measureWrapHeight(), specSize);
                break;
            case MeasureSpec.EXACTLY:
                result = specSize;
                break;
        }
        return result;
    }

    private int measureWrapHeight() {
        //測量wrapContent時(shí)高度,這個(gè)請根據(jù)自己的需求自己計(jì)算蜕依,我這里默認(rèn)50dp
        return CustomViewUtil.dp2px(mContext, 50);
    }

第四部分,在控件發(fā)生變化的時(shí)候 重新賦值寬高琉雳,以及重新為一些需要在控件大小發(fā)生變化時(shí)需要充值賦值的屬性賦值样眠。

 /**
     * 第四部分,在size發(fā)生變化時(shí)翠肘,重新賦值寬高
     *
     * @param w
     * @param h
     * @param oldw
     * @param oldh
     */
    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mWidth = w;
        mHeight = h;
        LayerDrawable background = (LayerDrawable) getBackground();
        Drawable drawable = background.getDrawable(0);
        drawable.getPadding(rectOfBackGround);
    }

第五部分檐束,屬于核心以及難點(diǎn)吧,具體實(shí)現(xiàn)我就不說了束倍,自己去看代碼被丧。其實(shí)代碼也沒啥看的,就是通過計(jì)算位置繪制而已肌幽。但是這個(gè)計(jì)算位置是很精細(xì)晚碾,很容易出錯(cuò)的地方,得加倍小心

 /**
     * 第五部分喂急,繪制格嘁,繪制時(shí),建議分層重下往上繪制
     *
     * @param canvas
     */
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        drawCalibrationTails(canvas);
        drawCursor(canvas);
    }

    private void drawCalibrationTails(Canvas canvas) {

        //計(jì)算需要畫多少刻度線
        //1廊移,先計(jì)算可畫刻度線區(qū)域的總長度


        int totalLengthCanDrawCalibrationTails = (int) (mWidth - rectOfBackGround.left - rectOfBackGround.right - 2 * mBorderWidth);
        int count = (int) (totalLengthCanDrawCalibrationTails / mCalibrationTailsWidth);


        canvas.save();


        for (int i = 0; i < count; i++) {
            int offset = i % 2 == 0 ? (i + 1) / 2 : -(i + 1) / 2;

            if (currentValue + offset > maxValue || currentValue + offset < minValue) {
                continue;
            } else {

                //畫刻度線

                if (i == 0) {
                    if ((currentValue + offset) % mImportantTailsGap == 0) {
                        canvas.drawLine(mWidth / 2, rectOfBackGround.top + mBorderWidth, mWidth / 2, rectOfBackGround.top + mBorderWidth + mImportantTailsLength, mImportantTailsPaint);
                    } else {
                        canvas.drawLine(mWidth / 2, rectOfBackGround.top + mBorderWidth, mWidth / 2, rectOfBackGround.top + mBorderWidth + mCalibrationTailsLength, mCalibrationTailsPaint);
                    }
                } else if (i % 2 == 0) {

                    if (mWidth / 2 + ((i + 1) / 2) * mCalibrationTailsDistance >= mWidth - rectOfBackGround.right - mBorderWidth) {
                        continue;
                    }

                    if ((currentValue + offset) % mImportantTailsGap == 0) {
                        canvas.drawLine(mWidth / 2 + ((i + 1) / 2) * mCalibrationTailsDistance, rectOfBackGround.top + mBorderWidth, mWidth / 2 + ((i + 1) / 2) * mCalibrationTailsDistance, rectOfBackGround.top + mBorderWidth + mImportantTailsLength, mImportantTailsPaint);
                    } else {
                        canvas.drawLine(mWidth / 2 + ((i + 1) / 2) * mCalibrationTailsDistance, rectOfBackGround.top + mBorderWidth, mWidth / 2 + ((i + 1) / 2) * mCalibrationTailsDistance, rectOfBackGround.top + mBorderWidth + mCalibrationTailsLength, mCalibrationTailsPaint);
                    }

                } else {
                    if (mWidth / 2 - ((i + 1) / 2) * mCalibrationTailsDistance <= rectOfBackGround.left + mBorderWidth) {
                        continue;
                    }

                    if ((currentValue + offset) % mImportantTailsGap == 0) {
                        canvas.drawLine(mWidth / 2 - ((i + 1) / 2) * mCalibrationTailsDistance, rectOfBackGround.top + mBorderWidth, mWidth / 2 - ((i + 1) / 2) * mCalibrationTailsDistance, rectOfBackGround.top + mBorderWidth + mImportantTailsLength, mImportantTailsPaint);
                    } else {
                        canvas.drawLine(mWidth / 2 - ((i + 1) / 2) * mCalibrationTailsDistance, rectOfBackGround.top + mBorderWidth, mWidth / 2 - ((i + 1) / 2) * mCalibrationTailsDistance, rectOfBackGround.top + mBorderWidth + mCalibrationTailsLength, mCalibrationTailsPaint);
                    }
                }

                //畫刻度線下面的文字
                if ((currentValue + offset) % mImportantTailsGap == 0) {
                    if ((mWidth / 2 + offset * mCalibrationTailsDistance + mTextPaint.measureText(String.valueOf(currentValue + offset)) / 2) > mWidth - rectOfBackGround.right - mBorderWidth) {
                        continue;
                    }

                    if ((mWidth / 2 + offset * mCalibrationTailsDistance - mTextPaint.measureText(String.valueOf(currentValue + offset)) / 2) < rectOfBackGround.left + mBorderWidth) {
                        continue;
                    }

                    canvas.drawText(String.valueOf(currentValue + offset), mWidth / 2 + offset * mCalibrationTailsDistance - mTextPaint.measureText(String.valueOf(currentValue + offset)) / 2, CustomViewUtil.drawTextFromTop(rectOfBackGround.top + mBorderWidth + Math.max(mCalibrationTailsLength, mImportantTailsLength) + mValueDistanceImportantTails, mTextPaint), mTextPaint);
                }
            }

        }


        canvas.restore();
    }

    private void drawCursor(Canvas canvas) {
        canvas.save();
        if (mCursorDrawable != null) {
            mCursorDrawable.setBounds(mWidth / 2 - mCursorDrawable.getIntrinsicWidth() / 2, (int) (rectOfBackGround.top + mBorderWidth), mWidth / 2 + mCursorDrawable.getIntrinsicWidth() / 2, (int) (rectOfBackGround.top + mBorderWidth + mCursorDrawable.getIntrinsicHeight()));
            mCursorDrawable.draw(canvas);
        } else {
            mCursorPaint.setColor(mCursorColor);
            mCursorPaint.setStrokeWidth(mCursorWidth);
            mCursorPaint.setStyle(Paint.Style.FILL_AND_STROKE);
            canvas.drawLine(mWidth / 2, 0 + rectOfBackGround.top + mBorderWidth, mWidth / 2, mHeight - rectOfBackGround.bottom - mBorderWidth, mCursorPaint);
        }
        canvas.restore();
    }

第六部分:這個(gè)不解釋了

/**
     * 第六部分
     * 設(shè)置數(shù)據(jù)糕簿,用以第一次賦值探入,或者在listView recycleView復(fù)用的時(shí)候賦值,或者在交互中重新賦值等等
     */
    public void setData(int currentValue) {
        if (currentValue > maxValue || currentValue < minValue) {
            throw new IllegalArgumentException("the value must between minValue and maxValue");
        }

        this.currentValue = currentValue;
        invalidate();
        if (mListener != null) {
            mListener.onValueChange(currentValue);
        }if (mListener != null) {
            mListener.onValueChange(currentValue);
        }
    }

第七部分 重難點(diǎn)部分懂诗,但是也很簡單蜂嗽。注意true和false的返回,

 /**
     * 第七部分
     * 處理觸摸事件
     */
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (mVelocityTracker == null) {
            mVelocityTracker = VelocityTracker.obtain();
        }
        mVelocityTracker.addMovement(event);

        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                mScroller.forceFinished(true);
                mLastX = (int) event.getX();
                mMove = 0;
                isMove = false;
                break;
            case MotionEvent.ACTION_MOVE:
                mCurrentX = (int) event.getX();
                mMove += (int) ((mLastX - mCurrentX) * dampNumber);

                if (mMove < mTouchSlop) {
                    isMove = true;
                }

                if (currentValue > maxValue) {
                    return false;
                }

                if (currentValue < minValue) {
                    return false;
                }

                if (isMove) {
                    changeMoveAndValue();
                }
                break;
            case MotionEvent.ACTION_CANCEL:
            case MotionEvent.ACTION_UP:
                isMove = false;
                countVelocityTracker(event);
                break;
        }

        mLastX = (int) event.getX();

        return true;
    }

第八部分殃恒,控件滑動(dòng)植旧。Scroller和VelocityTracker的使用

 /**
     * 第八部分處理滑動(dòng)相關(guān)的事情
     */
    private void changeMoveAndValue() {
        int tValue = (int) (mMove / mCalibrationTailsDistance);
        if (Math.abs(tValue) > 0) {
            currentValue = currentValue + tValue;
            mMove -= tValue * mCalibrationTailsDistance;
            if (currentValue < minValue || currentValue > maxValue) {
                currentValue = currentValue < minValue ? minValue : maxValue;
                mMove = 0;
                mScroller.forceFinished(true);
            }
            notifyValueChange(currentValue);
        }
        postInvalidate();
    }

    private void countMoveEnd() {
        int roundMove = Math.round(mMove / mCalibrationTailsDistance);
        currentValue = currentValue + roundMove;
        if (currentValue < minValue) {
            currentValue = minValue;
        }

        if (currentValue > maxValue) {
            currentValue = maxValue;
        }

        mLastX = 0;
        mMove = 0;

        notifyValueChange(currentValue);
        postInvalidate();
    }


    private int mLastScrollX;

    
    @Override
    public void computeScroll() {
        super.computeScroll();
        if (mScroller.computeScrollOffset()) {
            mCurrentX = mScroller.getCurrX();

            if (mScroller.getCurrX() == mScroller.getFinalX()) { // over
                countMoveEnd();
            } else {
                mMove += (mLastScrollX - mCurrentX);
                changeMoveAndValue();
            }
            mLastScrollX = mCurrentX;
        }
    }

    private void countVelocityTracker(MotionEvent event) {
//        mVelocityTracker.addMovement(event);
        mVelocityTracker.computeCurrentVelocity(200);
        float xVelocity = mVelocityTracker.getXVelocity();


        if (Math.abs(xVelocity) > mMinVelocity) {
            mScroller.fling(0, 0, (int) xVelocity, 0, Integer.MIN_VALUE, Integer.MAX_VALUE, 0, 0);
        }
        postInvalidate();
    }

最后

 /**
     * 定義相關(guān)的監(jiān)聽器
     */

    private OnValueChangeListener mListener;

    public void setOnValueChangeListener(OnValueChangeListener mListener) {
        this.mListener = mListener;
    }

    public void notifyValueChange(int value) {
        if (null != mListener) {
            mListener.onValueChange(value);
        }
    }


    public interface OnValueChangeListener {
        void onValueChange(int value);
    }

實(shí)際上還有一部分是動(dòng)畫,但是本例中未曾用到离唐,以后再說吧

demo

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末病附,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子亥鬓,更是在濱河造成了極大的恐慌完沪,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,402評論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件嵌戈,死亡現(xiàn)場離奇詭異覆积,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)熟呛,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評論 3 392
  • 文/潘曉璐 我一進(jìn)店門宽档,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人惰拱,你說我怎么就攤上這事雌贱。” “怎么了偿短?”我有些...
    開封第一講書人閱讀 162,483評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長馋没。 經(jīng)常有香客問我昔逗,道長,這世上最難降的妖魔是什么篷朵? 我笑而不...
    開封第一講書人閱讀 58,165評論 1 292
  • 正文 為了忘掉前任勾怒,我火速辦了婚禮,結(jié)果婚禮上声旺,老公的妹妹穿的比我還像新娘笔链。我一直安慰自己,他們只是感情好腮猖,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,176評論 6 388
  • 文/花漫 我一把揭開白布鉴扫。 她就那樣靜靜地躺著,像睡著了一般澈缺。 火紅的嫁衣襯著肌膚如雪坪创。 梳的紋絲不亂的頭發(fā)上炕婶,一...
    開封第一講書人閱讀 51,146評論 1 297
  • 那天,我揣著相機(jī)與錄音莱预,去河邊找鬼柠掂。 笑死,一個(gè)胖子當(dāng)著我的面吹牛依沮,可吹牛的內(nèi)容都是我干的涯贞。 我是一名探鬼主播,決...
    沈念sama閱讀 40,032評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼危喉,長吁一口氣:“原來是場噩夢啊……” “哼肩狂!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起姥饰,我...
    開封第一講書人閱讀 38,896評論 0 274
  • 序言:老撾萬榮一對情侶失蹤傻谁,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后列粪,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體审磁,經(jīng)...
    沈念sama閱讀 45,311評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,536評論 2 332
  • 正文 我和宋清朗相戀三年岂座,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了态蒂。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,696評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡费什,死狀恐怖钾恢,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情鸳址,我是刑警寧澤瘩蚪,帶...
    沈念sama閱讀 35,413評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站稿黍,受9級特大地震影響疹瘦,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜巡球,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,008評論 3 325
  • 文/蒙蒙 一言沐、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧酣栈,春花似錦险胰、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春缨睡,著一層夾襖步出監(jiān)牢的瞬間鸟悴,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評論 1 269
  • 我被黑心中介騙來泰國打工奖年, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留细诸,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,698評論 2 368
  • 正文 我出身青樓陋守,卻偏偏與公主長得像震贵,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子水评,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,592評論 2 353

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