Android 8.0 状态栏信号显示、信号定制

之前发了下拉通知栏开关修改的一篇文章。
这篇文章呢,主要介绍一下Android状态栏信号图标显示的流程。
便于在Android源生上开发的碰到问题的朋友,希望能对读者有所帮助。内容可能比较长
首先还是先介绍布局。

一、状态栏信号布局

路径:android\frameworks\base\packages\SystemUI\res\layout\status_bar.xml
如下代码就是状态栏的布局,包含:时钟、通知、system_icons等。

<?xml version="1.0" encoding="utf-8"?>
<com.android.systemui.statusbar.phone.PhoneStatusBarView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:systemui="http://schemas.android.com/apk/res/com.android.systemui"
    android:layout_width="match_parent"
    android:layout_height="@dimen/status_bar_height"
    android:id="@+id/status_bar"
    android:background="@drawable/system_bar_background"
    android:orientation="vertical"
    android:focusable="false"
    android:descendantFocusability="afterDescendants"
    >

    <ImageView
        android:id="@+id/notification_lights_out"
        android:layout_width="@dimen/status_bar_icon_size"
        android:layout_height="match_parent"
        android:paddingStart="6dip"
        android:paddingBottom="2dip"
        android:src="@drawable/ic_sysbar_lights_out_dot_small"
        android:scaleType="center"
        android:visibility="gone"
        />

    <LinearLayout android:id="@+id/status_bar_contents"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:paddingStart="6dp"
        android:paddingEnd="8dp"
        android:orientation="horizontal"
        >

        <!-- The alpha of this area is controlled from both PhoneStatusBarTransitions and
             PhoneStatusBar (DISABLE_NOTIFICATION_ICONS). -->
        <com.android.systemui.statusbar.AlphaOptimizedFrameLayout
            android:id="@+id/notification_icon_area"
            android:layout_width="0dip"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:orientation="horizontal" />

        <com.android.keyguard.AlphaOptimizedLinearLayout android:id="@+id/system_icon_area"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:orientation="horizontal"
            >
            <include layout="@layout/system_icons" />
            <com.android.systemui.statusbar.policy.Clock
                android:id="@+id/clock"

     android:textAppearance="@style/TextAppearance.StatusBar.Clock"
                android:layout_width="wrap_content"
                android:layout_height="match_parent"
                android:singleLine="true"
                android:paddingStart="@dimen/status_bar_clock_starting_padding"
                android:paddingEnd="@dimen/status_bar_clock_end_padding"
                android:gravity="center_vertical|start"
                />
        </com.android.keyguard.AlphaOptimizedLinearLayout>
    </LinearLayout>
    <ViewStub
        android:id="@+id/emergency_cryptkeeper_text"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout="@layout/emergency_cryptkeeper_text"
    />
</com.android.systemui.statusbar.phone.PhoneStatusBarView>

~
~
system_icons:信号区域:signal_cluster_view、状态栏电池,statusIcons(蓝牙、飞行模式statusIcons)。
system_icons内容如下图:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/system_icons"
    android:layout_width="wrap_content"
    android:layout_height="match_parent"
    android:gravity="center_vertical">
    <com.android.keyguard.AlphaOptimizedLinearLayout android:id="@+id/statusIcons"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:gravity="center_vertical"
        android:orientation="horizontal"/>
    <include layout="@layout/signal_cluster_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="@dimen/signal_cluster_margin_start"/>

    <com.android.systemui.BatteryMeterView android:id="@+id/battery"
        android:layout_height="match_parent"
        android:layout_width="wrap_content"
        />
</LinearLayout>

