Android封装单选与多选

平时开发的时候我们总会碰到这样的需求。

image.png

有时是多选,有时是单选,这样的页面基本都是用RecyclerView来做的,而如果每次做操作的时候都要去写这个单选框/多选框的逻辑,那就太麻烦了,所以我就想把这样的单选/多选列表功能给封装起来

一.思路

这种功能的基本布局页面都是这样

image.png

或者有些需要把选择框放在右边

image.png

我的思路是外层还是用RecyclerView来做,里层(就是实线内)我打算用ViewModel来做,这样既能封装外层的选择框逻辑,对内层也低耦合。

二.效果展示

1.单选模式
15063446875651506344674549.gif
2.多选模式
15063446848301506344654342.gif

PS:左边的布局是个ViewModel可以自由定义,因为时间的问题我没时间再写另外的布局来展示。

三.调用方法

调用方法很简单:

1.定义自己的ViewModel
2.调用
 checkRecyclerView = new CheckRecyclerView.Builder<ChoseShopEntity>(this,"com.berchina.o2omerchant.ui.viewModel.shop.ChoseShopViewModel")
                .setItemDecoration(new XXXItemDecoration(10))
                .setBackgroupColorResID(R.color.divider_grey)
//                .setCheckboxResID(R.drawable.cb_shop_create)
                .setIsRadio(false)
                .builder();
        framelayout.addView(checkRecyclerView.getContentView());

Builder中的第二个参数我传的就是ViewModel的类名。

3.设置数据
checkRecyclerView.setAdapter(datalist);

封装好了用起来就是快,不会每次都要重复去写单选和复选的逻辑。

四.CheckRecyclerView内的操作

我这里把RecyclerView、Adapter和ViewHolder都写在一个类中,仿照RecyclerView源码的写法,当然分开写也行。

先贴全部代码吧

public class CheckRecyclerView<T extends CheckRecyclerView.CheckRecyclerEntity> {

    protected Context context;
    protected RecyclerView recyclerView;
    protected static CheckRecyclerAdapter adapter;
    // 0 表示都不显示,1表示显示左边,2表示显示右边
    protected static int isShow;
    protected static String vmName;
    protected static int checkboxResID;
    protected static boolean isRadio;
    protected static RadioObserver radioObserver; // 监听单选
    protected static MultiObserver multiObserver; // 监听多选

    protected Builder builder;
    // 单选情况下的观察者
    private static Action1<Integer> observer1 = new Action1<Integer>() {
        @Override
        public void call(Integer integer) {
            if (adapter != null){
                adapter.checkChange(integer);
            }
        }
    };
    // 多选情况下的观察者
    private static Action1<Integer> observer2 = new Action1<Integer>() {
        @Override
        public void call(Integer integer) {
            if (adapter != null){
                adapter.multiChose(integer);
            }
        }
    };

    private CheckRecyclerView(Context context,Builder builder){
        this.context = context;
        this.builder = builder;

        this.isShow = builder.isShow;
        this.vmName = builder.vmName;
        this.checkboxResID = builder.checkboxResID;
        this.isRadio = builder.isRadio;
        this.radioObserver = builder.radioObserver;
        this.multiObserver = builder.multiObserver;

        initView();
    }

    private void initView(){
        recyclerView = new RecyclerView(context);
        recyclerView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
        recyclerView.setBackgroundResource(builder.backgroupColorResID);
        recyclerView.setLayoutManager(builder.layoutManager);
        if (builder.itemDecoration != null) {
            recyclerView.addItemDecoration(builder.itemDecoration);
        }
    }

    /**
     *  设置适配器
     */
    public void setAdapter(){
        List<T> datalist = builder.datalist;
        if (datalist == null){
            datalist = new ArrayList<T>();
        }
        adapter = new CheckRecyclerAdapter(context,datalist);

        recyclerView.setAdapter(adapter);
    }

