Android高德地图开发(四)自定义离线地图下载

一、概述

通过前几章我们对高德地图的操作有了基本的了解,后面将介绍如何获取高德地图的一些数据,例如:POI数据,公交数据,天气数据等等。这些都是高德地图为我们设计好了API,只需要调用就好了。不过本章要讲的是怎么自定义折叠列表并下载相应的城市地图,在此感谢 鸿洋 大神提供的折叠列表的思路。

二、本章内容

--- 下载离线地图
--- 折叠列表
1.首先,我们需要获取到城市数据,再高德地图API中OfflineMapManager类主要用于管理离线地图的下载,数据的获取等功能。
OfflineMapManager(Context ctx, OfflineMapManager.OfflineMapDownloadListener listener);
从OfflineMapManager的构造函数可以知道,第一个参数是一个上下文,第二个参数是监听下载地图的接口。我们实现这个接口必须实现3个方法

    @Override
    public void onDownload(int i, int i1, String s) {
        //实现高德地图离线下载的接口
    }

    @Override
    public void onCheckUpdate(boolean b, String s) {
        //实现高德地图离线下载的接口
    }

    @Override
    public void onRemove(boolean b, String s, String s1) {
        //实现高德地图离线下载的接口
    }

可以从方法名称中看出大致的作用,这里不过多描述。有了OfflineMapManager实例,下一步就是获取高德地图的城市数据

//                //获取城市列表
//                OfflineMapManager.getOfflineMapCityList()
//                //获取省份列表
//                OfflineMapManager.getOfflineMapProvinceList()
//                //获取已下载的城市列表
//                OfflineMapManager.getDownloadOfflineMapCityList()
//                //获取正在下载的城市列表
//                OfflineMapManager.getDownloadingCityList()

2.折叠列表实现,有了数据那么接下来开始将折叠列表,这里我使用RecyclerView来实现,至于还在用ListView的同学请使用RecyclerView,强烈推荐(RecyclerView的强大谁用谁知道)。
首先讲一下原理:
我们要实现的效果如下

    /**
     *       1-------根节点未展开
     *       2-------根节点展开
     *          2.1-------子节点未展开
     *          2.2-------子节点未展开
     *          2.3-------子节点展开
     *              2.3.1---子节点未展开
     *              2.3.2---子节点未展开
     *       3-------根节点未展开
     * */

假如我们只有这3级折叠,1,2,3为根节点;2.1,2.2,2.3为根节点2的子节点;同理2.3.1,2.3.2为二级节点中2.3节点的子节点。那么在Android中列表控件显示中,我们只会显示看的见的部分,所以我们在Adapter中需要有两个数据集合。

第一个是所有节点的全部数据集合,第二个就是我们需要展示的数据集合。当我们在点击可展开节点的时候,重新刷新一下展示数据集合中的内容并显示到列表中。这样就解决了数据显示的问题,那么还有一个难题就是数据存放的顺序。

我们知道Android中的列表都是自上而下的一次显示,那么折叠列表中我们怎样进行数据的排序呢?子节点中需要保存父节点,父节点中也必须要有所有子节点的集合。例如:

    /**
     *  new Node(1, 0, "书籍")
     *  new Node(2, 1, "Java从入门到放弃")
     *  new Node(3, 1, "Mysql从入门到删库")
     *  new Node(4, 1, "Mysql从删库到跑路")
     *  new Node(5, 1, "Android从入门到改行")
     *  new Node(6, 1, "PHP")
     *  new Node(7, 0, "文档")
     *  new Node(8, 0, "游戏")
     *  new Node(9, 8, "魔兽世界")
     *  new Node(10, 8, "守望先锋")
     *  new Node(11, 8, "英雄联盟")
     *  new Node(12, 8, "炉石传说")
     *  new Node(13, 0, "程序")
     *  new Node(14, 13, "面向对象的语言")
     *  new Node(15, 14, "Java")
     *  new Node(16, 14, "C++")
     *  new Node(17, 14, "JavaScript")
     *  new Node(18, 14, "PHP")
     *  new Node(19, 13, "面向过程的语言")
     *  new Node(20, 19, "C")
     *
     * */

