Android实现TreeView

实现的效果图如下所示:


image.png

1、定义Node节点对象

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

public class Node<T, B> implements Serializable {

    private static final long serialVersionUID = -4492354830247617313L;

    //节点对象
    private B Bean;
    //节点id
    private T id;
    //节点对应的父级节点id,根节点的id为0
    private T pId;
    //节点名称
    private String name;
    //当前节点的级别
    private int level;
    //是否展开
    private boolean isExpand = false;
    //设置展开、关闭的图片
    private int icon = -1;
    //是否被选中
    private boolean isChecked;
    //子集
    private List<Node> children = new ArrayList<>();
    //父节点
    private Node parent;
    //节点文件下载状态
    private int status = 0;//0:未下载 1:已下载 2:正在下载

    public Node() {
    }

    public Node(T id, T pId, String name) {
        super();
        this.id = id;
        this.pId = pId;
        this.name = name;
    }

    public B getBean() {
        return Bean;
    }

    public void setBean(B bean) {
        Bean = bean;
    }

    public T getId() {
        return id;
    }

    public void setId(T id) {
        this.id = id;
    }

    public T getpId() {
        return pId;
    }

    public void setpId(T pId) {
        this.pId = pId;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    /**
     * 获取level
     */
    public int getLevel() {
        return parent == null ? 0 : parent.getLevel() + 1;
    }

    public void setLevel(int level) {
        this.level = level;
    }

    public boolean isExpand() {
        return isExpand;
    }

    /**
     * 设置展开
     */
    public void setExpand(boolean isExpand) {
        this.isExpand = isExpand;
        if (!isExpand) {
            for (Node node : children) {
                node.setExpand(isExpand);
            }
        }
    }

    public int getIcon() {
        return icon;
    }

    public void setIcon(int icon) {
        this.icon = icon;
    }

    public boolean isChecked() {
        return isChecked;
    }

    public void setChecked(boolean checked) {
        isChecked = checked;
    }

    public List<Node> getChildren() {
        return children;
    }

    public void setChildren(List<Node> children) {
        this.children = children;
    }

    public Node getParent() {
        return parent;
    }

    public void setParent(Node parent) {
        this.parent = parent;
    }

    public int getStatus() {
        return status;
    }

    public void setStatus(int status) {
        this.status = status;
    }

    /**
     * 是否为跟节点
     */
    public boolean isRoot() {
        return parent == null;
    }

    /**
     * 判断父节点是否展开
     */
    public boolean isParentExpand() {
        if (parent == null)
            return false;
        return parent.isExpand();
    }

    /**
     * 是否是叶子节点
     */
    public boolean isLeaf() {
        return children.size() == 0;
    }

}

2、定义TreeHelper

import android.text.TextUtils;

import com.glendale.bim.base.R;

import java.util.ArrayList;
import java.util.List;


/**
 * @user:CJJ-SAL
 * @time:2019/8/22 15:38
 * @desc:
 */
public class TreeHelper {

    /**
     * 返回排序后的节点树
     */
    public static List<Node> getSortedNodes(List<Node> datas, int defaultExpandLevel) {
        List<Node> result = new ArrayList<Node>();
        // 设置Node间父子关系
        List<Node> nodes = convetData2Node(datas);
        // 拿到根节点
        List<Node> rootNodes = getRootNodes(nodes);
        // 排序以及设置Node间关系
        for (Node node : rootNodes) {
            addNode(result, node, defaultExpandLevel, 1);
        }
        return result;
    }

    /**
     * 将我们的数据转化为树的节点
     */
    public static List<Node> convetData2Node(List<Node> datas) {
        for (int i = 0; i < datas.size(); i++) {
            Node n = datas.get(i);
            for (int j = i + 1; j < datas.size(); j++) {
                Node m = datas.get(j);
                if (m.getpId() instanceof String) {
                    String mId = (String) m.getId();
                    String mpId = (String) m.getpId();
                    String nId = (String) n.getId();
                    String npId = (String) n.getpId();

                    if (TextUtils.equals(mpId, nId)) {
                        n.getChildren().add(m);
                        m.setParent(n);
                    } else if (TextUtils.equals(mId, npId)) {
                        m.getChildren().add(n);
                        n.setParent(m);
                    }
                } else if (m.getpId() instanceof Integer) {
                    if (m.getpId() == n.getId()) {
                        n.getChildren().add(m);
                        m.setParent(n);
                    } else if (m.getId() == n.getpId()) {
                        m.getChildren().add(n);
                        n.setParent(m);
                    }
                }
            }
        }
        return datas;
    }

