Android 屏幕适配总结

为什么需要屏幕适配?
  • 我们为什么需要屏幕适配,而不是屏幕越大应该显示更多的内容呢?不仅是我会有这样的疑问,我相信很多人和我一样有同样的疑,由于在Android开发中,由于Android设备碎片化非常严重,屏幕分辨率不计其数,而想要在各种分辨率的设备上保持UI元素一致的效果,具体表示就是:
    1、屏幕尺寸的碎片化,市面上有720x1280、1080x1920等等。
    2、屏幕的密度,有可能就是屏幕尺寸一样但是它的屏幕的密度是不一样的;
    3、国内厂商比如OPPO、华为等,我们需要去适配的刘海屏和水滴屏等等;

  • 市面上有多各种适配方案,比如谷歌推荐的dp、今日头条、百分比和最小宽度适配等方案,我以前使用最小宽度适配方案,现在不是了,这段时间也在调研适配方案,最后总结出了,一句话,没有最好的适配方案,只有合适的自己方案,我们现在项目就用了多种方案结合使用,感觉效果不错,所以就有了这篇文章,下面来看看一些前置知识。

    ###android.util.TypedValue
    public static float applyDimension(int unit, float value,
                                     DisplayMetrics metrics) {
      switch (unit) {
          case COMPLEX_UNIT_PX:// px = px
              return value;
          case COMPLEX_UNIT_DIP://px = density * dp
              return value * metrics.density;
          case COMPLEX_UNIT_SP:// px = density * dp    默认情况下scaledDensity = density
              return value * metrics.scaledDensity;
          case COMPLEX_UNIT_PT:
              return value * metrics.xdpi * (1.0f / 72);
          case COMPLEX_UNIT_IN:
              return value * metrics.xdpi;
          case COMPLEX_UNIT_MM:
              return value * metrics.xdpi * (1.0f / 25.4f);
      }
      return 0;
    

    }

所以系统中,最后不管你是使用dp还是还是sp最终还是会被转为px,所以我们适配的方案就有了,下面我们来看看一些概念。

android中的dp在渲染前会将dp转为px,计算公式:
  • px = density * dp;
  • density = dpi / 160;
  • px = dp * (dpi / 160);

而dpi是根据屏幕真实的分辨率和尺寸来计算的,每个设备都可能不一样的,我觉得dpi更像是作为一种标准出现。例如Android手机的:160dpi、320dpi、440dpi、480dpi,而density 在每个设备上都是固定的,dpi / 160 = density,屏幕的总 px 宽度 / density = 屏幕的总 dp 宽度。

自定义像素适配

自定义像素适配就是以一个特定宽度和高度尺寸的设备为参考,实际上就以设计师给的设计图为参考,根据这个参考值换算出缩放比例,举个例子:

假设参考尺寸为720px*1280px,


自定义像素.png

我们想要的效果是360x360在720px*1280px的显示的UI元素是屏幕的一般,在1080x1920上显示的是540x540,但是在1080x1920上显示却只有1/3的宽度这并不是我么想要的效果,计算方法:

  • 假如你当前屏幕宽度是720px上显示在一半: 720px / 720px * 360px = 360px;
  • 假如你当前屏幕宽度是1080px上显示在一半: 1080px / 720px * 360px = 540px;
  • 假如你当前屏幕宽度是1920px上显示在一半: 1920px / 720px * 360px = 690px;
  • 假如你当前屏幕宽度是1440px上显示在一半: 1440px / 720px * 360px = 720px;
  • 设计稿的值永远按照720px标准,它会自行缩放,怎么计算,首先我们应该要知道参考尺寸和当前屏幕的尺寸,代码如下:
public class UiPx2PxScaleAdapt {

private static UiPx2PxScaleAdapt mUIAdaptUtil;

private static float DEFAULT_STANDARD_WIDTH = 375;//px
private static float DEFAULT_STANDARD_HEIGHT = 667;

//设计图参考尺寸
private static float standardWidth = DEFAULT_STANDARD_WIDTH;//px
private static float standardHeight = DEFAULT_STANDARD_HEIGHT;

//这里是屏幕显示宽高
private static int mDisplayWidth;
private static int mDisplayHeight;


private UiPx2PxScaleAdapt(Context context) {
    if (mDisplayWidth == 0 || mDisplayHeight == 0) {
        WindowManager manager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
        DisplayMetrics metrics = new DisplayMetrics();
        manager.getDefaultDisplay().getMetrics(metrics);
        if (metrics.widthPixels > metrics.heightPixels) {//横屏
            mDisplayWidth = metrics.heightPixels;
            mDisplayHeight = metrics.widthPixels;
        } else {
            mDisplayWidth = metrics.widthPixels;
            mDisplayHeight = metrics.heightPixels - getStatusBarHeight(context);//为了精确一点呢,可以把状态栏高度减掉
        }
    }
}

public static UiPx2PxScaleAdapt adapt(Context context) {
    if (UiPx2PxScaleAdapt.mUIAdaptUtil == null) {
        synchronized (UiPx2PxScaleAdapt.class) {
            if (UiPx2PxScaleAdapt.mUIAdaptUtil == null) {
                UiPx2PxScaleAdapt.mUIAdaptUtil = new UiPx2PxScaleAdapt(context.getApplicationContext());

            }
        }
    }
    return mUIAdaptUtil;
}

public int getVerticalAdaptResult(int needValuePx) {
    return Math.round((needValuePx * getVerticalScale()));
}

public int getHorizontalAdaptResult(int needValuePx) {
    return Math.round(needValuePx * getHorizontalScale());
}

/**
 * 修改设计图参考尺寸
 *
 * @param standardWidth 设计图参考宽度 单位px
 */
public UiPx2PxScaleAdapt standardWidth(float standardWidth) {
    UiPx2PxScaleAdapt.standardWidth = standardWidth;
    return this;
}


/**
 * 修改设计图参考尺寸
 *
 * @param standardHeight 设计图参考高度 单位px
 */
public UiPx2PxScaleAdapt standardHeight(float standardHeight) {
    UiPx2PxScaleAdapt.standardHeight = standardHeight;
    return this;
}

/**
 * @return 获取水平方向的缩放比例
 */
public float getHorizontalScale() {
    return mDisplayWidth / standardWidth;
}

/**
 * @return 获取垂直方向的缩放比例
 */
public float getVerticalScale() {
    return mDisplayHeight / standardHeight;
}


private int getStatusBarHeight(Context context) {
    int resId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");
    if (resId > 0) {
        return context.getResources().getDimensionPixelSize(resId);
    }
    return 0;
}

}

代码很简单,就是我们通过当前屏幕的宽和高,然后使用当前屏幕的宽和高除以参考尺寸得到缩放比例,然后在使用缩放比例乘以具体设置的尺寸。

<!--基本设计720,想要显示一半填写360px-->
<TextView
    android:layout_width="360px"
    android:layout_height="wrap_content"
    android:background="@color/colorAccent"
    android:text="@string/app_name" />

开发的时候只要按照设计图填写尺寸就行了最后封装成工具类。

public class UiAdaptCalPx2PxUtil {

public static void setTextSize(TextView view, int size) {
    int adaptResult = UiPx2PxScaleAdapt.adapt(view.getContext()).getVerticalAdaptResult(size);
    view.setTextSize(TypedValue.COMPLEX_UNIT_PX, adaptResult);
}

public static void setUIAdaptPx2Px(View dstView,
                                   int width,
                                   int height,
                                   int topMargin,
                                   int bottomMargin,
                                   int lefMargin,
                                   int rightMargin) {

    ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) dstView.getLayoutParams();

    if (width != ViewGroup.LayoutParams.MATCH_PARENT &&
            width != ViewGroup.LayoutParams.WRAP_CONTENT) {
        layoutParams.width =
                UiPx2PxScaleAdapt.adapt(dstView.getContext()).getHorizontalAdaptResult(width);
    } else {
        layoutParams.width = width;
    }


    if (height != ViewGroup.LayoutParams.MATCH_PARENT &&
            height != ViewGroup.LayoutParams.WRAP_CONTENT) {
        layoutParams.height =
                UiPx2PxScaleAdapt.adapt(dstView.getContext()).getVerticalAdaptResult(height);
    } else {
        layoutParams.height = height;
    }

    layoutParams.setMargins(
            UiPx2PxScaleAdapt.adapt(dstView.getContext()).getHorizontalAdaptResult(lefMargin),
            UiPx2PxScaleAdapt.adapt(dstView.getContext()).getVerticalAdaptResult(topMargin),
            UiPx2PxScaleAdapt.adapt(dstView.getContext()).getHorizontalAdaptResult(rightMargin),
            UiPx2PxScaleAdapt.adapt(dstView.getContext()).getVerticalAdaptResult(bottomMargin));

    dstView.setLayoutParams(layoutParams);