Node类构造方法第一个参数为序号,第二个参数为父节点序号,第三个参数为该节点的内容,将上面的这些实例按顺序加入到数据集合中,数据就搞定了。

注意上面一段只是为了简明原理举得例子,我们这里下载离线地图还是不一样的,但原理都是这样,理论上这个可以实现很多级折叠,但我建议3级折叠已经够多了,再往上你在处理数据的时候会变得更加复杂。

好了原理明白就直接上代码。
Activity

public class OfflineMapActivity extends BaseActivity implements OfflineMapManager.OfflineMapDownloadListener {


    @BindView(R.id.m_recycler_view)
    RecyclerView mRecyclerView;
    private Unbinder binder;

    //离线地图管理实例
    private OfflineMapManager omm;
    //省份list
    private ArrayList<Province> provinces;

    private TreeAdapter mAdapter;

    @Override
    public void setContentView(@Nullable Bundle savedInstanceState) {
        setContentView(R.layout.activity_offline_map);
        binder = ButterKnife.bind(this);
    }

    @Override
    public void initData() {

        provinces = new ArrayList<>();
        omm = new OfflineMapManager(this, this);
        mAdapter = new TreeAdapter(getApplicationContext());
        mRecyclerView.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false));
        mRecyclerView.addItemDecoration(new LinearLayoutDecoration(this, LinearLayoutManager.VERTICAL));
        mRecyclerView.setAdapter(mAdapter);

        ArrayList<OfflineMapProvince> data = omm.getOfflineMapProvinceList();
        
        //这里我做了一个线程池,你可以把run()方法看成子线程中执行的
        MyApplication.getInstance().getPoolExecutor().execute(new Runnable() {
            @Override
            public void run() {

                String path = Environment.getExternalStorageDirectory() + "/MyMap/maps";
                File file = new File(path);
                if (!file.exists()) {
                    file.mkdirs();
                }

                //自定义离线地图下载存放目录
                MapsInitializer.sdcardDir = path;

//                //获取城市列表
//                OfflineMapManager.getOfflineMapCityList()
//                //获取省份列表
//                OfflineMapManager.getOfflineMapProvinceList()
//                //获取已下载的城市列表
//                OfflineMapManager.getDownloadOfflineMapCityList()
//                //获取正在下载的城市列表
//                OfflineMapManager.getDownloadingCityList()

                // 获取高德地图的数据
                ArrayList<OfflineMapProvince> data = omm.getOfflineMapProvinceList();
                provinces = getProvinceData(data);
                //将解析出来的数据组装成适配器的数据适配器中

                //按顺序装载数据,根节点pid = 0,
                final ArrayList<Node> allNodes = new ArrayList<>();
                int index = 0;
                for (int i = 0; i < provinces.size(); i++) {

                    Node n = new Node(index, 0);
                    n.setParentNode(null);
                    n.setData(provinces.get(i));
                    n.setExpand(false);
                    n.setLevel(1);

                    ArrayList<Node> subNodes = n.getSubNodeList();
                    n.setSubNodeList(subNodes);

                    ArrayList<City> cities = provinces.get(i).getCitys();
                    for (int j = 0; j < cities.size(); j++) {
                        index++;

                        Node one = new Node(index, n.getId());
                        one.setParentNode(n);
                        one.setData(cities.get(j));

                        one.setParentNode(n);
                        one.setLevel(2);
                        one.setExpand(false);
                        subNodes.add(one);
                    }

                    allNodes.add(n);
                    index++;
                }

                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        mAdapter.setData(allNodes);
                    }
                });
            }
        });

        mAdapter.setListener(new TreeAdapter.OnClickListener() {
            @Override
            public void onClick(int position, String cityCode) {
                try {

                    omm.downloadByCityCode(cityCode);
                    mAdapter.setStartDownLoading(position);
                } catch (AMapException e) {
                    e.printStackTrace();
                }
            }

            @Override
            public void pause() {
                if (omm != null) {
                    omm.pause();
                }
            }
        });
    }

    /**
     * 解析数据,也可以直接用高德地图类
     */
    private ArrayList<Province> getProvinceData(ArrayList<OfflineMapProvince> data) {

        ArrayList<Province> result = new ArrayList<>();
        //将获取的省份,城市解析出来
        for (int i = 0; i < data.size(); i++) {

            ArrayList<City> cityList = new ArrayList<>();
            Province p = new Province();
            p.setName(data.get(i).getProvinceName())
                    .setSimplicity(data.get(i).getJianpin())
                    .setFullPinyin(data.get(i).getPinyin())
                    .setProvinceCode(data.get(i).getProvinceCode())
                    .setCitys(cityList);

            ArrayList<OfflineMapCity> cities = data.get(i).getCityList();
            //这里我就不判空操作了,在高德地图源码中已经做了这步
//                    if (cities != null && cities.size() > 0) {
            for (int j = 0; j < cities.size(); j++) {

                City c = new City();
                c.setName(cities.get(j).getCity())
                        .setSimplicity(cities.get(j).getJianpin())
                        .setFullPinyin(cities.get(j).getPinyin())
                        .setCityCode(cities.get(j).getCode())
                        .setProvinceCityCode(cities.get(j).getAdcode());

                cityList.add(c);
            }
//                    }
            result.add(p);
        }
        return result;
    }

    @Override
    protected void onStart() {
        super.onStart();
    }

    @Override
    protected void onResume() {
        super.onResume();
    }

    @Override
    protected void onPause() {
        super.onPause();
    }

    @Override
    protected void onStop() {
        super.onStop();
    }

    @Override
    protected void onDestroy() {

        //注意binder紧跟在super.onDestroy()前一步,如果提前释放后,再释放UI控件会出空指针异常,注意位置
        binder.unbind();
        super.onDestroy();
    }

    @Override
    public void onDownload(int i, int i1, String s) {
        //实现高德地图离线下载的接口
        // i ------- 状态
        // i1 ------- 下载进度
        // s ------- 下载城市
        Log.e("CF", "onDownload: i=" + i + " i1=" + i1 + " s=" + s);
        if (i == 0) {
            //下载进度
            mAdapter.setDownloading(i1, s);
        } else if (i == -1) {
            //下载完成
            mAdapter.downloadComplete(s);
        }
    }

    @Override
    public void onCheckUpdate(boolean b, String s) {
        //实现高德地图离线下载的接口
        Log.e("CF", "onCheckUpdate: b=" + b + " s=" + s);
    }

    @Override
    public void onRemove(boolean b, String s, String s1) {
        //实现高德地图离线下载的接口
        Log.e("CF", "onRemove: b=" + b + " s=" + s + " s1=" + s1);
    }

}

