Android自定義圖片拖拽控件

源碼地址

效果圖

基本思路

布局示意圖

我們先考慮簡(jiǎn)單的情況父虑,兩個(gè)控件之間的圖片拖拽母赵,首先我們需要準(zhǔn)備ImageViewA和ImageViewB兩個(gè)ImageView,然后在里面設(shè)置圖片逸爵。接著我們需要考慮拖拽事件的觸發(fā)條件,這里假設(shè)為手指從ImageView的某個(gè)邊緣滑出一段距離即觸發(fā)拖拽事件

假設(shè)我們此時(shí)在ImageViewA的邊緣向下滑動(dòng)了一段距離凹嘲,在觸發(fā)拖拽事件的時(shí)候我們需要將ImageViewA的圖片隱藏,然后將A的圖片傳遞給一個(gè)半透明的ImageViewC并將C顯示出來(lái)师倔,由于這個(gè)ImageViewC同一時(shí)間只會(huì)有一個(gè),所以我們可以在自定義的layout中創(chuàng)建一個(gè)ImageView類型的成員變量進(jìn)行復(fù)用周蹭,在觸發(fā)拖拽事件的時(shí)候ImageViewC會(huì)跟隨手指滑動(dòng)趋艘,在手指抬起來(lái)的時(shí)候判斷ImageViewC的中心是否在另一個(gè)ImageViewB上疲恢,若在則交換A和B的圖片

整體思路還是比較清晰的,主要是要?jiǎng)?chuàng)建一個(gè)自定義的layout對(duì)子View進(jìn)行管理并自定義事件分發(fā)規(guī)則

代碼實(shí)現(xiàn)

自定義Layout

此處選擇繼承自FrameLayout致稀,因?yàn)榭梢酝ㄟ^(guò)margin屬性自由控制View所在的位置并且不會(huì)影響到其他的View冈闭,后期可能會(huì)向Layout中添加一些EditText、TextView或者各種自定義View抖单,而這些View可能是要疊在ImageView上面的萎攒,因此選擇繼承自FrameLayout無(wú)疑是最合適的,而且它已經(jīng)幫我們處理了很多細(xì)節(jié)矛绘,我們可以專注于功能的實(shí)現(xiàn)

獲取子View所在的區(qū)域信息

因?yàn)閷?duì)事件進(jìn)行分發(fā)需要子View的位置信息耍休,所以我們需要一個(gè)成員變量來(lái)保存,此處選擇RectF來(lái)保存一個(gè)View所在的區(qū)域货矮,然后將所有View的位置信息存放到一個(gè)HashMap中羊精,這樣就可以處理兩個(gè)以上的控件間圖片拖拽了

public class ConfigurableFrameLayout extends FrameLayout implements OnTriggerDragListener {
    ...
    // 保存所有子View的區(qū)域
    private HashMap<View, RectF> mChildViewRects; 
    ...

    @Override
    protected void onLayout(boolean changed,int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right,bottom);
        /* 因?yàn)閘ayout后每個(gè)子View的位置才確定,所以在此處初始化子View的位置信息*/
        initChildViewRect();
    }
    /**
    * 獲取各子View所在區(qū)域
    */
    private void initChildViewRect() {
        for (int i = 0; i < getChildCount(); i++) {
            View child = getChildAt(i);
            LayoutParams lp = (LayoutParams)child.getLayoutParams();
            // 避免多次創(chuàng)建對(duì)象
            RectF rect = mChildViewRects.get(child;
            if (rect == null) {
                rect = new RectF();
            }
            // 設(shè)置子View所在的矩形區(qū)域
            rect.set(lp.leftMargin,lp.topMargin,
                lp.leftMargin + child.getWidt(),
                lp.topMargin +child.getHeight());
            mChildViewRects.put(child, rect);
        }
    }
}

對(duì)事件進(jìn)行分發(fā)

子View有可能比較小囚玫,如果要兩根手指都在子View里面才能對(duì)圖片進(jìn)行操作會(huì)不太方便喧锦,而我們要實(shí)現(xiàn)只要一根手指在子View內(nèi),另一根手指無(wú)論在哪都可以對(duì)子View的圖片進(jìn)行操作抓督,并且同一時(shí)間只能操作一個(gè)子View燃少,此時(shí)就需要自定義事件分發(fā)規(guī)則

