Android之MaterialDesign使用(一)—— 常用控件的基本使用

Google I/O 2014 发布了Material Design。希望统一 Android平台设计语言规范。然而对于国内的很多产品和设计师而言,并没有对此产生多大的兴趣,也许是习惯,也许是觉得android的碎片化太严重不愿意花时间精力去做适配,所以更多的还是采用IOS的风格,导致尽管Material Design推出了很长时间但是在国内使用的还是比较少。但是就风格层面而言,我觉得Material Design所设计的还是非常不错的,特别是很多新推出的控件,使用起来感觉还是挺不错的,因此,还是很有必要学习一下关于Material Design的知识。

Android Material Design新增常用控件

1、ToolBar和Menu配合使用代替ActionBar
2、基于CoordinatorLayout的联动
3、侧滑抽屉NavigationView
4、卡片布局CardView
5、RecyclerView(在本篇里面不介绍,下一篇单独介绍)
6、TabLayout(配合Fragment使用)
7、弹出提醒SnackBar
8、FloatingActionButton

Toobar

toolbar.gif
准备工作

使用Toolbar之前需要引入依赖

implementation 'androidx.appcompat:appcompat:1.3.0'

Toolbar是android 5.0推出的一个新的导航控件用来取代传统的ActionBar控件。需要注意的是,如果使用Toolbar,需要先将系统的ActionBar去掉,可以通过主题将我们的父级主题设置为NoActionBar,如下所示

<style name="AppTheme" parent="Theme.AppCompat.DayNight.NoActionBar">
    <!-- Customize your theme here. -->
    <item name="colorPrimary">@color/colorPrimary</item>
    <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
    <item name="colorAccent">@color/colorAccent</item>
    <item name="android:navigationBarColor">@android:color/holo_orange_dark</item>
    <item name="android:colorControlHighlight">@android:color/holo_blue_bright</item>
</style>
使用步骤

1、布局里面进行声明

<androidx.appcompat.widget.Toolbar
    android:id="@+id/toolbar"
    android:layout_width="match_parent"
    android:layout_height="?attr/actionBarSize"
    android:background="?attr/colorPrimary"
    android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
    app:layout_scrollFlags="scroll|enterAlways|snap"
    app:popupTheme="@style/ThemeOverlay.AppCompat.Light" />

其中 android:theme主题是Toolbar自身的主题,用于显示的样式,那么app:popupTheme主题是干嘛的呢?其实这和后面讲的Menu有关,我们知道ToolBar很多时候是和Menu一起配合使用的,那么app:popupTheme其实就是定义弹出的Menu的样式。

2、代码里面设置Toolbar

setSupportActionBar(toolbar)

由于我们的父级theme已经将actionBar隐藏掉了,所以我们直接设置就行了。

3、通过代码动态设置Toolbar的属性(也可以直接在XML文件里面进行设置)

private void setToolbarProperty() {
        // 设置正标题
        toolbar.setTitle("正标题");
        // 设置副标题
        toolbar.setSubtitle("副标题");
        // 设置左边按钮图片
        toolbar.setNavigationIcon(R.mipmap.ic_launcher_round);
        // 设置(Log)标题与左边按钮之间图标
        toolbar.setLogo(R.mipmap.ic_launcher);
}

4、添加Menu
首先必须在Activity重写onCreateOptionsMenu方法,添加Menu的操作都在这个方法中执行。

/**
 * 创建菜单
 */
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
    menuInflater.inflate(R.menu.designer, menu)
    return true
}

其中designer的布局文件为

<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <item
        android:id="@+id/icon_one"
        android:icon="@drawable/item"
        android:title="目录"
        app:showAsAction="always" />

    <item
        android:id="@+id/icon_two"
        android:icon="@drawable/share"
        android:title="分享"
        app:showAsAction="ifRoom" />

    <item
        android:id="@+id/icon_three"
        android:icon="@drawable/setting"
        android:title="设置"
        app:showAsAction="never" />

</menu>

在这里面需要重点强调的一点就是showAction属性,它所不同的取值其所代表的意思是:

app:showAsAction="always/ifRoom/never"
always表示永远显示在Toolbar中
ifRoom表示屏幕空间足够的情况下显示在Toolbar中,不够的话就显示在菜单中
never表示永远显示在菜单中