    dstView.setPadding(
            UiPx2PxScaleAdapt.adapt(dstView.getContext()).getHorizontalAdaptResult(dstView.getPaddingLeft()),
            UiPx2PxScaleAdapt.adapt(dstView.getContext()).getVerticalAdaptResult(dstView.getPaddingTop()),
            UiPx2PxScaleAdapt.adapt(dstView.getContext()).getHorizontalAdaptResult(dstView.getPaddingRight()),
            UiPx2PxScaleAdapt.adapt(dstView.getContext()).getVerticalAdaptResult(dstView.getPaddingBottom()));
}


/**
 * 在布局中指定尺寸的
 *
 * @param dstViews
 */
public static void setUIAdaptPx2PxLayout(View... dstViews) {
    if (dstViews.length <= 0) return;

    for (View dstView : dstViews) {
        ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) dstView.getLayoutParams();

        if (layoutParams == null) continue;

        setUIAdaptPx2Px(dstView, layoutParams.width, layoutParams.height,
                layoutParams.topMargin, layoutParams.bottomMargin,
                layoutParams.leftMargin, layoutParams.rightMargin
        );
    }
}

}

可能有人会说太麻烦了,那我要开始装逼了,根据上面介绍的,都是需要开发者手动去设置尺寸,那么有没有不用手动去设置呢?方案是人想出来的,一般我们开发者写布局UI都是无非就是会加载到DecorView的contentiew上不管是Dialog还是Activity或者Fragment,我们肯定能拿到content布局,或者DecorView的ContentView,那为何我们不通过content或者ContentView,遍历所有的child,从而修改布局尺寸呢?并且我们还可以把Activity或者Fragment抽取为BaseXXX,当我么加载布局时,做到无感自动完成适配?,但是这种方式有一个缺点就是,会把第三方库也给适配,会造成预想不到的效果,所以建议使用手动的方式。

 /**
 * 把适配放到Base中自动完成适配 指定的是开发者定义的布局的根布局
 *
 * @param contentView 或者开发者在xml中定义的根布局
 */
public static void setUIAdaptPx2PxContentView(View contentView) {
    if (contentView == null) return;
    if (contentView instanceof ViewGroup) {
        //修改ViewGroup本身的
        ViewGroup viewGroup = (ViewGroup) contentView;
        ViewGroup.MarginLayoutParams vplp = (ViewGroup.MarginLayoutParams) viewGroup.getLayoutParams();
        setUIAdaptPx2Px(viewGroup, vplp.width, vplp.height,
                vplp.topMargin, vplp.bottomMargin,
                vplp.leftMargin, vplp.rightMargin
        );

        // 修改child本身的
        int childCount = viewGroup.getChildCount();
        for (int i = 0; i < childCount; i++) {
            View child = viewGroup.getChildAt(i);
            if (child instanceof ViewGroup) {
                setUIAdaptPx2PxContentView(child);
            } else {
                ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) child.getLayoutParams();
                if (layoutParams == null) continue;
                setUIAdaptPx2Px(child, layoutParams.width, layoutParams.height,
                        layoutParams.topMargin, layoutParams.bottomMargin,
                        layoutParams.leftMargin, layoutParams.rightMargin
                );
            }
        }
    }
}

如果你使用Kotlin,那么这样使用:

view.adapt().adaptLeftMargin(13).adaptRightMargin(11).adaptTextSize(11)

Kotlin拓展函数:

fun View.adapt(): AdaptBuilder {
   if (id == NO_ID) id = generateViewId()
    val tag = getTag(id)
   if (tag == null) {
       val builder = AdaptBuilder(this)
       setTag(this.id, builder)
      return builder
  }
    return tag as AdaptBuilder
}

//AdaptBuilder