自定義事件分發(fā)規(guī)則可以選擇重寫(xiě)dispatchTouchEvent()方法,但是需要考慮的細(xì)節(jié)比較多铃在,所以我們選擇攔截所有事件阵具,然后在onTouchEvent()方法中對(duì)事件進(jìn)行分發(fā)

//重寫(xiě)自定義Layout的onInterceptTouchEvent()和onTouchEvent()

@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
    super.onInterceptTouchEvent(ev);
    // 攔截所有事件
    return true;
}

private View mCurrentChildView; // 當(dāng)前正在處理觸摸事件的子View

@Override
public boolean onTouchEvent(MotionEventevent) {
    // 事件是否被子View消費(fèi)
    boolean handled = false;
    // 當(dāng)前事件流若已被分發(fā)給某個(gè)子View處理,則將后續(xù)事件都分發(fā)給該子View
    if (mCurrentChildView != null) {
        handled = dispatchTouchEventToChild(event, mCurrentChildView);
    }
    switch (event.getActionMasked()) {
        case MotionEvent.ACTION_DOWN:
            // 獲取位于觸點(diǎn)的子View
            mCurrentChildView = viewInXY(event.getX(), event.getY());
            // 判斷子View是否可以觸發(fā)拖拽事件定铜,可以則為其設(shè)置觸發(fā)時(shí)的監(jiān)聽(tīng)事件
            if (mCurrentChildView instanceof TriggerDraggable) {
                ((TriggerDraggable) mCurrentChildView).setOnTriggerDragListener(this);
            }
            // 只在ACTION_DOWN時(shí)對(duì)事件進(jìn)行分發(fā)阳液,事件只能交由一個(gè)子View處理
            if (mCurrentChildView != null) {
                handled =dispatchTouchEventToChild(event, mCurrentChildView);
            }
            break;
        case MotionEvent.ACTION_CANCEL:
        case MotionEvent.ACTION_UP:
            // 設(shè)置當(dāng)前處理事件流的子View為null
            mCurrentChildView = null;
            break;
    }
    if (!handled) handled = super.onTouchEvent(event);
    return handled;
}

/**
 * 獲取位于指定坐標(biāo)的子View
 * @param x x坐標(biāo)
 * @param y y坐標(biāo)
 * @return 包含該坐標(biāo)的子View,若找不到則返回null
 */
@Nullable
private View viewInXY(float x, float y) {
    for (int i = 0; i < getChildCount(); i++) {
        View child = getChildAt(i);
        RectF rectF = mChildViewRects.get(child);
        if (rectF != null && rectF.contains(x, y)) {
            return child;
        }
    }
    return null;
}

/**
 * 將觸摸事件坐標(biāo)變換后傳遞給子View
 * @return true如果事件被子View消費(fèi)揣炕,否則返回false
 */
private boolean dispatchTouchEventToChild(MotionEvent event, View child) {
    final float offsetX = getScrollX() - child.getLeft();
    final float offsetY = getScrollY() - child.getTop();
    event.offsetLocation(offsetX, offsetY);

    boolean handled = child.dispatchTouchEvent(event);

    event.offsetLocation(-offsetX, -offsetY);
    return handled;
}

判斷是否觸發(fā)拖拽事件

現(xiàn)在子View已經(jīng)可以接收到觸摸事件了帘皿,然后我們需要判斷是否觸發(fā)了拖拽事件,我們要實(shí)現(xiàn)的效果是可以指定View的上下左右的某個(gè)或某幾個(gè)邊界畸陡,當(dāng)超過(guò)指定的邊界指定的距離后即觸發(fā)拖拽事件矮烹,觸發(fā)后隱藏觸發(fā)條件的ImageViewA(此處假設(shè)在ImageViewA上觸發(fā))并將半透明的ImageViewC顯示出來(lái)

這邊需要考慮將判斷邏輯寫(xiě)在ImageView中還是Layout中,當(dāng)然罩锐,寫(xiě)在哪里都可以實(shí)現(xiàn)功能奉狈,但是設(shè)計(jì)上可能不夠合理,由于拖拽事件是在操作子View圖片的過(guò)程中觸發(fā)的涩惑,所以個(gè)人認(rèn)為在子View中對(duì)是否觸發(fā)進(jìn)行判斷比較合理仁期。

此處使用了觀察者模式,將判斷邏輯寫(xiě)在子View中,然后在觸發(fā)拖拽事件的時(shí)候通過(guò)監(jiān)聽(tīng)器通知Layout做相應(yīng)的操作