Adapter

public class TreeAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {

    //item布局类型
    private final static int NODE = 0;
    private final static int LEAF = 1;
    //所有数据
    private ArrayList<Node> allNodes;
    //可见数据
    private ArrayList<Node> visibleNodes;

    private LayoutInflater lf; //布局加载
    private Context context;
    //点击接口
    private OnClickListener listener;

    public TreeAdapter(Context context) {
        this.context = context.getApplicationContext();

        lf = LayoutInflater.from(this.context);

        visibleNodes = new ArrayList<>();
    }

    /**
     * 过滤可见数据
     * */
    private void filterVisibleNode() {

        visibleNodes.clear();

        for (int i = 0; i < allNodes.size(); i++) {

            Node one = allNodes.get(i);
            if (one.getPid() == 0 && !one.isSubNode()) {
                visibleNodes.add(one);
                expandNode(one);

            } else if (one.getPid() == 0 && one.isSubNode()) {
                visibleNodes.add(one);
            }
        }
    }

    /**
     * 加入展开子节点的数据,如果子节点还有展开项,继续展开
     * 列如: 1-------
     *       2-------
     *          2.1-------
     *          2.2-------
     *          2.3-------
     *              2.3.1---
     *              2.3.2---
     *       3-------
     *
     * 按顺序将需要显示的展开数据添加到visibleNodes中
     * */
    private void expandNode(Node one) {
        //如果节点不是叶节点,且节点展开
        if (one.isExpand() && !one.isSubNode()) {

            ArrayList<Node> subLists = one.getSubNodeList();
            for (int j = 0; j < subLists.size(); j++) {
                visibleNodes.add(subLists.get(j));
                expandNode(subLists.get(j));
            }
        }
    }