5、监听Menu的点击事件
复写onOptionsItemSelected方法,在这里通过View的ID去执行其所对应的点击事件

/**
 * 响应菜单的点击事件
 */
override fun onOptionsItemSelected(item: MenuItem): Boolean {
    when (item.itemId) {
        R.id.icon_one -> drawer_layout.openDrawer(GravityCompat.START)
        R.id.icon_two -> Toast.makeText(this, "你点击了第二个菜单", Toast.LENGTH_SHORT).show()
        R.id.icon_three -> Toast.makeText(this, "你点击了第三个菜单", Toast.LENGTH_SHORT).show()
    }
    return true
}

基于CoordinatorLayout的联动

CoordinatorLayout的联动算的上是Material Designer的一大亮点,对于CoordinatorLayout来说,其重要的作用就是实现协调其他组件实现联动的效果。


CoordinatorLayout联动.gif

其布局文件为

<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    tools:context=".FruitActivity">

    <com.google.android.material.appbar.AppBarLayout
        android:id="@+id/appBar"
        android:layout_width="match_parent"
        android:layout_height="250dp"
        android:fitsSystemWindows="true">

        <com.google.android.material.appbar.CollapsingToolbarLayout
            android:id="@+id/collapsing_toolbar"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:fitsSystemWindows="true"
            android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
            app:contentScrim="@color/colorPrimaryDark"
            app:layout_scrollFlags="scroll|exitUntilCollapsed">

            <ImageView
                android:id="@+id/fruit_image_view"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:fitsSystemWindows="true"
                android:scaleType="centerCrop"
                app:layout_collapseMode="parallax" />

            <androidx.appcompat.widget.Toolbar
                android:id="@+id/toolbar"
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize"
                app:layout_collapseMode="pin" />

        </com.google.android.material.appbar.CollapsingToolbarLayout>

    </com.google.android.material.appbar.AppBarLayout>

    <androidx.core.widget.NestedScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical">

            <androidx.cardview.widget.CardView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginLeft="15dp"
                android:layout_marginTop="35dp"
                android:layout_marginRight="15dp"
                android:layout_marginBottom="15dp"
                app:cardCornerRadius="4dp">

                <TextView
                    android:id="@+id/fruit_content_text"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_margin="10dp" />

            </androidx.cardview.widget.CardView>

        </LinearLayout>

    </androidx.core.widget.NestedScrollView>

</androidx.coordinatorlayout.widget.CoordinatorLayout>

既然是协调其他组件,那么CoordinatorLayout肯定是作为最顶层布局进行放置。至于其他的组件,这里大概介绍一下:

(1)AppBarLayout:一种支持响应滚动手势的app bar布局,AppBarLayout 继承自LinearLayout,布局方向为垂直方向。所以你可以把它当成垂直布局的LinearLayout来使用。AppBarLayout是在LinearLayou上加了一些材料设计的概念,它可以让你定制当某个可滚动View的滚动手势发生变化时,其内部的子View实现何种动作。

(2)CollapsingToolbarLayout:可折叠的Toolbar。

(3)NestedScrollView:支持嵌套滑动的ScrollView。NestedScrollViewScrollView的区别就在于 NestedScrollView支持嵌套滑动,无论是作为父控件还是子控件,嵌套滑动都支持,且默认开启。因此,在一些需要支持嵌套滑动的情景中,比如一个 ScrollView内部包裹一个 RecyclerView,那么就会产生滑动冲突,这个问题就需要你自己去解决。而如果使用 NestedScrollView包裹 RecyclerView,嵌套滑动天然支持,你无需做什么就可以实现前面想要实现的功能了。

(4)CardView:卡片布局(下面会讲到)。

那么,CoordinatorLayout到底是如何进行联动的呢,它对其他组件进行联动的依据又是什么呢?答案其实很简单,就是通过layout_scrollFlags。layout_scrollFlags的取值有很多,具体来说:

(1)scroll:Child View 伴随着scrollingView的滚动事件而滚出或滚进屏幕。需要强调的是如果使用了其他值,必定要使用这个值才能起作用(比如你想使用enterAlways,则必须使用scroll,否则enterAlways无效)。

(2)exitUntilCollapsed:当你定义了一个minHeight,这个view将在滚动到达这个最小高度的时候消失。