為子View設(shè)置監(jiān)聽(tīng)器的代碼可以參考上文對(duì)事件進(jìn)行分發(fā)中的OnTouchEvent()中ACTION_DOWN下面的代碼

//用于解耦ConfigurableFrameLayout與DraggableImageView的接口

/**
 * 實(shí)現(xiàn)該接口表示可觸發(fā)拖拽事件
 */
public interface TriggerDraggable {
    // 判斷是否觸發(fā)拖拽事件
    boolean triggerDrag(MotionEvent event);
    // 設(shè)置拖拽事件監(jiān)聽(tīng)器
    void setOnTriggerDragListener(OnTriggerDragListener listener);
}

/**
 * 拖拽事件監(jiān)聽(tīng)器
 */
public interface OnTriggerDragListener {
    // 在拖拽的時(shí)候調(diào)用
    void onDrag(MotionEvent event);
    // 拖拽事件結(jié)束時(shí)調(diào)用
    void onDragFinish(MotionEvent event);
}

在子View中判斷是否觸發(fā)拖拽事件跛蛋,這邊我沒(méi)有直接使用ImageView,而是繼承了我之前寫(xiě)的一個(gè)自定義ImageView熬的,主要是比普通的ImageView多了手勢(shì)操作圖片旋轉(zhuǎn)、平移赊级、縮放三個(gè)功能押框。(ImageViewC只需要展示圖片,所以使用的還是普通的ImageView)

public class DraggableImageView extends TransformativeImageView implements TriggerDraggable {
    
    ...

    /**
    * 可觸發(fā)拖拽事件的邊界,若數(shù)組某個(gè)index的變量為true理逊,則表示該index對(duì)應(yīng)的邊界可以觸發(fā)拖拽事件橡伞;
    * 默認(rèn)所有邊界均不可觸發(fā)拖拽事件
    * index: {0, 1, 2, 3} -> boundary: {left, top, right, bottom}
    *
    * 例:mBoundary = {true, false, false, true} 表示左邊界與下邊界可觸發(fā)拖拽事件
    */
    private boolean[] mBoundary = new boolean[4];
    private float mTriggerDistance = DEFAULT_TRIGGER_DISTANCE; // 觸發(fā)拖拉事件的距離

    /**
    * 判斷是否觸發(fā)拖拽事件
    * @param event 觸摸事件
    * @return 符合觸發(fā)條件則返回true,否則返回false
    */
    @Override
    public boolean triggerDrag(MotionEvent event) {
        boolean canDrag = false;
        // 當(dāng)前觸點(diǎn)坐標(biāo)
        final float x = event.getX();
        final float y = event.getY();    
        // 判斷某個(gè)邊界是否可觸發(fā)拖拽事件并且達(dá)到了觸發(fā)條件
        if (mBoundary[0] && -x > mTriggerDistance
                || mBoundary[1] && -y > mTriggerDistance
                || mBoundary[2] && x - getWidth() > mTriggerDistance
                || mBoundary[3] && y - getHeight() > mTriggerDistance) {
            canDrag = true;
        }
        return canDrag;
    }

    private boolean isPointerCountChanged = false; // 本次觸摸事件流中觸點(diǎn)數(shù)量是否減少
    private boolean mCanDrag = false; // 是否可以將圖片拖拽出控件
    private OnTriggerDragListener mOnTriggerDragListener; // 拖拽事件監(jiān)聽(tīng)器
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        super.onTouchEvent(event);    
        switch (event.getActionMasked()) {
            case MotionEvent.ACTION_MOVE:
                /* 只有處于不可拖拽狀態(tài)時(shí)才判斷是否觸發(fā)拖拽事件晋被,
                * 且本次觸摸事件流觸點(diǎn)數(shù)量未減少的情況兑徘,才判斷是否觸發(fā)拖拽事件
                */
                if (!mCanDrag && !isPointerCountChanged && triggerDrag(event)) {
                    mCanDrag = true;
                }
                // 調(diào)用拖拽監(jiān)聽(tīng)方法
                if (mCanDrag && mOnTriggerDragListener != null) {
                    mOnTriggerDragListener.onDrag(event);
                }
                break;
            case MotionEvent.ACTION_CANCEL:
            case MotionEvent.ACTION_UP:
                // 拖拽事件結(jié)束
                if (mCanDrag && mOnTriggerDragListener != null) {
                    mOnTriggerDragListener.onDragFinish(event);
                }
                // 清除是否可拖拽的標(biāo)志位
                mCanDrag = false;
                // ACTION_UP意味著本次事件流結(jié)束,所以將記錄觸點(diǎn)數(shù)量是否減少的標(biāo)志位清除
                isPointerCountChanged = false;
                break;
            case MotionEvent.ACTION_POINTER_UP:
                // 觸點(diǎn)數(shù)量減少
                isPointerCountChanged = true;
                break;
        }    return true;
    }

    ...
}