    @NonNull
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {

        //根据节点类型的不同加载不同的Item布局
        if (viewType == NODE) {

            NodeViewHolder holder = new NodeViewHolder(lf.inflate(R.layout.item_ndoe_layout, parent, false));
            return holder;

        } else if (viewType == LEAF) {

            LeafViewHolder holder = new LeafViewHolder(lf.inflate(R.layout.item_leaf_layout, parent, false));
            return holder;
        }
        return null;
    }

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

        if (holder instanceof NodeViewHolder) {

            //带扩展数据的item设置
            if (visibleNodes.get(position).isExpand()) {
                ((NodeViewHolder) holder).itemImag.setImageResource(R.drawable.triangle_bottom);
            } else {
                ((NodeViewHolder) holder).itemImag.setImageResource(R.drawable.triangle_right);
            }
            ((NodeViewHolder) holder).itemLayout.setPadding(visibleNodes.get(position).getLevel() * 30, 5, 5, 5);

            final Province p = (Province) visibleNodes.get(position).getData();
            ((NodeViewHolder) holder).itemText.setText(p.getName());

            ((NodeViewHolder) holder).itemLayout.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {

                    int id = visibleNodes.get(position).getId();
                    for (int i = 0; i < allNodes.size(); i++) {
                        if (allNodes.get(i).getId() == id) {

                            if (allNodes.get(i).isExpand()) {
                                allNodes.get(i).setExpand(false);
                            } else {
                                allNodes.get(i).setExpand(true);
                            }
                            break;
                        }
                    }
                    filterVisibleNode();
                    notifyDataSetChanged();
                }
            });

        } else if (holder instanceof LeafViewHolder) {

            //子叶节点,就是不可以再展开的节点item布局
            final City c = (City) visibleNodes.get(position).getData();

            ((LeafViewHolder) holder).itemText.setText(c.getName());
            ((LeafViewHolder) holder).itemLayout.setPadding(20 + visibleNodes.get(position).getLevel() * 20, 5, 5, 0);
            ((LeafViewHolder) holder).itemLeafDownBtn.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    if (listener != null) {
                        if (!visibleNodes.get(position).isLoading()) {

                            listener.onClick(position, c.getCityCode());
                            visibleNodes.get(position).setLoading(true);
                        } else {
                            listener.pause();
                            visibleNodes.get(position).setLoading(false);
                        }
                        notifyDataSetChanged();
                    }
                }
            });

            if (visibleNodes.get(position).isLoading()) {
                ((LeafViewHolder) holder).itemLeafDownBtn.setBackgroundResource(R.drawable.pause);
            } else {
                ((LeafViewHolder) holder).itemLeafDownBtn.setBackgroundResource(R.drawable.dwonload);
            }


            if (visibleNodes.get(position).isVisible()) {
                ((LeafViewHolder) holder).itemLeafDownloadingLayout.setVisibility(View.VISIBLE);
                ((LeafViewHolder) holder).itemLeafPercentText.setText(visibleNodes.get(position).getPercent() + "%");
                ((LeafViewHolder) holder).itemLeafProgressbar.setProgress(visibleNodes.get(position).getPercent());
            } else {
                ((LeafViewHolder) holder).itemLeafDownloadingLayout.setVisibility(View.GONE);
            }

            if (visibleNodes.get(position).isDowned()) {
                ((LeafViewHolder) holder).itemLeafDownBtn.setVisibility(View.INVISIBLE);
                ((LeafViewHolder) holder).itemLeafDownText.setVisibility(View.VISIBLE);
            } else {
                ((LeafViewHolder) holder).itemLeafDownBtn.setVisibility(View.VISIBLE);
                ((LeafViewHolder) holder).itemLeafDownText.setVisibility(View.INVISIBLE);
            }
        }

    }

    @Override
    public int getItemCount() {
        return visibleNodes == null ? 0 : visibleNodes.size();
    }

    @Override
    public int getItemViewType(int position) {
        if (visibleNodes.get(position).isSubNode()) {
            return LEAF;
        } else {
            return NODE;
        }
    }

    /**
     * 数据设置更新
     * */
    public void setData(ArrayList<Node> allNodes) {
        this.allNodes = allNodes;

        filterVisibleNode();

        notifyDataSetChanged();
    }

    /**
     * 显示下载进度
     * */
    public void setDownloading(int percent, String cityName) {

        for (Node one : visibleNodes) {
            if (one.isSubNode()) {
                City c = (City) one.getData();
                if (c.getName().equals(cityName)) {
                    one.setPercent(percent);
                    one.setVisible(true);
                    break;
                }
            }
        }

        notifyDataSetChanged();
    }

    /**
     * 下载完成
     * */
    public void downloadComplete(String cityName) {

        for (Node one : visibleNodes) {
            if (one.isSubNode()) {

                City c = (City) one.getData();
                if (c.getName().equals(cityName)) {
                    one.setVisible(false);
                    one.setDowned(true);
                    break;
                }
            }
        }
        notifyDataSetChanged();
    }

    /**
     * 设置显示下载
     * */
    public void setStartDownLoading(int position) {

        visibleNodes.get(position).setVisible(true);
        notifyDataSetChanged();
    }

    public class NodeViewHolder extends RecyclerView.ViewHolder {

        private LinearLayout itemLayout;
        private ImageView itemImag;
        private TextView itemText;

        public NodeViewHolder(View itemView) {
            super(itemView);

            itemLayout = itemView.findViewById(R.id.item_node_layout);
            itemImag = itemView.findViewById(R.id.item_node_img);
            itemText = itemView.findViewById(R.id.item_node_content);
        }
    }

    public class LeafViewHolder extends RecyclerView.ViewHolder {

        private ConstraintLayout itemLayout;
        private TextView itemText;
        private TextView itemLeafPercentText;
        private ProgressBar itemLeafProgressbar;
        private RelativeLayout itemLeafDownloadingLayout;

        private ImageView itemLeafDownBtn;
        private TextView itemLeafDownText;

        public LeafViewHolder(View itemView) {
            super(itemView);
            itemLayout = itemView.findViewById(R.id.item_leaf_layout);
            itemText = itemView.findViewById(R.id.item_leaf_content);
            itemLeafPercentText = (TextView) itemView.findViewById(R.id.item_leaf_percent_text);
            itemLeafProgressbar = (ProgressBar) itemView.findViewById(R.id.item_leaf_progressbar);
            itemLeafDownloadingLayout = (RelativeLayout) itemView.findViewById(R.id.item_leaf_downloading_layout);
            itemLeafDownBtn = (ImageView) itemView.findViewById(R.id.item_leaf_down_btn);
            itemLeafDownText = (TextView) itemView.findViewById(R.id.item_leaf_down_text);
        }
    }

    public void setListener(OnClickListener listener) {
        this.listener = listener;
    }

    public interface OnClickListener {
        void onClick(int position, String cityCode);

        void pause();
    }
}