(3)enterAlways:一旦向上滚动这个view就可见。

(4)enterAlwaysCollapsed:当你定义了一个minHeight,那么view将在到达这个最小高度的时候开始显示,并且从这个时候开始慢慢展开,当滚动到顶部的时候展开完。

当然,对于NestedScrollView控件来说,它必须添加app:layout_behavior="@string/appbar_scrolling_view_behavior”,这个属性用于通知AppBarLayoutNestedScrollView何时发生了滚动事件。

基于上面的例子可以总结出CoordinatorLayout联动的两点规律:

1、父布局肯定是CoordinatorLayout
2、一定会设置app:layout_scrollFlagsapp:layout_behavio两个属性。滑动的view官方建议使用NestedScrollView或者RecyclerView。ListView在5.0以下就没有效果了。

侧滑抽屉NavigationView

NavigationView侧滑栏.gif

1.将DrawerLayout作为根布局

<?xml version="1.0" encoding="utf-8"?>
<androidx.drawerlayout.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/drawer_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MaterialDesignerActivity">

    <androidx.coordinatorlayout.widget.CoordinatorLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <com.google.android.material.appbar.AppBarLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content">

            <androidx.appcompat.widget.Toolbar
                android:id="@+id/toolbar"
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize"
                android:background="?attr/colorPrimary"
                android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
                app:layout_scrollFlags="scroll|enterAlways|snap"
                app:popupTheme="@style/ThemeOverlay.AppCompat.Light" />
        </com.google.android.material.appbar.AppBarLayout>

        <androidx.swiperefreshlayout.widget.SwipeRefreshLayout
            android:id="@+id/swipe_refresh_layout"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:layout_behavior="@string/appbar_scrolling_view_behavior">

            <androidx.recyclerview.widget.RecyclerView
                android:id="@+id/recycler_view"
                android:layout_width="match_parent"
                android:layout_height="match_parent" />

        </androidx.swiperefreshlayout.widget.SwipeRefreshLayout>


        <com.google.android.material.floatingactionbutton.FloatingActionButton
            android:id="@+id/fab"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="bottom|end"
            android:layout_margin="30dp"
            android:src="@drawable/action"
            app:elevation="8dp" />

    </androidx.coordinatorlayout.widget.CoordinatorLayout>

    <com.google.android.material.navigation.NavigationView
        android:id="@+id/nav_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        app:headerLayout="@layout/nav_header"
        app:menu="@menu/nav_menu" />


</androidx.drawerlayout.widget.DrawerLayout>

观察布局文件可知,在DrawerLayout布局下有两个视图,一个就是我们刚刚讲到的CoordinatorLayout,还有一个是NavigationView,那么这个NavigationView是做什么用的呢?
NavigationView是Google在5.0之后推出布局控件,放在DrawerLayout中来使用从而实现侧拉效果。DrawerLayout关键的两行布局代码

app:headerLayout="@layout/nav_header"
app:menu="@menu/nav_menu"

那么这两行是什么意思呢?看下面图就明白了


NavigationView.png

其中headerLayout的布局为

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="180dp"
    android:background="?attr/colorPrimary"
    android:padding="10dp">

    <de.hdodenhof.circleimageview.CircleImageView
        android:id="@+id/icon_image"
        android:layout_width="70dp"
        android:layout_height="70dp"
        android:layout_centerInParent="true"
        android:src="@drawable/default_icon" />


    <TextView
        android:id="@+id/username"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:text="zhoufan@gmail.com"
        android:textSize="14sp"
        android:textColor="#FFF"/>

    <TextView
        android:id="@+id/mail"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_above="@id/username"
        android:text="Green"
        android:textSize="14sp"
        android:textColor="#FFF"/>

</RelativeLayout>

与我们平时写的布局一致。

Menu布局是一个菜单布局

<menu xmlns:android="http://schemas.android.com/apk/res/android">

    <group android:checkableBehavior="single">

        <item
            android:id="@+id/nav_call"
            android:icon="@drawable/call"
            android:title="Call" />

        <item
            android:id="@+id/nav_friend"
            android:icon="@drawable/friend"
            android:title="Friend" />
        <item
            android:id="@+id/nav_location"
            android:icon="@drawable/location"
            android:title="Location" />
        <item
            android:id="@+id/nav_mail"
            android:icon="@drawable/mail"
            android:title="Mail" />

    </group>