觸發(fā)拖拽事件后執(zhí)行的操作

觸發(fā)拖拽事件后我們需要一個(gè)半透明的ImageViewC(代碼中為mInterpolationImageView)來(lái)存放觸發(fā)拖拽的子View中的圖片

//在自定義Layout中創(chuàng)建并初始化ImageViewC

private void initInterpolationView() {
    mInterpolationImageView = new ImageView(getContext());
    // TODO dp轉(zhuǎn)px, 大小羡洛,透明度為可配置
    LayoutParams lp = new LayoutParams(300, 300);
    mInterpolationImageView.setLayoutParams(lp);
    mInterpolationImageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
    mInterpolationImageView.setAlpha(0.5f);
    mInterpolationImageView.setVisibility(GONE);
    addView(mInterpolationImageView);
}

觸發(fā)拖拽后挂脑,觸發(fā)事件的控件的圖片會(huì)隱藏,并且半透明的ImageViewC會(huì)跟隨手指移動(dòng)

public class ConfigurableFrameLayout extends FrameLayout implements OnTriggerDragListener {

    ...

    // 判斷拖拽的ImageView是否已經(jīng)設(shè)置了當(dāng)前處理事件的子View的圖片
    private boolean mInterpolationHasImg = false;

    /**
    * 拖拽ImageView,使之跟隨觸點(diǎn)位置移動(dòng)
    * @param event 當(dāng)前觸摸事件
    */
    private void dragInterpolationImageView(MotionEvent event) {
        ImageView curImgView = null;
        if (mCurrentChildView instanceof ImageView) {
            curImgView = (ImageView) mCurrentChildView;
        }
        if (curImgView != null) {
            // 為中間控件設(shè)置圖片
            if (!mInterpolationHasImg
                    && curImgView.getDrawable() instanceof BitmapDrawable) {
                Drawable drawable = curImgView.getDrawable();
                Bitmap bitmap = null;
                if (drawable instanceof BitmapDrawable) {
                    bitmap = ((BitmapDrawable) drawable).getBitmap();
                }
                if (bitmap != null) {
                    mInterpolationImageView.setImageBitmap(bitmap);
                }
                mInterpolationHasImg = true;
            }
            // 隱藏控件的圖片
            curImgView.setImageAlpha(0);
            // 設(shè)置中間控件為可見(jiàn)
            mInterpolationImageView.setVisibility(VISIBLE);
            // 跟隨手指移動(dòng)中間控件
            LayoutParams lp = (LayoutParams) mInterpolationImageView.getLayoutParams();
            lp.setMargins((int)(event.getX() - mInterpolationImageView.getWidth() / 2),
                    (int)(event.getY() - mInterpolationImageView.getHeight() / 2),
                    0, 0);
            // 將中間控件移到最上層
            mInterpolationImageView.bringToFront();
        }
    }

    ...

     @Override
    public void onDrag(MotionEvent event) {
        // 由于傳遞過(guò)來(lái)的事件是相對(duì)于子View的坐標(biāo)欲侮,所以需要變換為相對(duì)Layout的坐標(biāo)
        final float offsetX = mCurrentChildView.getLeft() - getScrollX();
        final float offsetY = mCurrentChildView.getTop() - getScrollY();
        event.offsetLocation(offsetX, offsetY);

        dragInterpolationImageView(event);

        event.offsetLocation(-offsetX, -offsetY);
    }

    ...
}

拖拽結(jié)束時(shí)判斷是否交換圖片

當(dāng)所有手指抬起崭闲,即拖拽事件結(jié)束后,判斷ImageViewC 的中心是否在另一個(gè)子View上威蕉,若在則交換兩者圖片

public class ConfigurableFrameLayout extends FrameLayout implements OnTriggerDragListener {

    ...