数据封装类

public class City {

    //城市名称
    private String name;
    //城市代码
    private String cityCode;
    //简拼
    private String simplicity;
    //全拼
    private String fullPinyin;
    //与省份的关系代码,注:跟省份代码不同
    private String provinceCityCode;

    public City() {
        this.name = "";
        this.cityCode = "";
        this.simplicity = "";
        this.fullPinyin = "";
        this.provinceCityCode = "";
    }

    public String getName() {
        return name;
    }

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

    public String getCityCode() {
        return cityCode;
    }

    public City setCityCode(String cityCode) {
        this.cityCode = cityCode;
        return this;
    }

    public String getSimplicity() {
        return simplicity;
    }

    public City setSimplicity(String simplicity) {
        this.simplicity = simplicity;
        return this;
    }

    public String getFullPinyin() {
        return fullPinyin;
    }

    public City setFullPinyin(String fullPinyin) {
        this.fullPinyin = fullPinyin;
        return this;
    }

    public String getProvinceCityCode() {
        return provinceCityCode;
    }

    public City setProvinceCityCode(String provinceCityCode) {
        this.provinceCityCode = provinceCityCode;
        return this;
    }
}


public class Province {

    //省份名字
    private String name;
    //简拼
    private String simplicity;
    //全拼
    private String fullPinyin;
    //省份代码
    private String provinceCode;
    //省份下的城市
    private ArrayList<City> citys;

    public Province() {
        this.name = "";
        this.simplicity = "";
        this.fullPinyin = "";
        this.provinceCode = "";
        this.citys = new ArrayList<>();
    }