class AdaptBuilder(private val adaptView: View) {
private val lp: ViewGroup.MarginLayoutParams = adaptView.layoutParams as ViewGroup.MarginLayoutParams

fun adaptWidth(@Px width: Int): AdaptBuilder {
    if (width != ViewGroup.LayoutParams.MATCH_PARENT && width != ViewGroup.LayoutParams.WRAP_CONTENT) {
        lp.width = UiPx2PxScaleAdapt.getHorizontalAdaptResult(width.toFloat())
    } else {
        lp.width = width
    }
    return this
}


fun adaptHeight(@Px height: Int): AdaptBuilder {
    if (height != ViewGroup.LayoutParams.MATCH_PARENT && height != ViewGroup.LayoutParams.WRAP_CONTENT) {
        lp.height = UiPx2PxScaleAdapt.getVerticalAdaptResult(height.toFloat())
    } else {
        lp.height = height
    }
    return this
}


fun adaptTopMargin(@Px topMargin: Int): AdaptBuilder {
    lp.setMargins(
        lp.leftMargin,
        UiPx2PxScaleAdapt.getVerticalAdaptResult(topMargin.toFloat()),
        lp.rightMargin,
        lp.bottomMargin
    )
    return this
}

fun adaptLeftMargin(@Px lefMargin: Int): AdaptBuilder {
    lp.setMargins(
        UiPx2PxScaleAdapt.getHorizontalAdaptResult(lefMargin.toFloat()),
        lp.topMargin,
        lp.rightMargin,
        lp.bottomMargin
    )
    return this
}

fun adaptBottomMargin(@Px bottomMargin: Int): AdaptBuilder {
    lp.setMargins(
        lp.leftMargin,
        lp.topMargin,
        lp.rightMargin,
        UiPx2PxScaleAdapt.getVerticalAdaptResult(bottomMargin.toFloat())
    )
    return this
}

fun adaptRightMargin(@Px rightMargin: Int): AdaptBuilder {
    lp.setMargins(
        lp.leftMargin,
        lp.topMargin,
        UiPx2PxScaleAdapt.getHorizontalAdaptResult(rightMargin.toFloat()),
        lp.bottomMargin
    )
    return this
}

fun adaptPaddingLeft(@Px setPaddingLeft: Int): AdaptBuilder {
    adaptView.setPadding(
        UiPx2PxScaleAdapt.getHorizontalAdaptResult(setPaddingLeft.toFloat()),
        adaptView.paddingTop, adaptView.paddingRight, adaptView.bottom
    )
    return this
}

fun adaptPaddingRight(@Px setPaddingRight: Int): AdaptBuilder {
    adaptView.setPadding(
        adaptView.left,
        adaptView.paddingTop,
        UiPx2PxScaleAdapt.getHorizontalAdaptResult(setPaddingRight.toFloat()),
        adaptView.bottom
    )
    return this
}

fun adaptPaddingTop(@Px setPaddingTop: Int): AdaptBuilder {
    adaptView.setPadding(
        adaptView.left,
        UiPx2PxScaleAdapt.getVerticalAdaptResult(setPaddingTop.toFloat()),
        adaptView.paddingRight,
        adaptView.bottom
    )
    return this
}

fun adaptPaddingBootom(@Px setPaddingBottom: Int): AdaptBuilder {
    adaptView.setPadding(
        adaptView.left,
        adaptView.paddingTop,
        adaptView.paddingRight,
        UiPx2PxScaleAdapt.getVerticalAdaptResult(setPaddingBottom.toFloat())
    )
    return this
}


/**
 *字体缩放比列:按照方向适配,比如:如果你想要在换行时准确,那么就是用水平方向的,否则垂直
 */
fun adaptTextSize(size: Float, isVertical: Boolean = false): AdaptBuilder {
    if (adaptView is TextView) {
        val adaptResult = UiPx2PxScaleAdapt.getVerticalAdaptResult(size)
        val adaptResult2 = UiPx2PxScaleAdapt.getHorizontalAdaptResult(size)
        adaptView.setTextSize(TypedValue.COMPLEX_UNIT_PX, (if (isVertical) adaptResult else adaptResult2).toFloat())
    }
    return this
}


/**
 * 如果是已经完成了加载并绘制了,那么需要调用此方法重新测量绘制
 * 如果没有完成加载可调用可不用
 */
fun buildAdapt(): ViewGroup.MarginLayoutParams {
    adaptView.layoutParams = lp
    return lp
}

}

小结

大家知道我们在代码中填写的尺寸,无非就是width、height、margin和pandding,所以在代码中可以使用这个工具类完成,而使用自定义像素适配的好处就是非常完美的适配所有的尺寸,能够满足同时适配水平和垂直两个方向,而目前是,面上基本就是一个方向上适配,如果你使用Kotlin,就非常的简单了。

修改系统density,densityDpi适配