    /**
     * 判斷是否需要交換圖片刁俭,并清理一些標(biāo)志位憔狞,設(shè)置各子控件的最終狀態(tài)
     * @param event 當(dāng)前觸摸事件
     */
    private void dragFinish(MotionEvent event) {
        ImageView curImgView = null;
        if (mCurrentChildView instanceof ImageView) {
            curImgView = (ImageView) mCurrentChildView;
        }
        if (curImgView != null) {
            // 判斷當(dāng)前觸點(diǎn)是否在其他子View內(nèi),若是則交換兩者圖片
            View v = viewInXY(event.getX(), event.getY());
            if (v instanceof ImageView) {
                exchangeImg(curImgView, (ImageView) v);
            }
            // 將之前拖拽過(guò)程隱藏當(dāng)前處理事件的子View的圖片顯示出來(lái)
            curImgView.setImageAlpha(255);
            // 隱藏中間控件
            mInterpolationImageView.setVisibility(GONE);
            // 設(shè)置中間控件不含當(dāng)前處理事件的子View的圖片
            mInterpolationHasImg = false;
        }
    }
    /**
    * 交換兩個(gè)ImageView的圖片
    * @param fromImgView 源控件
    * @param toImgView 目標(biāo)控件
    */
    private void exchangeImg(ImageView fromImgView, ImageView toImgView) {
        // 若源控件不包含圖片則不交換
        if (toImgView == null || fromImgView == null || fromImgView.getDrawable() == null) {
            return;
        }
        Bitmap fromBmp = null;
        Bitmap toBmp = null;
        // 獲取源控件圖片
        if (fromImgView.getDrawable() instanceof BitmapDrawable) {
            fromBmp = ((BitmapDrawable) fromImgView.getDrawable()).getBitmap();
        }
        // 獲取目標(biāo)控件圖片
        if (toImgView.getDrawable() instanceof BitmapDrawable) {
            toBmp = ((BitmapDrawable) toImgView.getDrawable()).getBitmap();
        }
        // 交換兩者圖片
        if (toBmp != null) fromImgView.setImageBitmap(toBmp);
        if (fromBmp != null) toImgView.setImageBitmap(fromBmp);
    }

    ...

     @Override
    public void onDragFinish(MotionEvent event) {
        // 由于傳遞過(guò)來(lái)的事件是相對(duì)于子View的坐標(biāo)通今,所以需要變換為相對(duì)Layout的坐標(biāo)
        final float offsetX = mCurrentChildView.getLeft() - getScrollX();
        final float offsetY = mCurrentChildView.getTop() - getScrollY();
        event.offsetLocation(offsetX, offsetY);

        dragFinish(event);

        event.offsetLocation(-offsetX, -offsetY);
    }
}

使用

自定義Layout和ImageView寫(xiě)好后就大功告成了策治,只需要在xml文件中進(jìn)行配置,然后為ImageView設(shè)置圖片即可看到效果

// 下面兩個(gè)屬性為TrasmativeImageView中的自定義屬性
app:max_scale="4" 最大縮放比例為4倍
app:revert="false" 不開(kāi)啟回彈效果

// 下面的為DraggableImageView的自定義屬性 
app:boundary_left="true" 左邊界可觸發(fā)拖拽
app:boundary_top="true" 上邊界可觸發(fā)拖拽
app:trigger_distance="3dp" 觸發(fā)拖拽的距離
<?xml version="1.0" encoding="utf-8"?>
<cn.lkllkllkl.configurableframelayout.ConfigurableFrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    tools:context="cn.lkllkllkl.configurableframelayoutsample.MainActivity">

    <cn.lkllkllkl.configurableframelayout.DraggableImageView
        android:background="@color/gray"
        android:id="@+id/draggable_image_view_1"
        android:layout_width="200dp"
        android:layout_height="200dp"
        android:layout_marginLeft="90dp"
        android:layout_marginTop="30dp"
        app:max_scale="4"
        app:revert="false"
        app:boundary_bottom="true"
        app:trigger_distance="100dp"
        />


    <cn.lkllkllkl.configurableframelayout.DraggableImageView
        android:background="@color/gray"
        android:id="@+id/draggable_image_view_2"
        android:layout_width="150dp"
        android:layout_height="150dp"
        android:layout_marginTop="270dp"
        android:layout_marginLeft="50dp"
        app:revert="false"
        app:max_scale="4"
        app:boundary_top="true"
        app:boundary_right="true"
        app:trigger_distance="3dp"/>

    <cn.lkllkllkl.configurableframelayout.DraggableImageView
        android:background="@color/gray"
        android:id="@+id/draggable_image_view_3"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:layout_marginTop="290dp"
        android:layout_marginLeft="220dp"
        app:max_scale="4"
        app:revert="false"
        app:boundary_left="true"
        app:boundary_top="true"
        app:trigger_distance="3dp"/>