    /**
     *  设置适配器
     */
    public void setAdapter(List<T> datalist){
        adapter = new CheckRecyclerAdapter(context,datalist);

        recyclerView.setAdapter(adapter);
    }

    /**
     * get and set 方法
     */
    public RecyclerView getContentView() {
        return recyclerView;
    }
    public static CheckRecyclerAdapter getAdapter() {
        return adapter;
    }
    public void setIsShow(int isShow) {
        this.isShow = isShow;
    }
    public static void setVmName(String vmName) {
        CheckRecyclerView.vmName = vmName;
    }

    /**
     *  获取到选择的结果
     *  规则:-1表示没有选择
     */
    // 单选左
    public int getLeftRadio(){
        if (adapter != null && adapter.getDatas() != null && adapter.getDatas().size() > 0){
            return adapter.getLeftRadio();
        }
        return  -1;
    }
    // 单选右
    public int getRightRadio(){
        if (adapter != null && adapter.getDatas() != null && adapter.getDatas().size() > 0){
            return adapter.getRightRadio();
        }
        return  -1;
    }
    // 多选左   返回被选中的数组
    public List<Integer> getLeftMulti(){
        List<Integer> multiList = new ArrayList<>();
        if (adapter != null && adapter.getDatas() != null && adapter.getDatas().size() > 0){
            for (int i = 0; i < adapter.getDatas().size(); i++) {
                if (((CheckRecyclerEntity)adapter.getDatas().get(i)).leftCheck == true) {
                    multiList.add(i);
                }
            }
        }
        return multiList;
    }
    // 多选右   返回被选中的数组
    public List<Integer> getRightMulti(){
        List<Integer> multiList = new ArrayList<>();
        if (adapter != null && adapter.getDatas() != null && adapter.getDatas().size() > 0){
            for (int i = 0; i < adapter.getDatas().size(); i++) {
                if (((CheckRecyclerEntity)adapter.getDatas().get(i)).rightCheck == true) {
                    multiList.add(i);
                }
            }
        }
        return multiList;
    }

    /**
     *  判断在多选的情况下是否全部选择
     */
    public boolean isAllChose(){
        Boolean isAllChose = true;
        if (adapter != null && adapter.getDatas() != null && adapter.getDatas().size() > 0) {
            if (isShow == 1) {
                for (int i = 0; i < adapter.getDatas().size(); i++) {
                    if (((CheckRecyclerEntity)adapter.getDatas().get(i)).leftCheck == false){
                        return false;
                    }
                }
            } else if (isShow == 2) {
                for (int i = 0; i < adapter.getDatas().size(); i++) {
                    if (((CheckRecyclerEntity)adapter.getDatas().get(i)).rightCheck == false){
                        return false;
                    }
                }
            } else {
                return false;
            }
        }else {
            isAllChose = false;
        }
        return isAllChose;
    }

    /**
     *  外部控制内部进行单选的操作
     */
    public void setRadio(int position){
        if (adapter != null){
            adapter.checkChange(position);
        }
    }
    /**
     *  设置全选/全不选
     *  规则:true表示全选 false表示全不选
     */
    public void setAllMulti(Boolean bol){
        if (adapter != null){
            adapter.setAllMulti(bol);
        }
    }

    /**
     *  recycelerView 的 adapter
     */
    public static class CheckRecyclerAdapter<T extends CheckRecyclerEntity> extends XXXRecyclerViewBaseAdapter<T>{
        // 记录单选的选项(用空间换时间)
        private int leftRadio = -1;
        private int rightRadio = -1;

        public CheckRecyclerAdapter(Context context, List dataSource) {
            super(context, dataSource);
        }

        @Override
        protected BerRecyclerViewHolder getViewHolder(ViewGroup parent) {
            return new CheckRecyclerViewHolder(LayoutInflater.from(context).inflate(R.layout.item_check_recycler,parent,false),context);
        }