    /**
     * 获取根节点
     */
    public static List<Node> getRootNodes(List<Node> datas) {
        List<Node> root = new ArrayList<Node>();
        for (Node node : datas) {
            if (node.isRoot())
                root.add(node);
        }
        return root;
    }

    /**
     * 通过递归的方式,把一个节点上的所有的子节点等都按顺序放入
     */
    public static <T, B> void addNode(List<Node> nodes, Node<T, B> node, int defaultExpandLeval, int currentLevel) {
        nodes.add(node);
        if (defaultExpandLeval >= currentLevel) {
            node.setExpand(true);
        }
        if (node.isLeaf()) {
            return;
        }
        for (int i = 0; i < node.getChildren().size(); i++) {
            addNode(nodes, node.getChildren().get(i), defaultExpandLeval, currentLevel + 1);
        }
    }

    /**
     * 过滤出所有可见的Node
     */
    public static List<Node> filterVisibleNode(List<Node> nodes) {
        List<Node> result = new ArrayList<Node>();
        for (Node node : nodes) {
            // 如果为跟节点,或者上层目录为展开状态
            if (node.isRoot() || node.isParentExpand()) {
                setNodeIcon(node);
                result.add(node);
            }
        }
        return result;
    }

    /**
     * 设置节点的图标
     */
    private static void setNodeIcon(Node node) {
        if (node.getChildren().size() > 0 && node.isExpand()) {
            node.setIcon(R.drawable.icon_node_expanding);
        } else if (node.getChildren().size() > 0 && !node.isExpand()) {
            node.setIcon(R.drawable.icon_node_no_expanding);
        } else {
            node.setIcon(-1);
        }
    }
}

3、实现ListView适配器

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.ListView;

import java.util.ArrayList;
import java.util.List;

/**
 * @user:CJJ-SAL
 * @time:2019/8/22 15:14
 * @desc:
 */
public abstract class TreeListViewAdapter extends BaseAdapter {

    protected Context context;
    protected LayoutInflater mInflater;
    //存储所有可见的Node
    protected List<Node> mNodes = new ArrayList<>();
    //存储所有的Node
    protected List<Node> mAllNodes = new ArrayList<>();
    //节点点击接口
    private OnTreeNodeClickListener onTreeNodeClickListener;
    private OnTreeNodeLongClickListener onTreeNodeLongClickListener;
    //默认不展开
    private int defaultExpandLevel = 0;
    //展开与关闭的图片
    private int iconExpand = -1, iconNoExpand = -1;

    private List<Node> mDatas = new ArrayList<>();
    private ListView mTree;

    public TreeListViewAdapter(ListView mTree, Context context, List<Node> mDatas, int defaultExpandLevel) {
        this.mTree = mTree;
        this.context = context;
        this.mDatas = mDatas;
        this.defaultExpandLevel = defaultExpandLevel;
        initData();
    }