</cn.lkllkllkl.configurableframelayout.ConfigurableFrameLayout>

/**
 * Activity 代碼
 */

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        DraggableImageView draggableImageView1 =
                (DraggableImageView) findViewById(R.id.draggable_image_view_1);
        DraggableImageView draggableImageView2 =
                (DraggableImageView) findViewById(R.id.draggable_image_view_2);
        DraggableImageView draggableImageView3 =
                (DraggableImageView) findViewById(R.id.draggable_image_view_3);
        // 此處使用Glide將圖片載入View中
        GlideApp.with(this)
                .load(R.drawable.black)
                .into(draggableImageView1);
        GlideApp.with(this)
                .load(R.drawable.cat)
                .into(draggableImageView2);
        GlideApp.with(this)
                .load(R.drawable.cat)
                .into(draggableImageView3);
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末葡幸,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌舀奶,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,858評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件斋射,死亡現(xiàn)場(chǎng)離奇詭異育勺,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)罗岖,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門涧至,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人桑包,你說(shuō)我怎么就攤上這事南蓬。” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,282評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵赘方,是天一觀的道長(zhǎng)烧颖。 經(jīng)常有香客問(wèn)我,道長(zhǎng)窄陡,這世上最難降的妖魔是什么炕淮? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,842評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮跳夭,結(jié)果婚禮上涂圆,老公的妹妹穿的比我還像新娘。我一直安慰自己优妙,他們只是感情好乘综,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,857評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著套硼,像睡著了一般卡辰。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上邪意,一...
    開(kāi)封第一講書(shū)人閱讀 51,679評(píng)論 1 305
  • 那天九妈,我揣著相機(jī)與錄音,去河邊找鬼雾鬼。 笑死萌朱,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的策菜。 我是一名探鬼主播晶疼,決...
    沈念sama閱讀 40,406評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼又憨!你這毒婦竟也來(lái)了翠霍?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,311評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤蠢莺,失蹤者是張志新(化名)和其女友劉穎寒匙,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體躏将,經(jīng)...
    沈念sama閱讀 45,767評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡锄弱,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了祸憋。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片会宪。...
    茶點(diǎn)故事閱讀 40,090評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖蚯窥,靈堂內(nèi)的尸體忽然破棺而出掸鹅,到底是詐尸還是另有隱情喜命,我是刑警寧澤,帶...
    沈念sama閱讀 35,785評(píng)論 5 346
  • 正文 年R本政府宣布河劝,位于F島的核電站壁榕,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏赎瞎。R本人自食惡果不足惜牌里,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,420評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望务甥。 院中可真熱鬧牡辽,春花似錦、人聲如沸敞临。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,988評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)挺尿。三九已至奏黑,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間编矾,已是汗流浹背熟史。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,101評(píng)論 1 271
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留窄俏,地道東北人蹂匹。 一個(gè)月前我還...
    沈念sama閱讀 48,298評(píng)論 3 372
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像凹蜈,于是被迫代替她去往敵國(guó)和親限寞。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,033評(píng)論 2 355

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

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,163評(píng)論 25 707
  • 《夏季静尼,爺爺》這是個(gè)炎熱的夏季看著你匆忙的身影不覺(jué)中已七十個(gè)年頭你是個(gè)勤勞的農(nóng)民雖然已七十多歲了可是你從未放下過(guò)放...
    葉威閱讀 220評(píng)論 0 1
  • 7白粉,是我們传泊,七個(gè)姑娘、平凡的姑娘鸭巴。 本來(lái)是不討喜的數(shù)字眷细,可是因?yàn)槲覀儯蝗挥懴擦司樽妫€格外可愛(ài)溪椎。 其實(shí)這個(gè)序言本來(lái)...
    詹小邪閱讀 112評(píng)論 0 1
  • 介紹 大約半個(gè)月前第一次聽(tīng)說(shuō)了OKR的概念,此方法是google用于企業(yè)內(nèi)部管理的方法,但我覺(jué)得這個(gè)方法一樣可以用...
    陳素封閱讀 10,360評(píng)論 1 24
  • JobScheduler的地位非常的重要校读,所有的關(guān)鍵都在JobScheduler沼侣,它的重要性就相當(dāng)于是Spa...
    陽(yáng)光男孩spark閱讀 1,043評(píng)論 0 0