~
signal_cluster_view有对应的布局,代码上写了,内容如下,内容较多、我就只截取部分展示了。其中mobile_signal_group就是信号区域了,它也对应一个布局 mobile_signal_group.xmlmobile_signal_group中主要是信号图标、数据流量箭头等位置显示了。

   <LinearLayout
       android:id="@+id/mobile_signal_group"
       android:layout_height="wrap_content"
       android:layout_width="wrap_content"
       >
   </LinearLayout>
   <FrameLayout
       android:id="@+id/no_sims_combo"
       android:layout_height="wrap_content"
       android:layout_width="wrap_content"
       android:contentDescription="@string/accessibility_no_sims">
       <com.android.systemui.statusbar.AlphaOptimizedImageView
           android:theme="?attr/lightIconTheme"
           android:id="@+id/no_sims"
           android:layout_height="wrap_content"
           android:layout_width="wrap_content"
           android:src="@drawable/stat_sys_no_sims"
           />
       <com.android.systemui.statusbar.AlphaOptimizedImageView
           android:theme="?attr/darkIconTheme"
           android:id="@+id/no_sims_dark"
           android:layout_height="wrap_content"
           android:layout_width="wrap_content"
           android:src="@drawable/stat_sys_no_sims"
           android:alpha="0.0"
           />
   </FrameLayout>

~

mobile_signal_group.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:systemui="http://schemas.android.com/apk/res-auto"
    android:id="@+id/mobile_combo"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:orientation="horizontal"
    >
    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="2dp"
        android:id="@+id/data_inout"
        android:visibility="gone"
        />
    <FrameLayout
        android:layout_height="17dp"
        android:layout_width="wrap_content">
        <ImageView
            android:id="@+id/mobile_in"
            android:layout_height="wrap_content"
            android:layout_width="wrap_content"
            android:src="@drawable/ic_activity_down"
            android:visibility="gone"
            android:paddingEnd="2dp"
            />
        <ImageView
            android:id="@+id/mobile_out"
            android:layout_height="wrap_content"
            android:layout_width="wrap_content"
            android:src="@drawable/ic_activity_up"
            android:paddingEnd="2dp"
            android:visibility="gone"
            />
    </FrameLayout>
    <FrameLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        >
        <FrameLayout
            android:id="@+id/mobile_signal_single"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            >
            <com.android.systemui.statusbar.AnimatedImageView
                android:theme="@style/DualToneLightTheme"
                android:id="@+id/mobile_signal"
                android:layout_height="wrap_content"
                android:layout_width="wrap_content"
                systemui:hasOverlappingRendering="false"
                />
            <com.android.systemui.statusbar.AnimatedImageView
                android:theme="@style/DualToneDarkTheme"
                android:id="@+id/mobile_signal_dark"
                android:layout_height="wrap_content"
                android:layout_width="wrap_content"
                android:alpha="0.0"
                systemui:hasOverlappingRendering="false"
                />
            <ImageView
                android:id="@+id/mobile_type"
                android:layout_height="wrap_content"
                android:layout_width="wrap_content"
                />
            <ImageView
                android:id="@+id/mobile_roaming"
                android:layout_width="wrap_content"
                android:layout_height="17dp"
                android:paddingStart="22dp"
                android:paddingTop="1.5dp"
                android:paddingBottom="3dp"
                android:scaleType="fitCenter"
                android:src="@drawable/stat_sys_roaming"
                android:contentDescription="@string/accessibility_data_connection_roaming"
                android:visibility="gone" />
        </FrameLayout>
        <LinearLayout
            android:id="@+id/mobile_signal_stacked"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:orientation="vertical"
            android:visibility="gone"
            >
            <ImageView
                android:id="@+id/mobile_signal_data"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                />
            <ImageView
                android:id="@+id/mobile_signal_voice"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                />
        </LinearLayout>
    </FrameLayout>
</LinearLayout>

二、状态栏信号显示流程