以前我不知道居然还能修改系统density,densityDpi适配,根据前面的介绍的前置知识,我们知道,不管你在布局填写dp和是sp最终还是转为px,计算公式: px = density * dp, density = dpi / 160, px = dp * (dpi / 160),而density 的意思就是 1 dp 占当前设备多少像素,而屏幕的总 dp 宽度在不同的设备上是会变化的,但是我们在布局中填写的 dp 值却是固定不变的,所以我们能不能根据px = density * dp公式,把dp看成固定设计图的尺寸,那么 density = px/ dp,如果在屏幕的尺寸和我们,即:当前设备屏幕总宽度(单位为px)/ 设计图总宽度(单位为 dp) = density,就是想前面讲的根据:当前设备屏幕总宽度(单位为px) / 设计图总宽度 (单位为 px) * 具体的值(单位为 px) = px是一样的,比如:

  • 假如你当前屏幕宽度是720px,设计图是720dp,要显示的效果是360dp: 720px / 720pdp = 1.0,最终的显示的效果由公式:px = density * dp,1.0 x 360 = 360px

  • 假如你当前屏幕宽度是1080px,设计图是720dp,要显示的效果是360dp: 1080px / 720pdp = 1.5,最终的显示的效果由公式:px = density * dp,1.5 x 360 = 540px

验证的结果就是宽度刚好是屏幕的一半,具体看看那代码:

public class Density {
private static float WIDTH = 375; //参考设备的宽,单位是dp 1440dp / 2 = 187.5dp  居中
private static float appDensity = 0f;
private static float appScaleDensity = 0f;
private static int appDensityDpi = 0;

public static void adaptDensity(Application app, Activity activity) {
    //获取当前app的屏幕显示信息
    DisplayMetrics appMetrics = app.getResources().getDisplayMetrics();
    if (appDensity == 0) {
        appDensity = appMetrics.density;
        appScaleDensity = appMetrics.scaledDensity;
        appDensityDpi = appMetrics.densityDpi;
    }

    Log.e("TAG", appMetrics.widthPixels + "  " + appMetrics.density);

    //计算目标值density, scaleDensity, densityDpi
    float targetDensity = appMetrics.widthPixels / WIDTH;
    // 默认 density 和 scaledDensity 相等
    float targetScaleDensity = targetDensity * (appScaleDensity / appDensity);
    //dpi = density * 160
    int targetDensityDpi = (int) (targetDensity * 160);

    Log.e("TAG", "" + targetDensity);


    //替换Activity的density, scaleDensity, densityDpi
    DisplayMetrics displayMetrics = activity.getResources().getDisplayMetrics();
    displayMetrics.density = targetDensity;
    displayMetrics.scaledDensity = targetScaleDensity;
    displayMetrics.densityDpi = targetDensityDpi;

    Log.e("TAG", displayMetrics.widthPixels + "  " + displayMetrics.density);

}


/**
 * 为了解决对第三方库的适配的影响,我们可以取消Density适配,然后使用自定义像素适配
 * <p>
 * 取消density适配
 *
 * @param activity
 */
public static void cancelAdaptDensity(Activity activity) {
    DisplayMetrics displayMetrics = activity.getResources().getDisplayMetrics();
    displayMetrics.density = appDensity;
    displayMetrics.scaledDensity = appScaleDensity;
    displayMetrics.densityDpi = appDensityDpi;
}
}

这个方案的优点是侵入性和成本低,但是缺点就是,这种修改是全局性的,但凡有一些第三方库的参考尺寸和我们的不一致,那么这个方案就会失效,为了解决对第三方库的适配的影响,我们是可以取消Density适配,然后结合前面讲的自定义像素适配,所以前面我说了没有完美的适配方案,只有更适合,我们项目中就使用两种适配方案结合使用。

总结
  • 1、自定义像素目前我觉得应该是最好的适配方式,能够满足同时适配水平和垂直两个方向,同时不会受一些第三方库影响,缺点就是开发者会写更多的代码,但是我觉得也不算缺点;
  • 2、修改系统density这种适配方案,只能同时适配(水平或垂直)方向,有点是侵入和适配成本低,缺点就是受第三方库影响;
  • 3、百分比布局适配方案,就是根据父容器尺寸作为参考,在View的加载过程中,根据父容器的实际尺寸换算出目标尺寸,在作用到View上,这种方案侵入性强,在适配中还要开发者自行计算比例,成本太高,不能精确的做到是适配,比如我想这个Button距离左边20dp,那百分比就做不到,百分比就是通过父容器的width和height然后剩余指定的百分比,并不能作为任何场景的适配。,感兴趣的可以看着片文章屏幕适配?不如手写个百分比布局
  • 4、最小宽度适配方案,这种方案缺点就是侵入性强,会有引入大量的适配资源,增大app体积;
    最后我想说的是没有完美的适配方案,只有更适合的:
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,835评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,598评论 1 295
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,569评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,159评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,533评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,710评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,923评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,674评论 0 203
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,421评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,622评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,115评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,428评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,114评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,097评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,875评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,753评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,649评论 2 271

推荐阅读更多精彩内容