        /**
         * 单选
         */
        public void checkChange(int position){
            if (position < dataSource.size()) {
                // 先将所有都设为未点击
                for (int i = 0; i < dataSource.size(); i++) {
                    dataSource.get(i).leftCheck = false;
                    dataSource.get(i).rightCheck = false;
                }
                // 再设置点击的Item
                if (isShow == 1) {
                    dataSource.get(position).leftCheck = true;
                    leftRadio = position;
                }else if (isShow == 2){
                    dataSource.get(position).rightCheck = true;
                    rightRadio = position;
                }

                // 做响应
                if (radioObserver != null) {
                    radioObserver.radioClick(position, isShow);
                }

                this.notifyDataSetChanged();
            }
        }

        /**
         *  多选
         */
        public void multiChose(int position){
            // 点击后展示相反的情况
            if (isShow == 1) {
                dataSource.get(position).leftCheck = !dataSource.get(position).leftCheck;
            }else if (isShow == 2){
                dataSource.get(position).rightCheck = !dataSource.get(position).rightCheck;
            }

            // 做响应
            if (multiObserver != null) {
                multiObserver.multiClick(position, isShow);
            }

            this.notifyDataSetChanged();
        }

        /**
         * 设置全选
         */
        public void setAllMulti(Boolean bol){
            if (isShow == 1) {
                for (int i = 0; i < dataSource.size(); i++) {
                    dataSource.get(i).leftCheck = bol;
                }
                this.notifyDataSetChanged();
            }else if (isShow == 2){
                for (int i = 0; i < dataSource.size(); i++) {
                    dataSource.get(i).rightCheck = bol;
                }
                this.notifyDataSetChanged();
            }
        }

        // get and set
        public int getLeftRadio() {return leftRadio;}
        public int getRightRadio() {return rightRadio;}
    }

    /**
     *  recycelerView 的 viewholder
     */
    public static class CheckRecyclerViewHolder<T extends CheckRecyclerEntity> extends XXXRecyclerViewHolder<T>{

        @InjectView(R.id.cb_left)
        CheckBox cbLeft;
        @InjectView(R.id.cb_right)
        CheckBox cbRight;
        @InjectView(R.id.fl_content)
        FrameLayout flContent;

        private BaseViewModel viewmodel;


        public CheckRecyclerViewHolder(View itemView, Context context) {
            super(itemView, context);
            ButterKnife.inject(this,itemView);

            if (isShow == 0){
                cbLeft.setVisibility(View.GONE);
                cbRight.setVisibility(View.GONE);
            }else if (isShow == 1){
                cbLeft.setVisibility(View.VISIBLE);
                cbRight.setVisibility(View.GONE);
            }else if (isShow == 2){
                cbLeft.setVisibility(View.GONE);
                cbRight.setVisibility(View.VISIBLE);
            }
            // 设置CheckBox的样式
            if (checkboxResID != 0) {
                cbLeft.setButtonDrawable(checkboxResID);
                cbRight.setButtonDrawable(checkboxResID);
            }
            // 应反射创建viewmodel对象
            try {
                Class<?> myClass = Class.forName(vmName);//完整类名
                Class[] paramTypes = { Context.class };
                Object[] params = {context};
                Constructor con = myClass.getConstructor(paramTypes);
                viewmodel = (BaseViewModel) con.newInstance(params);
                flContent.addView(viewmodel.getContentView());
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }

        }

        @Override
        public void setDataToView() {
            super.setDataToView();
            // 处理选择框的点击 , 只做数据展示,逻辑放在外边处理
            cbLeft.setChecked(data.leftCheck);
            cbRight.setChecked(data.rightCheck);
            //为viewmodel添加数据
            viewmodel.setData(data);
        }

        /**
         *  接下来做 单选 和 多选 的操作  todo 先测试单选看看能不能用
         */
        @OnClick({R.id.cb_left,R.id.cb_right})
        public void itemClick(View v){
            switch (v.getId()){
                case R.id.cb_left:
                    if (isRadio) {
                        // 单选
                        Observable.just(getAdapterPosition()).subscribe(observer1);
                    }else {
                        // 多选
                        Observable.just(getAdapterPosition()).subscribe(observer2);
                    }
                    break;
                case R.id.cb_right:
                    if (isRadio) {
                        Observable.just(getAdapterPosition()).subscribe(observer1);
                    }else {
                        Observable.just(getAdapterPosition()).subscribe(observer2);
                    }
                    break;
            }
        }

    }