接下来是信号的显示流程,android 8.0开始,与之前的android版本信号显示不太一样了。8.0源生代码中信号图标都是代码画出来的,并非与之前版本一样,直接使用图片显示了。所以,在开发中,我需要自己去修改,定制自己的信号样式,显示规格。
SignalClusterView.java
路径:frameworks\base\package\SystemUI\src\com\android\systemui\statusbar\SignalClusterView.java。它对应布局signal_cluster_view.xml,刚刚上面介绍过。
SignalClusterView.java完成控件的初始化、赋予图片显示。
下面,来说说它的逻辑。
SignalClusterView.java中有个内部类:PhoneState。
PhoneState中的apply()方法中对信号控件进行赋值。
其中mMobile就是信号控件
如下代码。

 public boolean apply(boolean isSecondaryIcon) {
            if (mMobileVisible && !mIsAirplaneMode) {
                if (mLastMobileStrengthId != mMobileStrengthId) {
                    if (mReadIconsFromXML) {
                       setIconForView(mMobile, mMobileStrengthId);
                        setIconForView(mMobileDark, mMobileStrengthId);

                    } else {
                        mMobile.getDrawable().setLevel(mMobileStrengthId);
                        mMobileDark.getDrawable().setLevel(mMobileStrengthId);
                    }
                    mLastMobileStrengthId = mMobileStrengthId;
                }

                if (mLastMobileTypeId != mMobileTypeId) {
                    mMobileType.setImageResource(mMobileTypeId);
                    mLastMobileTypeId = mMobileTypeId;
                }

                mDataActivity.setImageResource(mDataActivityId);

                if (mStackedDataId != 0 && mStackedVoiceId != 0) {
                    mStackedData.setImageResource(mStackedDataId);
                    mStackedVoice.setImageResource(mStackedVoiceId);
                    mMobileSingleGroup.setVisibility(View.GONE);
                    mMobileStackedGroup.setVisibility(View.VISIBLE);
                } else {
                    mStackedData.setImageResource(0);
                    mStackedVoice.setImageResource(0);
                    mMobileSingleGroup.setVisibility(View.VISIBLE);
                    mMobileStackedGroup.setVisibility(View.GONE);
                }

                mMobileGroup.setContentDescription(mMobileTypeDescription
                        + " " + mMobileDescription);
                mMobileGroup.setVisibility(View.VISIBLE);
            } else {
                mMobileGroup.setVisibility(View.GONE);
            }

            // When this isn't next to wifi, give it some extra padding between the signals.
            mMobileGroup.setPaddingRelative(isSecondaryIcon ? mSecondaryTelephonyPadding : 0,
                    0, 0, 0);
            mMobile.setPaddingRelative(
                    mIsMobileTypeIconWide ? mWideTypeIconStartPadding : mMobileDataIconStartPadding,
                    0, 0, 0);
            mMobileDark.setPaddingRelative(
                    mIsMobileTypeIconWide ? mWideTypeIconStartPadding : mMobileDataIconStartPadding,
                    0, 0, 0);

            if (DEBUG) Log.d(TAG, String.format("mobile: %s sig=%d typ=%d",
                        (mMobileVisible ? "VISIBLE" : "GONE"), mMobileStrengthId, mMobileTypeId));

            mMobileType.setVisibility(mMobileTypeId != 0 ? View.VISIBLE : View.GONE);
            mMobileRoaming.setVisibility((mRoaming && !mReadIconsFromXML)? View.VISIBLE : View.GONE);
            mMobileActivityIn.setVisibility(mActivityIn ? View.VISIBLE : View.GONE);
            mMobileActivityOut.setVisibility(mActivityOut ? View.VISIBLE : View.GONE);
            mDataActivity.setVisibility(mDataActivityId != 0 ? View.VISIBLE : View.GONE);

            return mMobileVisible;
        }

~
mReadIconsFromXML:是定义在config.xml中config_read_icons_from_xml的值,顾名思义。
setIconForView:设置图片,mIconScaleFactor是定义在dimen.xml中的值:status_bar_icon_scale_factor;

下面是setIconForView

 private void setIconForView(ImageView imageView, @DrawableRes int iconId) {
        // Using the imageView's context to retrieve the Drawable so that theme is preserved.
        Drawable icon = imageView.getContext().getDrawable(iconId);

        if (mIconScaleFactor == 1.f) {
            imageView.setImageDrawable(icon);
        } else {
            imageView.setImageDrawable(new ScalingDrawableWrapper(icon, mIconScaleFactor));
        }
    }