    public String getName() {
        return name;
    }

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

    public String getSimplicity() {
        return simplicity;
    }

    public Province setSimplicity(String simplicity) {
        this.simplicity = simplicity;
        return this;
    }

    public String getFullPinyin() {
        return fullPinyin;
    }

    public Province setFullPinyin(String fullPinyin) {
        this.fullPinyin = fullPinyin;
        return this;
    }

    public String getProvinceCode() {
        return provinceCode;
    }

    public Province setProvinceCode(String provinceCode) {
        this.provinceCode = provinceCode;
        return this;
    }

    public ArrayList<City> getCitys() {
        return citys;
    }

    public Province setCitys(ArrayList<City> citys) {
        this.citys = citys;
        return this;
    }
}

public class Node {

    //当前id
    private int id;
    //父id
    private int pid;
    //等级
    private int level;
    //数据,此处放置的对应item的数据,在使用时,一定要直到存放的什么数据,否则强转出错
    private Object data;
    //父节点实例
    private Node parentNode;
    //子节点集合
    private ArrayList<Node> subNodeList;
    //展开状态
    private boolean isExpand;

    //-------------------------------这下面成员变量是扩展用,根据需求变更,主要的是在上面那些成员变量必须要-----------------------------------------
    //百分比
    private int percent;
    //是否可见进度条
    private boolean isVisible;
    //加载状态
    private boolean isLoading;
    //下载完成状态
    private boolean isDowned;

    public Node(int id, int pid) {
        this.id = id;
        this.pid = pid;
        this.parentNode = null;
        this.data = null;
        this.subNodeList = new ArrayList<>();
        this.isExpand = false;
        this.percent = 0;
        this.isVisible = false;
        this.isLoading = false;
        this.isDowned = false;
    }

    public int getId() {
        return id;
    }

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

    public int getPid() {
        return pid;
    }

    public void setPid(int pid) {
        this.pid = pid;
    }

    public int getLevel() {
        return level;
    }

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

    public Object getData() {
        return data;
    }

    public void setData(Object data) {
        this.data = data;
    }

    public Node getParentNode() {
        return parentNode;
    }

    public void setParentNode(Node parentNode) {
        this.parentNode = parentNode;
    }

    public ArrayList<Node> getSubNodeList() {
        return subNodeList;
    }

    public void setSubNodeList(ArrayList<Node> subNodeList) {
        this.subNodeList = subNodeList;
    }

    public boolean isSubNode() {
        return subNodeList.size() == 0;
    }

    public boolean isExpand() {
        return isExpand;
    }

    public void setExpand(boolean expand) {
        isExpand = expand;
    }

    public int getPercent() {
        return percent;
    }

    public void setPercent(int percent) {
        this.percent = percent;
    }

    public boolean isVisible() {
        return isVisible;
    }

    public void setVisible(boolean visible) {
        isVisible = visible;
    }

    public boolean isLoading() {
        return isLoading;
    }

    public void setLoading(boolean loading) {
        isLoading = loading;
    }

    public boolean isDowned() {
        return isDowned;
    }

    public void setDowned(boolean downed) {
        isDowned = downed;
    }
}

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

推荐阅读更多精彩内容

  • 国家电网公司企业标准(Q/GDW)- 面向对象的用电信息数据交换协议 - 报批稿:20170802 前言: 排版 ...
    庭说阅读 10,852评论 6 13
  • 来吧,我们先来看一下原版的微信发送位置 嗯,是发送位置,为什么不带发送实时位置,缺个另外一个真机。嗯,买一个16年...
    王培921223阅读 1,287评论 0 2
  • feisky云计算、虚拟化与Linux技术笔记posts - 1014, comments - 298, trac...
    不排版阅读 3,813评论 0 5
  • 昏暗的灯光, 若即若离, 书影交织, 朗朗书声, 掩盖不了心中的那丝悸动, 不忍心,让背影离去。 灯熄、影灭、梦破...
    Mmzer阅读 273评论 0 0
  • 文/简故北 六月雨,陶黎戴着耳机打一把蓝色的雨伞从嘉年华的广场而过,耳机里正在播放李荣浩的《不将就》… 那时候我以...
    简故北阅读 260评论 1 4