</menu>

布局设置完成后效果就出来了,手指向右滑动的时候就会把NavigationView所包裹的内容展示出来,当然,你也可以通过代码点击将NavigationView所包裹的内容展示出来。具体的实现为:

// 打开抽屉
drawer_layout.openDrawer(GravityCompat.START)
// 关闭抽屉
nav_view.setNavigationItemSelectedListener {
    drawer_layout.closeDrawers()
    true
}

卡片布局CardView

卡片布局.png

CardView是继承于FrameLayout,用于布局的圆角,阴影等效果的实现,其基本的属性为:

方法 含义
属性 作用
cardElevation 阴影的大小
cardCornerRadius 卡片的圆角大小
contentPadding 卡片内容于边距的间隔

如果直接给CardView添加android:foreground="?attr/selectableItemBackground"就会加上水波纹点击反馈。

TabLayout(配合Fragment使用)

tabLayout.gif

在众多的APP当中,这种效果已经很普遍了,具体来说,就是使用TabLayout + Fragment来实现,其实现的过程为:

1.创建布局文件

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/windowBackground"
    android:orientation="vertical">

    <com.stkj.baselibrary.commonitem.UserItem
        android:id="@+id/search_detail_all_item"
        android:layout_width="match_parent"
        android:layout_height="@dimen/dp44"
        android:layout_marginTop="@dimen/dp44"
        app:item_left_icon_res="@mipmap/back"
        app:item_left_icon_visible="true"
        app:item_middle_text_color="@color/color333333"
        app:item_middle_text_size="@dimen/dp17"
        app:item_middle_text_visible="true" />

    <com.google.android.material.tabs.TabLayout
        android:id="@+id/search_detail_all_tab"
        android:layout_width="match_parent"
        android:layout_height="@dimen/dp44"
        android:background="@color/windowBackground"
        app:tabIndicatorColor="@color/colorE6B536"
        app:tabIndicatorHeight="@dimen/dp2"
        app:tabTextAppearance="@style/App_Theme"
        app:tabTextColor="@color/color999999"
        app:tabBackground="@android:color/transparent"
        app:tabRippleColor="@android:color/transparent"
        app:tabIndicatorFullWidth="false"
        app:tabSelectedTextColor="@color/colorE6B536"/>

    <androidx.viewpager.widget.ViewPager
        android:id="@+id/search_detail_all_view_pager"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</LinearLayout>

2.设置适配器

public class TypeSearchAdapter extends FragmentStatePagerAdapter {

    private FragmentManager mFragmentManager;

    //保存每个Fragment的Tag,刷新页面的依据
    private List<Fragment> fragments;
    private String[] mTitles;

    public TypeSearchAdapter(FragmentManager fm,List<Fragment> fragments, String[] titles) {
        super(fm);
        this.fragments = fragments;
        this.mTitles = titles;
        mFragmentManager = fm;
    }

    @Override
    public Fragment getItem(int position) {
        return fragments.get(position);
    }

    @Override
    public int getCount() {
        return fragments != null ? fragments.size() : 0;
    }

    @Nullable
    @Override
    public CharSequence getPageTitle(int position) {
        return mTitles[position];
    }
}

注意这里使用的是FragmentStatePagerAdapter而非FragmentPagerAdapter。至于为什么,大家可以自行去看看二者的区别在哪。

3.关联

private void init() {
    String[] mTabList = new String[list.size()];
    for (int i = 0; i < list.size(); i++) {
        String title = list.get(i).getTitle();
        mTabList[i] = title;
        mFragmentList.add(TypeSearchDetailAllFragment.newInstance(String.valueOf(list.get(i).getCategoryID()),mTitle,mTitleType));
        if (title.equals(mTitle)) {
            mPosition = i;
        }
    }
    mAdapter = new TypeSearchAdapter(getSupportFragmentManager(), mFragmentList, mTabList);
    mTabLayout.setTabMode(TabLayout.MODE_SCROLLABLE);
    mViewPager.setAdapter(mAdapter);
    mTabLayout.setupWithViewPager(mViewPager);
    mViewPager.setCurrentItem(mPosition, false);
    mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
        @Override
        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {

        }

        @Override
        public void onPageSelected(int position) {
            userItem.getMiddleTv().setText(mTabList[position]);
            mTitle = mTabList[position];
        }

        @Override
        public void onPageScrollStateChanged(int state) {

        }
    });
}