~
~
下面主要来说说:setIconForView(mMobile, mMobileStrengthId);中的mMobileStrengthId,这个特别重要。在MobileSignalController.java中的方法notifyListeners()中调用SignalClusterView.javasetMobileDataIndicators()方法传进来的。
下面是setMobileDataIndicators()

   @Override
    public void setMobileDataIndicators(IconState statusIcon, IconState qsIcon, int statusType,
            int qsType, boolean activityIn, boolean activityOut, int dataActivityId,
            int stackedDataId, int stackedVoiceId,String typeContentDescription,
            String description, boolean isWide, int subId, boolean roaming) {
        PhoneState state = getState(subId);
        if (state == null) {
            return;
        }

        state.mMobileVisible = statusIcon.visible && !mBlockMobile;
        #state.mMobileStrengthId = statusIcon.icon;
        state.mMobileTypeId = statusType;
        state.mMobileDescription = statusIcon.contentDescription;
        state.mMobileTypeDescription = typeContentDescription;
        state.mIsMobileTypeIconWide = statusType != 0 && isWide;
        state.mRoaming = roaming;
        state.mActivityIn = activityIn && mActivityEnabled && !mWifiVisible;
        state.mActivityOut = activityOut && mActivityEnabled && !mWifiVisible;
        state.mDataActivityId = dataActivityId;
        state.mStackedDataId = stackedDataId;
        state.mStackedVoiceId = stackedVoiceId;

        apply();
    }

~
~
MobileSignalController.java中的方法notifyListeners()

 @Override
    public void notifyListeners(SignalCallback callback) {
        if (mConfig.readIconsFromXml) {
            generateIconGroup();
        }
        MobileIconGroup icons = getIcons();

        String contentDescription = getStringIfExists(getContentDescription());
        String dataContentDescription = getStringIfExists(icons.mDataContentDescription);
        final boolean dataDisabled = mCurrentState.iconGroup == TelephonyIcons.DATA_DISABLED
                && mCurrentState.userSetup;

        // Show icon in QS when we are connected or data is disabled.
        boolean showDataIcon = mCurrentState.dataConnected || dataDisabled;
        IconState statusIcon = new IconState(mCurrentState.enabled && !mCurrentState.airplaneMode,
                getCurrentIconId(), contentDescription);

        int qsTypeIcon = 0;
        IconState qsIcon = null;
        String description = null;
        // Only send data sim callbacks to QS.
        if (mCurrentState.dataSim) {
            qsTypeIcon = showDataIcon ? icons.mQsDataType : 0;
            qsIcon = new IconState(mCurrentState.enabled
                    && !mCurrentState.isEmergency, getQsCurrentIconId(), contentDescription);
            description = mCurrentState.isEmergency ? null : mCurrentState.networkName;
        }
        boolean activityIn = mCurrentState.dataConnected
                && !mCurrentState.carrierNetworkChangeMode
                && mCurrentState.activityIn;
        boolean activityOut = mCurrentState.dataConnected
                && !mCurrentState.carrierNetworkChangeMode
                && mCurrentState.activityOut;
        showDataIcon &= mCurrentState.isDefault || dataDisabled;
        if (SystemProperties.getBoolean("persist.vendor.radio.L_L_4G", false)
                && (mDataNetType == TelephonyManager.NETWORK_TYPE_LTE_CA
                       || mDataNetType == TelephonyManager.NETWORK_TYPE_LTE)) showDataIcon = true;
        int typeIcon = (showDataIcon && mStyle == STATUS_BAR_STYLE_ANDROID_DEFAULT) ? icons.mDataType : 0;
        int dataActivityId = showDataIcon && !showMobileActivity() ? icons.mActivityId : 0;
        callback.setMobileDataIndicators(statusIcon, qsIcon, typeIcon, qsTypeIcon,
                activityIn, activityOut, dataActivityId,
                icons.mStackedDataIcon, icons.mStackedVoiceIcon,
                dataContentDescription, description, icons.mIsWide,
                mSubscriptionInfo.getSubscriptionId(), mCurrentState.roaming);
    }