    /**
     *  定义两个数据来记录是否点击选择框
     *  规则:调用封装的数据类要继承这个类
     */
    public static class CheckRecyclerEntity{
        public boolean leftCheck;
        public boolean rightCheck;
    }

    /**
     *  =======================================================================
     *   Builder方法
     *  ========================================================================
     */
    public static class Builder<T extends CheckRecyclerView.CheckRecyclerEntity>{

        private Context context;
        private int isShow = 1; // 默认为显示左边的
        private String vmName;
        private List<T> datalist;
        //RecyclerView相关
        private RecyclerView.ItemDecoration itemDecoration;
        private RecyclerView.LayoutManager layoutManager;
        private int backgroupColorResID; //RecyclerView背景颜色,默认为白色
        // checkbox相关
        private int checkboxResID = 0;
        private boolean isRadio = true;// 定义单选(true)和多选(true)
        private RadioObserver radioObserver; // 监听单选
        private MultiObserver multiObserver; // 监听多选

        // 这个一定要传完整 包名+类名
        public Builder(Context context,String vmName){
            this.context = context;
            this.vmName = vmName;

            layoutManager = new LinearLayoutManager(context);
            backgroupColorResID = R.color.white;
        }

        public Builder setIsShow(int isShow) {
            this.isShow = isShow;
            return this;
        }

        public Builder setDatalist(List<T> datalist) {
            this.datalist = datalist;
            return this;
        }

        public Builder setItemDecoration(RecyclerView.ItemDecoration itemDecoration) {
            this.itemDecoration = itemDecoration;
            return this;
        }

        public Builder setLayoutManager(RecyclerView.LayoutManager layoutManager) {
            this.layoutManager = layoutManager;
            return this;
        }

        public Builder setCheckboxResID(int checkboxResID) {
            this.checkboxResID = checkboxResID;
            return this;
        }

        public Builder setBackgroupColorResID(int backgroupColorResID) {
            this.backgroupColorResID = backgroupColorResID;
            return this;
        }

        public Builder setIsRadio(boolean radio) {
            isRadio = radio;
            return this;
        }

        public Builder setRadioObserver(RadioObserver radioObserver) {
            this.radioObserver = radioObserver;
            return this;
        }

        public Builder setMultiObserver(MultiObserver multiObserver) {
            this.multiObserver = multiObserver;
            return this;
        }

        public CheckRecyclerView builder(){
           return new CheckRecyclerView(context,this);
        }

    }

    /**
     *  单选时的响应
     */
    public interface RadioObserver{
        public void radioClick(int position,int isShow);
    }
    /**
     *  多选时的响应
     */
    public interface MultiObserver{
        public void multiClick(int position,int isShow);
    }

}

代码也不能说长,500行这样,其实还好。也只封装了一些基本的功能操作,之后还可以扩展。其实我还算挺良心的,加了很多注解,我个人习惯先写注解再写方法。

基类的XXXRecyclerViewBaseAdapter和XXXRecyclerViewHolder是我自己写的Adapter和ViewHolder的基类


1.基本的流程

我有注解也不一行一行解释,主要是用了Builder模式,在外边使用时可以addView,用自定义写XML控件也行。

定义单选和多选的规则:

(1)protected static int isShow; 0 表示都不显示,1表示显示左边,2表示显示右边,默认为1
(2)protected static boolean isRadio; 表示单选/多选,true表示单选,false表示多选

定义RecyclerView

private void initView(){
        recyclerView = new RecyclerView(context);
        recyclerView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
        recyclerView.setBackgroundResource(builder.backgroupColorResID);
        recyclerView.setLayoutManager(builder.layoutManager);
        if (builder.itemDecoration != null) {
            recyclerView.addItemDecoration(builder.itemDecoration);
        }
    }