    public void initData() {
        if (null == mDatas || mDatas.size() == 0) {
            return;
        }
        mInflater = LayoutInflater.from(context);
        //对所有的Node进行排序
        mAllNodes = TreeHelper.getSortedNodes(mDatas, defaultExpandLevel);
        //过滤出可见的Node
        mNodes = TreeHelper.filterVisibleNode(mAllNodes);
        mTree.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                expandOrCollapse(position);
                if (onTreeNodeClickListener != null) {
                    onTreeNodeClickListener.onClick(mNodes.get(position), position);
                }
            }
        });
        mTree.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
            @Override
            public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
                if (onTreeNodeLongClickListener != null) {
                    onTreeNodeLongClickListener.onLongClick(mNodes.get(position), position);
                }
                return true;
            }
        });
    }

    @Override
    public int getCount() {
        return mNodes.size();
    }

    @Override
    public Object getItem(int position) {
        return mNodes.get(position);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        Node node = mNodes.get(position);
        convertView = getConvertView(node, position, convertView, parent);
        // 设置内边距
        convertView.setPadding(node.getLevel() * 20, 3, 3, 3);
        return convertView;
    }

    /**
     * 相应ListView的点击事件 展开或关闭某节点
     */
    public void expandOrCollapse(int position) {
        Node n = mNodes.get(position);
        if (n != null) {// 排除传入参数错误异常
            if (!n.isLeaf()) {
                n.setExpand(!n.isExpand());
                mNodes = TreeHelper.filterVisibleNode(mAllNodes);
                notifyDataSetChanged();// 刷新视图
            }
        }
    }

    /**
     * 设置节点选中状态
     *
     * @param node
     * @param isChecked
     */
    public void setChecked(Node node, boolean isChecked) {
        node.setChecked(isChecked);
        setChildChecked(node, isChecked);
        if (node.getParent() != null) {
            setNodeParentChecked(node.getParent(), isChecked);
        }
        notifyDataSetChanged();
    }

    /**
     * 获取排序后所有节点
     *
     * @return
     */
    public List<Node> getAllNodes() {
        if (mAllNodes == null)
            mAllNodes = new ArrayList<Node>();
        return mAllNodes;
    }

    /**
     * 设置子节点的选中状态
     *
     * @param node
     * @param isChecked
     * @param <T>
     * @param <B>
     */
    public <T, B> void setChildChecked(Node<T, B> node, boolean isChecked) {
        if (!node.isLeaf()) {
            node.setChecked(isChecked);
            for (Node childrenNode : node.getChildren()) {
                setChildChecked(childrenNode, isChecked);
            }
        } else {
            node.setChecked(isChecked);
        }
    }

    /**
     * 设置节点的父级选中状态
     *
     * @param node
     * @param checked
     */
    private void setNodeParentChecked(Node node, boolean checked) {
        if (checked) {
            node.setChecked(checked);
            if (node.getParent() != null) {
                setNodeParentChecked(node.getParent(), checked);
            }
        } else {
            List<Node> childrens = node.getChildren();
            boolean isChecked = false;
            for (Node children : childrens) {
                if (children.isChecked()) {
                    isChecked = true;
                }
            }
            //如果所有子节点都没有被选中 父节点也不选中
            if (!isChecked) {
                node.setChecked(checked);
            }
            if (node.getParent() != null) {
                setNodeParentChecked(node.getParent(), checked);
            }
        }
    }

    public void setOnTreeNodeClickListener(OnTreeNodeClickListener onTreeNodeClickListener) {
        this.onTreeNodeClickListener = onTreeNodeClickListener;
    }

    public void setOnTreeNodeLongClickListener(OnTreeNodeLongClickListener onTreeNodeLongClickListener) {
        this.onTreeNodeLongClickListener = onTreeNodeLongClickListener;
    }

    public abstract View getConvertView(Node node, int position, View convertView, ViewGroup parent);

}

4、实现TreeRecyclerAdapter适配器

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;

import java.util.ArrayList;
import java.util.List;

import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;

/**
 * @user:CJJ-SAL
 * @time:2019/11/21 11:48
 * @desc:
 */
public abstract class TreeRecyclerAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {

    protected Context context;
    protected LayoutInflater mInflater;
    //存储所有可见的Node
    protected List<Node> mNodes = new ArrayList<>();
    //存储所有的Node
    protected List<Node> mAllNodes = new ArrayList<>();
    //节点点击接口
    private OnTreeNodeClickListener onTreeNodeClickListener;
    private OnTreeNodeLongClickListener onTreeNodeLongClickListener;
    //默认不展开
    private int defaultExpandLevel = 0;
    //展开与关闭的图片
    private int iconExpand = -1, iconNoExpand = -1;

    private List<Node> mDatas = new ArrayList<>();
    private RecyclerView mTree;

    public TreeRecyclerAdapter(RecyclerView mTree, Context context, List<Node> datas, int defaultExpandLevel) {
        setHasStableIds(true);
        this.mTree = mTree;
        this.context = context;
        this.mDatas = datas;
        this.defaultExpandLevel = defaultExpandLevel;
        initData();
    }

    public void initData() {
        if (null == mDatas || mDatas.size() == 0) {
            return;
        }
        mInflater = LayoutInflater.from(context);
        //对所有的Node进行排序
        mAllNodes = TreeHelper.getSortedNodes(mDatas, defaultExpandLevel);
        //过滤出可见的Node
        mNodes = TreeHelper.filterVisibleNode(mAllNodes);
    }