~
statusIcon:见setMobileDataIndicators()蓝色部分代码。
statusIcon.icon对应下面代码getCurrentIconId()。不明白可以看下面NetworkController.java。

 IconState statusIcon = new IconState(mCurrentState.enabled && !mCurrentState.airplaneMode,
                getCurrentIconId(), contentDescription);

NetworkController.java中的内部类IconState
路径:\frameworks\base\packages\SystemUI\src\com\android\systemui\statusbar\policy\NetworkController.java

 public static class IconState {
        public final boolean visible;
        public final int icon;
        public final String contentDescription;

        public IconState(boolean visible, int icon, String contentDescription) {
            this.visible = visible;
            this.icon = icon;
            this.contentDescription = contentDescription;
        }

        public IconState(boolean visible, int icon, int contentDescription,
                Context context) {
            this(visible, icon, context.getString(contentDescription));
        }
    }

android8.0和以前版本的区别就在这里。getCurrentIconId():

下面是8.0的代码,在8.0中是直接通过信号级别然后通过SignalDrawable代码绘制图片完成的。先对来说比往常版本要简单,但是如果要使用自己的信号样式,就是说定制信号样式。那么可以根据往常的版本来做。

 @Override
    public int getCurrentIconId() {
        if (mCurrentState.iconGroup == TelephonyIcons.CARRIER_NETWORK_CHANGE) {
            return SignalDrawable.getCarrierChangeState(getNumLevels());
        } else if (mCurrentState.connected) {
            int level = mCurrentState.level;
            if (mConfig.inflateSignalStrengths) {
                level++;
            }
            if (mConfig.readIconsFromXml) {
                return getIcons().mSingleSignalIcon;
            } else {
                return SignalDrawable.getState(level, getNumLevels(),
                    false);
            }
        } else if (mCurrentState.enabled) {
            if (mConfig.readIconsFromXml) {
                return getIcons().mSbDiscState;
            } else {
                return SignalDrawable.getEmptyState(getNumLevels());
            }
        } else {
            return 0;
        }
    }

~
~
~
以下是8.0之前的处理情况,直接获取信号级别图片,而这个图片呢是在你的res资源文件中。下面细看。
如果你要定制信号,请将readIconsFromXml 设置为true或改写代码。
readIconsFromXml 之前介绍过,在config中有定义。

 @Override
    public int getCurrentIconId() {
         if (mConfig.readIconsFromXml && mCurrentState.connected) {
            return getIcons().mSingleSignalIcon;
        } else {
            return super.getCurrentIconId();
        }
    }

情况一、没有改写readIconsFromXml 。
return super.getCurrentIconId();
父类\frameworks\base\packages\SystemUI\src\com\android\systemui\statusbar\policySignalController.java中的getCurrentIconId()和getIcons();

  public int getCurrentIconId() {
        if (mCurrentState.connected) {
            return getIcons().mSbIcons[mCurrentState.inetCondition][mCurrentState.level];
        } else if (mCurrentState.enabled) {
            return getIcons().mSbDiscState;
        } else {
            return getIcons().mSbNullState;
        }
    }
 protected I getIcons() {
        return (I) mCurrentState.iconGroup;
    }

这里有点小复杂,MobileSignalController.java继承了SignalController.java。
并在generateIconGroup()对mCurrentState.iconGroup进行了赋值。
sbIcons 后面那个null是qsIcons,源生代码上状态栏下拉数据开关是显示信号的,一般看到手机上的都是开发者修改后的。
这里sbIcons使用了TelephonyIcons中的一个数组,数组中对应res中的信号图片文件。

       int[][] sbIcons = TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH;
 mCurrentState.iconGroup = new MobileIconGroup(
                TelephonyManager.getNetworkTypeName(dataType),
                sbIcons , null, contentDesc, 0, 0, sbDiscState, 0, discContentDesc,
                dataContentDesc, dataTypeIcon, false, qsDataTypeIcon,
                singleSignalIcon, stackedDataIcon, stackedVoiceIcon, dataActivityId);