首先实例化好我们需要加载的Fragment,然后通过适配器将它与TabLayout进行关联。这里有两行比较重要的代码

mTabLayout.setTabMode(TabLayout.MODE_SCROLLABLE);
mTabLayout.setupWithViewPager(mViewPager);

第一行代码的意思是设置TabLayout的滚动模式,其可选值有两种:
1》TabLayout.MODE_SCROLLABLE:当元素过多时会超出父布局,并可以滑动Tab,Tab的宽度为Tab的实际宽度。
2》TabLayout.MODE_FIXED:无论界面由多少元素都会充满父布局。并且平均分配Tab的宽度。
第二行代码的意思是将TabLayout与ViewPager相关联,因为ViewPager填充的是Fragment,所以相当于TabLayout与Fragment实现了联动,也就是我们上面看到的效果。当然,除此之外,你还可以在布局文件里面设置TabLayout的一些属性,具体的属性为:

方法 含义
tabSelectedTextColor 设置TabLayout选中时候的颜色
tabTextColor 设置TabLayout未选中时候的颜色
tabIndicatorColor 设置TabLayout指示器的颜色
tabBackground 设置TabLayout背景的颜色
tabTextAppearance 设置TabLayout内部字体大小和样式
tabIndicatorHeight 设置TabLayout指示器的高度
tabRippleColor 设置TabLayout条目点击时候的颜色
tabIndicatorFullWidth 设置TabLayout是否填充满整个宽度

弹出提醒Snackbar

Snackbar.gif

Snackbar的使用和Toast基本类似,其基本的使用为:

仅仅只是提示文字弹出提示文字之后一段时间后会消失。

Snackbar.make(button, "第一次使用SnackBar", Snackbar.LENGTH_SHORT).show();

提供一个可以点击的按钮

Snackbar.make(it, "Data deleted", Snackbar.LENGTH_SHORT).setAction(
    "Undo"
) {
    Toast.makeText(this@MaterialDesignerActivity, "Data restored", Toast.LENGTH_SHORT)
        .show()
}.show()

必须和用户交互之后才消失
Snackbar.make()设置时间的参数改为:Snackbar.LENGTH_INDEFINITE。如果不设置这个参数,Snackbar都会在一段时间后消失。

Snackbar.make(button, "第一次使用SnackBar", Snackbar.LENGTH_INDEFINITE).setAction("确定", new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {

                    }
                }).show();

添加状态回调,监听Snackbar的展示和消失

Snackbar.make(button, "第一次使用SnackBar", Snackbar.LENGTH_INDEFINITE).setAction("确定", new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {

                    }
                }).addCallback(new Snackbar.Callback() {
                    @Override
                    public void onShown(Snackbar sb) {
                        super.onShown(sb);
                        Toast.makeText(MainActivity.this, "弹出了", Toast.LENGTH_SHORT).show();
                    }

                    @Override
                    public void onDismissed(Snackbar transientBottomBar, int event) {
                        super.onDismissed(transientBottomBar, event);
                        Toast.makeText(MainActivity.this, "消失了", Toast.LENGTH_SHORT).show();
                    }
                }).show();

当然,在实际的开发过程中,Snackbar的使用还是比较少的,一般还是更加习惯使用Toast,如果涉及到交互的,很多时候会考虑使用AlertDialog或者PopupWindow而不是Snackbar。

FloatingActionButton

界面浮动的标签,一般用于页面关键功能入口。FloatingActionButton非常简单,知道了解FloatingActionButton的一些属性和点击回调即可。

方法 含义
fabSize 定义FloatingActionButton的大小。auto(大) mini(小) normal(中)
elevation 普通状态下的阴影深度
pressedTranslationZ 按下时的阴影深度
backgroundTint 默认展示的背景颜色
rippleColor 按下时的颜色(5.0以后为水波纹的颜色)
layout_anchor 定位其他控件,和其他控件边界相交
layout_anchorGravity 和layout_anchor属性联用,在其他控件的相对位置
useCompatPadding 设置内边距

点击事件监听

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

推荐阅读更多精彩内容