数据要继承

 public static class CheckRecyclerEntity{
        public boolean leftCheck;
        public boolean rightCheck;
    }

表示左边是否点击和右边是否点击

最关键是ViewHolder的两个方法:
(1)initView

public CheckRecyclerViewHolder(View itemView, Context context) {
            super(itemView, context);
            ButterKnife.inject(this,itemView);

            if (isShow == 0){
                cbLeft.setVisibility(View.GONE);
                cbRight.setVisibility(View.GONE);
            }else if (isShow == 1){
                cbLeft.setVisibility(View.VISIBLE);
                cbRight.setVisibility(View.GONE);
            }else if (isShow == 2){
                cbLeft.setVisibility(View.GONE);
                cbRight.setVisibility(View.VISIBLE);
            }
            // 设置CheckBox的样式
            if (checkboxResID != 0) {
                cbLeft.setButtonDrawable(checkboxResID);
                cbRight.setButtonDrawable(checkboxResID);
            }
            // 应反射创建viewmodel对象
            try {
                Class<?> myClass = Class.forName(vmName);//完整类名
                Class[] paramTypes = { Context.class };
                Object[] params = {context};
                Constructor con = myClass.getConstructor(paramTypes);
                viewmodel = (BaseViewModel) con.newInstance(params);
                flContent.addView(viewmodel.getContentView());
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }

首先判断isShow 是单选/多选/隐藏 来展示页面。然后设置CheckBox的样式。
最后创建ViewModel对象添加到最上边图中实线的区域中。但是我们展示的页面并不是固定的,也就是说我们会在不同的情况创建不同的viewmodel对象,所以这里不能直接写,我用了反射来添加viewmodel,所以在上边调用的地方传了viewmodel的包名+类名,是为了用类名创建相应的viewmodel对象添加到区域中。

(2)setDataToView

@Override
        public void setDataToView() {
            super.setDataToView();
            // 处理选择框的点击 , 只做数据展示,逻辑放在外边处理
            cbLeft.setChecked(data.leftCheck);
            cbRight.setChecked(data.rightCheck);
            //为viewmodel添加数据
            viewmodel.setData(data);
        }

这个就很简单了,根据leftCheck和rightCheck的值设置左右checkbox的展示,然后给viewmodel设置数据。

2.点击时的处理

做完展示时的处理之后就要做点击时的逻辑处理。

@OnClick({R.id.cb_left,R.id.cb_right})
        public void itemClick(View v){
            switch (v.getId()){
                case R.id.cb_left:
                    if (isRadio) {
                        // 单选
                        Observable.just(getAdapterPosition()).subscribe(observer1);
                    }else {
                        // 多选
                        Observable.just(getAdapterPosition()).subscribe(observer2);
                    }
                    break;
                case R.id.cb_right:
                    if (isRadio) {
                        Observable.just(getAdapterPosition()).subscribe(observer1);
                    }else {
                        Observable.just(getAdapterPosition()).subscribe(observer2);
                    }
                    break;
            }
        }

先判断点左还是点右,再判断是单选模式还是多选模式,我这里用了观察者模式。

 // 单选情况下的观察者
    private static Action1<Integer> observer1 = new Action1<Integer>() {
        @Override
        public void call(Integer integer) {
            if (adapter != null){
                adapter.checkChange(integer);
            }
        }
    };
    // 多选情况下的观察者
    private static Action1<Integer> observer2 = new Action1<Integer>() {
        @Override
        public void call(Integer integer) {
            if (adapter != null){
                adapter.multiChose(integer);
            }
        }
    };

点击之后会在adapter中做响应操作。

/**
         * 单选
         */
        public void checkChange(int position){
            if (position < dataSource.size()) {
                // 先将所有都设为未点击
                for (int i = 0; i < dataSource.size(); i++) {
                    dataSource.get(i).leftCheck = false;
                    dataSource.get(i).rightCheck = false;
                }
                // 再设置点击的Item
                if (isShow == 1) {
                    dataSource.get(position).leftCheck = true;
                    leftRadio = position;
                }else if (isShow == 2){
                    dataSource.get(position).rightCheck = true;
                    rightRadio = position;
                }

                // 做响应
                if (radioObserver != null) {
                    radioObserver.radioClick(position, isShow);
                }

                this.notifyDataSetChanged();
            }
        }

        /**
         *  多选
         */
        public void multiChose(int position){
            // 点击后展示相反的情况
            if (isShow == 1) {
                dataSource.get(position).leftCheck = !dataSource.get(position).leftCheck;
            }else if (isShow == 2){
                dataSource.get(position).rightCheck = !dataSource.get(position).rightCheck;
            }

            // 做响应
            if (multiObserver != null) {
                multiObserver.multiClick(position, isShow);
            }

            this.notifyDataSetChanged();
        }

注释都有,算法也不难,不用详细去讲了。主要封装的逻辑其实也就这些,而其他的方法基本都是和外边交互时用到的方法,我都写了注释。
比如

/**
     *  外部控制内部进行单选的操作
     */
    public void setRadio(int position){
        if (adapter != null){
            adapter.checkChange(position);
        }
    }

这个就是外面要求里面单选哪个。比如说有些需求是要默认选第一个,所以可以在外面调用checkRecyclerView.setRadio(0);

/**
     *  设置全选/全不选
     *  规则:true表示全选 false表示全不选
     */
    public void setAllMulti(Boolean bol){
        if (adapter != null){
            adapter.setAllMulti(bol);
        }
    }

这是设置全选,比如外面点击哪个按钮后里面显示全选,那就在外面调用checkRecyclerView.setAllMulti(true);

其它的什么我都加了注释,就不都说了。

3.Builder的属性

简单介绍在Builder中定义的属性吧
(1)isShow 显示选择框在左还是右
(2)datalist需要适配的数据
(3)itemDecoration RecyclerView的分割线
(4)layoutManager RecyclerView的布局
(5)backgroupColorResID RecyclerView的颜色
(6)checkboxResID Checkbox的样式ID
(7)isRadio 单选还是多选
(8)radioObserver 单选的监听,要实现这个接口
(9)multiObserver 多选的监听
(10)vmName viewmodel的包名+类名
注意要传包名+类名,只传类名不行吗?不行,因为不同的包下可以有相同的类名,我怎么懂是要哪个。

其实我觉得代码也不算多,也不难理解,最近没时间也没能写个demo放到github上,之后闲下来再弄吧。然后也没经过严格的测试,可能一些地方写得不是很好。


更新

后来我用这个东西发现一个错的地方,就是内部类和成员都不要使用static,不然在多处使用的时候会出错。当初习惯性的会下意思使用静态内部类,后来发现用着用着就有问题了。还是太粗心了。
把代码中的静态内部类和静态成员改成非静态的就行。

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

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 170,569评论 25 707
  • 这篇文章分三个部分,简单跟大家讲一下 RecyclerView 的常用方法与奇葩用法;工作原理与ListView比...
    LucasAdam阅读 4,320评论 0 27
  • 前言: 有感毕淑敏老师的巜我所喜爱的女人》,云烟亦写下了巜我喜欢的女人》,可能内容有部分雷同,可能人人喜欢的女...
    当代诗人云烟阅读 274评论 2 1
  • 你是天空中的一滴雨 投影在我心里 你不必感叹 也不必诧异 一瞬间 你打湿了我 相逢在雨中 你轻轻飘来 我用身体身体...
    阁楼书生阅读 289评论 3 5
  • 诺维斯基坚守达拉斯19年,高峰低谷一路走来不离不弃,他从来不是某些人口中的软蛋,现在他是一个真正的城市英雄。 达拉...
    西科南麦阅读 885评论 6 5