~~以上这种情况是简单的定制。但是比较复杂一点的定制这样就无法完成,比如三星的部分手机,还有个品牌手机电信卡信号显示双层信号。
~~
~~

下面说说第二种情况。

 @Override
    public int getCurrentIconId() {
         if (mConfig.readIconsFromXml && mCurrentState.connected) {
            return getIcons().mSingleSignalIcon;
        } else {
            return super.getCurrentIconId();
        }
    }

当修改过mConfig.readIconsFromXml的值。
return getIcons().mSingleSignalIcon;
mSingleSignalIcon:对应上方代码
mCurrentState.iconGroup = new MobileIconGroup(.........){........}

中的参数singleSignalIcon。
而singleSignalIcon是通过generateIconGroup()方法从TelephonyIcons中获取的。(generateIconGroup详情,请看源码MobileSignalController.java)

singleSignalIcon = TelephonyIcons.getSignalStrengthIcon(slotId, inet, level, roaming);

~
~

下面介绍一下TelephonyIcons.java

getSignalStrengthIcon():返回值就是根据传进参数获取的信号级别图片。

mSignalStrengthArray:

获取的是res中array.xml中的三维数组,数组中存放着图片名称。R.array.multi_signal_strength

  readIconsFromXml()方法中:
   mSignalStrengthArray = mRes.getStringArray(R.array.multi_signal_strength);
 static int getSignalStrengthIcon(int slot, int inet, int level, boolean roaming) {
        log(TAG, "getSignalStrengthIcon: " + String.format(
                "slot=%d, inetCondition=%d, level=%d, roaming=%b", slot, inet, level, roaming));

        String[] signalStrengthArray, selectedTypeArray;

        signalStrengthArray = mRes.getStringArray(mRes.getIdentifier(!roaming ?
                mSignalStrengthArray[slot] : mSignalStrengthRoamingArray[slot], null, NS));
        log(TAG, String.format("signalStrengthArray.length=%d", signalStrengthArray.length));

        selectedTypeArray = mRes.getStringArray(mRes.getIdentifier(
                signalStrengthArray[mSelectedSignalStreagthIndex[slot]], null, NS));
        log(TAG, String.format("selectedTypeArray.length=%d", selectedTypeArray.length));

        String[] inetArray = mRes.getStringArray(
                mRes.getIdentifier(selectedTypeArray[inet], null, NS));
        log(TAG, String.format("inetArray.length=%d", inetArray.length));

        return mRes.getIdentifier(inetArray[level], null, NS);
    }

~
这里在说一下,如果定制自己的信号样式的时候。发现代码修改都没有问题,R.array.multi_signal_strength的修改也没有问题。咦,怎么显示的和我在R.array.multi_signal_strength写的图片不一样,这个时候,检查一下android\vendor\qcom\proprietary\qrdplus\China下面。有三个文件夹ChinaMobile、ChinaTelecom、ChinaUnicom。一般是默认使用ChinaTelecom。为什么看这里呢,因为如果这里的array.xml的R.array.multi_signal_strength中和SystemUI中的R.array.multi_signal_strength中有相同的数组名字,它会使用ChinaTelecom中array.xml中的值。有点绕,希望能看明白。
~
~
~

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

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 170,589评论 25 707
  • afinalAfinal是一个android的ioc,orm框架 https://github.com/yangf...
    passiontim阅读 15,102评论 2 44
  • 笑看人生风雨路,淡泊平和心自安 笑看人生风雨路,淡泊平和心自安。生活,悲喜交集、忧乐相拌、苦甜相依,懂得放弃,才...
    杨蕾001阅读 327评论 0 0
  • 正文: 我们每个人都需要独处,对于独处的时间也远超过我们实际独处的时间。可是,另一方面来说,每个人也都有自己...
    徐传勤阅读 516评论 1 0
  • 1、RAC(TARGET, [KEYPATH, [NIL_VALUE]]):用于给某个对象的某个属性绑定。 // ...
    csp阅读 542评论 0 0