    @Override
    public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {


        Node node = mNodes.get(position);
        // 设置内边距
        holder.itemView.setPadding(node.getLevel() * 20, 3, 3, 3);
        //设置点击事件
        holder.itemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                expandOrCollapse(position);
                if (onTreeNodeClickListener != null) {
                    onTreeNodeClickListener.onClick(mNodes.get(position), position);
                }
            }
        });
        //设置长按点击事件
        holder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
            @Override
            public boolean onLongClick(View v) {
                if (onTreeNodeLongClickListener != null) {
                    onTreeNodeLongClickListener.onLongClick(mNodes.get(position), position);
                }
                return true;
            }
        });
        onBindViewHolder(node, holder, position);
    }

    @Override
    public int getItemCount() {
        return mNodes.size();
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    /**
     * 相应ListView的点击事件 展开或关闭某节点
     */
    public void expandOrCollapse(int position) {
        Node n = mNodes.get(position);
        if (n != null) {// 排除传入参数错误异常
            if (!n.isLeaf()) {
                n.setExpand(!n.isExpand());
                mNodes = TreeHelper.filterVisibleNode(mAllNodes);
                notifyDataSetChanged();// 刷新视图
            }
        }
    }

    /**
     * 获取排序后所有节点
     *
     * @return
     */
    public List<Node> getAllNodes() {
        if (mAllNodes == null)
            mAllNodes = new ArrayList<Node>();
        return mAllNodes;
    }

    /**
     * 设置节点选中状态
     *
     * @param node
     * @param isChecked
     */
    public void setChecked(Node node, boolean isChecked) {
        node.setChecked(isChecked);
        setChildChecked(node, isChecked);
        if (node.getParent() != null) {
            setNodeParentChecked(node.getParent(), isChecked);
        }
        notifyDataSetChanged();
    }

    /**
     * 设置子节点的选中状态
     *
     * @param node
     * @param isChecked
     * @param <T>
     * @param <B>
     */
    public <T, B> void setChildChecked(Node<T, B> node, boolean isChecked) {
        if (!node.isLeaf()) {
            node.setChecked(isChecked);
            for (Node childrenNode : node.getChildren()) {
                setChildChecked(childrenNode, isChecked);
            }
        } else {
            node.setChecked(isChecked);
        }
    }

    /**
     * 设置节点的父级选中状态
     *
     * @param node
     * @param checked
     */
    private void setNodeParentChecked(Node node, boolean checked) {
        if (checked) {
            node.setChecked(checked);
            if (node.getParent() != null) {
                setNodeParentChecked(node.getParent(), checked);
            }
        } else {
            List<Node> childrens = node.getChildren();
            boolean isChecked = false;
            for (Node children : childrens) {
                if (children.isChecked()) {
                    isChecked = true;
                }
            }
            //如果所有子节点都没有被选中 父节点也不选中
            if (!isChecked) {
                node.setChecked(checked);
            }
            if (node.getParent() != null) {
                setNodeParentChecked(node.getParent(), checked);
            }
        }
    }

    public void setOnTreeNodeClickListener(OnTreeNodeClickListener onTreeNodeClickListener) {
        this.onTreeNodeClickListener = onTreeNodeClickListener;
    }

    public void setOnTreeNodeLongClickListener(OnTreeNodeLongClickListener onTreeNodeLongClickListener) {
        this.onTreeNodeLongClickListener = onTreeNodeLongClickListener;
    }

    public abstract void onBindViewHolder(Node node, RecyclerView.ViewHolder holder, final int position);
}

5、定义节点的点击事件

/**
 * @user:CJJ-SAL
 * @time:2019/8/22 15:20
 * @desc:节点点击事件
 */
public interface OnTreeNodeClickListener {
    void onClick(Node node, int position);
}

/**
 * @user:CJJ-SAL
 * @time:2019/8/22 15:20
 * @desc:节点长按事件
 */
public interface OnTreeNodeLongClickListener {
    void onLongClick(Node node, int position);
}

6、可以使用下面的测试数据进行测试
测试数据如下:

[
 {
   "parentTaskId": "0",
   "taskId": "1",
   "taskName": "福建移动厦门数据中心",
   "taskType": 1,
   "taskFormType": 0
 },
 {
   "parentTaskId": "1",
   "taskId": "2",
   "taskName": "一层",
   "taskType": 2,
   "taskFormType": 0
 },
 {
   "parentTaskId": "2",
   "taskId": "3",
   "taskName": " 桩基",
   "taskType": 3,
   "taskFormType": 1
 },
 {
   "parentTaskId": "2",
   "taskId": "4",
   "taskName": "承台系梁",
   "taskType": 3,
   "taskFormType": 2
 },
 {
   "parentTaskId": "2",
   "taskId": "5",
   "taskName": "墩柱",
   "taskType": 3,
   "taskFormType": 3
 },
 {
   "parentTaskId": "2",
   "taskId": "6",
   "taskName": "盖梁",
   "taskType": 3,
   "taskFormType": 4
 },
 {
   "parentTaskId": "2",
   "taskId": "7",
   "taskName": "箱梁",
   "taskType": 3,
   "taskFormType": 5
 }
]
public class TestDemoAdapter extends TreeRecyclerAdapter {

    public TestDemoAdapter (RecyclerView mTree, Context context, List<Node> datas, int defaultExpandLevel) {
        super(mTree, context, datas, defaultExpandLevel);
    }

    @NonNull
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        GlTreeViewItemBinding binding = DataBindingUtil.inflate(LayoutInflater.from(parent.getContext()), R.layout.gl_tree_view_item, parent, false);
        MyViewHolder holder = new MyViewHolder(binding.getRoot());
        holder.setBinding(binding);
        return holder;
    }

    @Override
    public void onBindViewHolder(Node node, RecyclerView.ViewHolder holder, int position) {
        MyViewHolder myViewHolder = (MyViewHolder) holder;
        myViewHolder.setData(node, position);
    }

    class MyViewHolder extends RecyclerView.ViewHolder {

        GlTreeViewItemBinding binding;

        public GlTreeViewItemBinding getBinding() {
            return binding;
        }

        public void setBinding(GlTreeViewItemBinding binding) {
            this.binding = binding;
        }

        public MyViewHolder(@NonNull View itemView) {
            super(itemView);
        }

        public void setData(Node node, int position) {
            //设置展开关闭的icon
            if (node.getIcon() == -1) {
                binding.imgTreeIcon.setVisibility(View.INVISIBLE);
            } else {
                binding.imgTreeIcon.setVisibility(View.VISIBLE);
                binding.imgTreeIcon.setImageResource(node.getIcon());
            }
            //设置父节点是否显示选择的控件
            binding.imgTreeChoose.setVisibility(View.GONE);
//            if (node.getChildren().size() > 0) {
//                binding.imgTreeChoose.setVisibility(View.GONE);
//            } else {
//                binding.imgTreeChoose.setVisibility(View.VISIBLE);
//            }
            //设置选中状态的icon
//            if (node.isChecked()) {//选中
//                node.setChecked(true);
//                binding.imgTreeChoose.setImageResource(R.drawable.ic_checkbox_checked);
//            } else {//未选中
//                node.setChecked(false);
//                binding.imgTreeChoose.setImageResource(R.drawable.ic_checkbox_uncheck);
//            }

//            binding.imgTreeChoose.setOnClickListener(new View.OnClickListener() {
//                @Override
//                public void onClick(View v) {
//                    boolean isChecked = node.isChecked();
//                    isChecked = !isChecked;
//                    LogUtil.E("是否选中:" + isChecked);
//                    node.setChecked(isChecked);
//                    //设置选中的状态
//                    //setChecked(node, isChecked);
//                    if (isChecked) {
//                        binding.imgTreeChoose.setImageResource(R.drawable.ic_checkbox_checked);
//                    } else {
//                        binding.imgTreeChoose.setImageResource(R.drawable.ic_checkbox_uncheck);
//                    }
//                    //节点选择结果回调接口
//                    if (null != onItemCheckedListener) {
//                        onItemCheckedListener.onSelectedNodes(node, position);
//                    }
//                }
//            });
            binding.textNodeName.setText(node.getName());
        }
    }

    //节点选择回调接口
    public interface OnItemCheckedListener {
        void onSelectedNodes(Node node, int position);
    }

    private OnItemCheckedListener onItemCheckedListener;

    public void setOnItemCheckedListener(OnItemCheckedListener onItemCheckedListener) {
        this.onItemCheckedListener = onItemCheckedListener;
    }
}

创建布局gl_tree_view_item

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="30dp"
        android:background="@drawable/bg_list_item_"
        android:gravity="center_vertical"
        android:orientation="horizontal">

        <ImageView
            android:id="@+id/imgTreeIcon"
            android:layout_width="30dp"
            android:layout_height="30dp"
            android:padding="5dp"
            tools:ignore="ContentDescription" />

        <ImageView
            android:id="@+id/imgTreeChoose"
            android:layout_width="30dp"
            android:layout_height="30dp"
            android:padding="5dp"
            tools:ignore="ContentDescription" />

        <TextView
            android:id="@+id/textNodeName"
            android:layout_width="0dp"
            android:layout_height="30dp"
            android:layout_weight="1"
            android:ellipsize="end"
            android:gravity="center|left"
            android:singleLine="true"
            android:textColor="#333333"
            android:textSize="15sp" />

    </LinearLayout>
</layout>

View层代码自己实现哦

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