Android inflate方法總結

前言

inflater.inflate(R.layout.layout_inflate_test,null);
inflater.inflate(R.layout.layout_inflate_test, root,false);
inflater.inflate(R.layout.layout_inflate_test, root,true);

看到上面這幾個方法是不是非常眼熟,基本上做過Android開發的人都會調用過inflate方法,可是你真的了解inflate方法嗎?各個參數都是什么含義?傳遞不同參數會產生什么效果?以前的觀點是鑰匙就是用來開鎖的,椅子就是用來坐的,能用不就行了,管它原理是什么。這種觀點對于一個新手還好說,刨根問底確實有點難度,但是隨著時間的推移,你總會不斷遇到一些相同的問題。如果你不早點把原理搞清楚,那么你就像路過沒有燈光的胡同,會在相同的地方跌倒一次又一次。扯遠了哈,其實網上對inflate方法太多的總結和分析,我在這里主要是自己記錄總結,當然能幫到有需要的人更好。

我先提供三個鏈接:分析都挺好的
1,郭神分析:http://blog.csdn.net/guolin_blog/article/details/12921889
2, http://blog.csdn.net/u012702547/article/details/52628453
3, http://blog.csdn.net/l540675759/article/details/78080656

其實博主我,之前沒寫這篇博客的時候,只會一直用,然后都不知道LayoutInflater的加載原理,每次直接
LayoutInflater.from(context).inflate(R.layout.activity_test, root, false);
//不行就這樣,反正有一種能實現我要的效果
LayoutInflater.from(context).inflate(R.layout.activity_test, null);
反正總有一種方式適合我。

上面摘錄自第三篇博客,我深有共鳴。我以前也是這樣,雖然閉著眼睛,憑著經驗也可以邁過一些坑。但是如果你是一個有追求的想讓自己的title加上高級兩個字的工程師,你就要去看源碼,去了解有疑問的地方的原理,不然你去大廠面試的時候深深體會到書到用時方恨少,胸中無墨,啞口無言的真正含義。

分析

首先

放源碼之前先要知道inflate方法是干嘛的,看返回是一個View,就知道這個方法是要根據布局id把這個布局加載成一個View并返回的。

源碼分析

   /**
     * ...
     */
    public View inflate(@LayoutRes int resource, @Nullable ViewGroup root) {
        return inflate(resource, root, root != null);
    }

    /**
     * ...
     */
    public View inflate(XmlPullParser parser, @Nullable ViewGroup root) {
        return inflate(parser, root, root != null);
    }

    /**
     * Inflate a new view hierarchy from the specified xml resource. Throws
     * {@link InflateException} if there is an error.
     *
     * @param resource ID for an XML layout resource to load (e.g.,
     *        <code>R.layout.main_page</code>)
     * @param root Optional view to be the parent of the generated hierarchy (if
     *        <em>attachToRoot</em> is true), or else simply an object that
     *        provides a set of LayoutParams values for root of the returned
     *        hierarchy (if <em>attachToRoot</em> is false.)
     * @param attachToRoot Whether the inflated hierarchy should be attached to
     *        the root parameter? If false, root is only used to create the
     *        correct subclass of LayoutParams for the root view in the XML.
     * @return The root View of the inflated hierarchy. If root was supplied and
     *         attachToRoot is true, this is root; otherwise it is the root of
     *         the inflated XML file.
     */
    public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {
        final Resources res = getContext().getResources();
        if (DEBUG) {
            Log.d(TAG, "INFLATING from resource: \"" + res.getResourceName(resource) + "\" ("
                    + Integer.toHexString(resource) + ")");
        }

        final XmlResourceParser parser = res.getLayout(resource);
        try {
            return inflate(parser, root, attachToRoot);
        } finally {
            parser.close();
        }
    }

    /**
     * Inflate a new view hierarchy from the specified XML node. Throws
     * {@link InflateException} if there is an error.
     * <p>
     * <em><strong>Important</strong></em>&nbsp;&nbsp;&nbsp;For performance
     * reasons, view inflation relies heavily on pre-processing of XML files
     * that is done at build time. Therefore, it is not currently possible to
     * use LayoutInflater with an XmlPullParser over a plain XML file at runtime.
     *
     * @param parser XML dom node containing the description of the view
     *        hierarchy.
     * @param root Optional view to be the parent of the generated hierarchy (if
     *        <em>attachToRoot</em> is true), or else simply an object that
     *        provides a set of LayoutParams values for root of the returned
     *        hierarchy (if <em>attachToRoot</em> is false.)
     * @param attachToRoot Whether the inflated hierarchy should be attached to
     *        the root parameter? If false, root is only used to create the
     *        correct subclass of LayoutParams for the root view in the XML.
     * @return The root View of the inflated hierarchy. If root was supplied and
     *         attachToRoot is true, this is root; otherwise it is the root of
     *         the inflated XML file.
     */
    public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
        synchronized (mConstructorArgs) {
            Trace.traceBegin(Trace.TRACE_TAG_VIEW, "inflate");

            final Context inflaterContext = mContext;
            final AttributeSet attrs = Xml.asAttributeSet(parser);
            Context lastContext = (Context) mConstructorArgs[0];
            mConstructorArgs[0] = inflaterContext;
            View result = root;

            try {
                // Look for the root node.
                int type;
                while ((type = parser.next()) != XmlPullParser.START_TAG &&
                        type != XmlPullParser.END_DOCUMENT) {
                    // Empty
                }

                if (type != XmlPullParser.START_TAG) {
                    throw new InflateException(parser.getPositionDescription()
                            + ": No start tag found!");
                }

                final String name = parser.getName();

                if (DEBUG) {
                    System.out.println("**************************");
                    System.out.println("Creating root view: "
                            + name);
                    System.out.println("**************************");
                }

                if (TAG_MERGE.equals(name)) {
                    if (root == null || !attachToRoot) {
                        throw new InflateException("<merge /> can be used only with a valid "
                                + "ViewGroup root and attachToRoot=true");
                    }

                    rInflate(parser, root, inflaterContext, attrs, false);
                } else {
                    // Temp is the root view that was found in the xml
                    final View temp = createViewFromTag(root, name, inflaterContext, attrs);

                    ViewGroup.LayoutParams params = null;

                    if (root != null) {
                        if (DEBUG) {
                            System.out.println("Creating params from root: " +
                                    root);
                        }
                        // Create layout params that match root, if supplied
                        params = root.generateLayoutParams(attrs);
                        if (!attachToRoot) {
                            // Set the layout params for temp if we are not
                            // attaching. (If we are, we use addView, below)
                            temp.setLayoutParams(params);
                        }
                    }

                    if (DEBUG) {
                        System.out.println("-----> start inflating children");
                    }

                    // Inflate all children under temp against its context.
                    rInflateChildren(parser, temp, attrs, true);

                    if (DEBUG) {
                        System.out.println("-----> done inflating children");
                    }

                    // We are supposed to attach all the views we found (int temp)
                    // to root. Do that now.
                    if (root != null && attachToRoot) {
                        root.addView(temp, params);
                    }

                    // Decide whether to return the root that was passed in or the
                    // top view found in xml.
                    if (root == null || !attachToRoot) {
                        result = temp;
                    }
                }

            } catch (XmlPullParserException e) {
                final InflateException ie = new InflateException(e.getMessage(), e);
                ie.setStackTrace(EMPTY_STACK_TRACE);
                throw ie;
            } catch (Exception e) {
                final InflateException ie = new InflateException(parser.getPositionDescription()
                        + ": " + e.getMessage(), e);
                ie.setStackTrace(EMPTY_STACK_TRACE);
                throw ie;
            } finally {
                // Don't retain static reference on context.
                mConstructorArgs[0] = lastContext;
                mConstructorArgs[1] = null;

                Trace.traceEnd(Trace.TRACE_TAG_VIEW);
            }

            return result;
        }
    }

源碼有點長,不要被嚇到,下面一一拆解
看源碼一共有四個inflate方法,這四個又分為兩類:

首參數為布局id,@LayoutRes int resource ,也是我們經常用的;
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot)

首參數為Parser,XmlPullParser parse ,我開發中好像沒有用到過。
public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot)

源碼中inflate(R.layout.layout_inflate_test,null)其實調用的是inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot),inflate(XmlPullParser parser, @Nullable ViewGroup root)調用的是inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot)。

final XmlResourceParser parser = res.getLayout(resource);
        try {
            return inflate(parser, root, attachToRoot);
        } finally {
            parser.close();
        }

第一類其實調用的也是第四個方法。那就著重看一下第四個方法inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot)。
開始分析:

final Context inflaterContext = mContext;
final AttributeSet attrs = Xml.asAttributeSet(parser);
Context lastContext = (Context) mConstructorArgs[0];
mConstructorArgs[0] = inflaterContext;
View result = root;

看開始的幾句代碼,先不管前面的那幾句定義,最后一句話View result = root;那么這個result基本就是作為返回值了,看這個方法最后return result;好吧果然是的。result = root,也就是將第二個參數ViewGroup root返回了,但是使用的inflate方法的時候我們有可能傳遞的是null也有可能不是null,繼續往下看

// Look for the root node.
int type;
while ((type = parser.next()) != XmlPullParser.START_TAG &&
        type != XmlPullParser.END_DOCUMENT) {
    // Empty
}

if (type != XmlPullParser.START_TAG) {
    throw new InflateException(parser.getPositionDescription()
            + ": No start tag found!");
}

final String name = parser.getName();

這幾句的意思是獲取到根節點的標簽名稱

if (TAG_MERGE.equals(name)) {
    if (root == null || !attachToRoot) {
        throw new InflateException("<merge /> can be used only with a valid "
                + "ViewGroup root and attachToRoot=true");
    }

    rInflate(parser, root, inflaterContext, attrs, false);
}

如果根節點的標簽是merge,如果root為null或者attachToRoot為false會直接拋異常,也就是當根標簽為merge的時候必須使用inflater.inflate(R.layout.layout_inflate_test, root,true);這種形式,不然會報錯,你可以自己試驗一下。實驗結果:


329DC4C2-A7AF-4B70-807C.png

繼續往下看:rInflate(parser, root, inflaterContext, attrs, false);

/**
     * Recursive method used to descend down the xml hierarchy and instantiate
     * views, instantiate their children, and then call onFinishInflate().
     * <p>
     * <strong>Note:</strong> Default visibility so the BridgeInflater can
     * override it.
     */
    void rInflate(XmlPullParser parser, View parent, Context context,
            AttributeSet attrs, boolean finishInflate) throws XmlPullParserException, IOException {

        final int depth = parser.getDepth();
        int type;
        boolean pendingRequestFocus = false;

        while (((type = parser.next()) != XmlPullParser.END_TAG ||
                parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {

            if (type != XmlPullParser.START_TAG) {
                continue;
            }

            final String name = parser.getName();

            if (TAG_REQUEST_FOCUS.equals(name)) {
                pendingRequestFocus = true;
                consumeChildElements(parser);
            } else if (TAG_TAG.equals(name)) {
                parseViewTag(parser, parent, attrs);
            } else if (TAG_INCLUDE.equals(name)) {
                if (parser.getDepth() == 0) {
                    throw new InflateException("<include /> cannot be the root element");
                }
                parseInclude(parser, context, parent, attrs);
            } else if (TAG_MERGE.equals(name)) {
                throw new InflateException("<merge /> must be the root element");
            } else {
                final View view = createViewFromTag(parent, name, context, attrs);
                final ViewGroup viewGroup = (ViewGroup) parent;
                final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);
                rInflateChildren(parser, view, attrs, true);
                viewGroup.addView(view, params);
            }
        }

        if (pendingRequestFocus) {
            parent.restoreDefaultFocus();
        }

        if (finishInflate) {
            parent.onFinishInflate();
        }
    }

這個方法的意思是:遞歸方法進入xml層次結構并實例化視圖,實例化它們的子項,然后調用onFinishInflate()。這個方法的內容可以先不看,知道它的作用就行了,就是把這個布局里面的各個子項實例化。舉個例子一個完整的快遞肯定是大盒子包小盒子再包,有的包了好幾層最后才是你的商品。你想要的肯定不是最外層的那個空盒子,你需要的是一個完整的快遞。這個方法就是用來把一個或者好幾個商品用紙盒子一層層包起來組成一個可以運輸的快遞的,上面的root就是最外面的那個盒子。

else {
    // Temp is the root view that was found in the xml
    final View temp = createViewFromTag(root, name, inflaterContext, attrs);

    ViewGroup.LayoutParams params = null;

    if (root != null) {
        if (DEBUG) {
            System.out.println("Creating params from root: " +
                    root);
        }
        // Create layout params that match root, if supplied
        params = root.generateLayoutParams(attrs);
        if (!attachToRoot) {
            // Set the layout params for temp if we are not
            // attaching. (If we are, we use addView, below)
            temp.setLayoutParams(params);
        }
    }

    if (DEBUG) {
        System.out.println("-----> start inflating children");
    }

    // Inflate all children under temp against its context.
    rInflateChildren(parser, temp, attrs, true);

    if (DEBUG) {
        System.out.println("-----> done inflating children");
    }

    // We are supposed to attach all the views we found (int temp)
    // to root. Do that now.
    if (root != null && attachToRoot) {
        root.addView(temp, params);
    }

    // Decide whether to return the root that was passed in or the
    // top view found in xml.
    if (root == null || !attachToRoot) {
        result = temp;
    }
}

剛才哪個merge是特殊情況,一般常見的是else里面的情況

// Temp is the root view that was found in the xml
final View temp = createViewFromTag(root, name, inflaterContext, attrs);

生成一個root view,也就是根據根目錄的標簽生成的view。這里有個需要注意的地方,最后一個參數attrs,也就是說這個根視圖view的一些屬性還是會被添加上去例如背景顏色等屬性

ViewGroup.LayoutParams params = null;

 if (root != null) {
        if (DEBUG) {
            System.out.println("Creating params from root: " +
                    root);
        }
        // Create layout params that match root, if supplied
        params = root.generateLayoutParams(attrs);
        if (!attachToRoot) {
            // Set the layout params for temp if we are not
            // attaching. (If we are, we use addView, below)
            temp.setLayoutParams(params);
        }
    }

考點來了,如果root != null,創建LayoutParams,params = root.generateLayoutParams(attrs);這個參數attrs來自上面的final AttributeSet attrs = Xml.asAttributeSet(parser);Xml.asAttributeSet(parser)這句代碼我也不是很懂,但是有時候可以通過具體現象或返回值推算某一句代碼的作用。它返回一個AttributeSet,AttributeSet是view的布局屬性集合,所以這里的作用就是把我們傳入的布局的屬性拿到。然后后面根據這些屬性創建LayoutParams。看下面

/**
     * Returns a new set of layout parameters based on the supplied attributes set.
     * 根據提供的屬性集返回一個新的LayoutParams
     * @param attrs the attributes to build the layout parameters from
     *
     * @return an instance of {@link android.view.ViewGroup.LayoutParams} or one
     *         of its descendants
     */
    public LayoutParams generateLayoutParams(AttributeSet attrs) {
        return new LayoutParams(getContext(), attrs);
    }

...

/**
     * Creates a new set of layout parameters. The values are extracted from
     * the supplied attributes set and context. The XML attributes mapped
     * to this set of layout parameters are:
     *
     * <ul>
     *   <li><code>layout_width</code>: the width, either an exact value,
     *   {@link #WRAP_CONTENT}, or {@link #FILL_PARENT} (replaced by
     *   {@link #MATCH_PARENT} in API Level 8)</li>
     *   <li><code>layout_height</code>: the height, either an exact value,
     *   {@link #WRAP_CONTENT}, or {@link #FILL_PARENT} (replaced by
     *   {@link #MATCH_PARENT} in API Level 8)</li>
     * </ul>
     *
     * @param c the application environment
     * @param attrs the set of attributes from which to extract the layout
     *              parameters' values
     */
    public LayoutParams(Context c, AttributeSet attrs) {
        TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.ViewGroup_Layout);
        setBaseAttributes(a,
                R.styleable.ViewGroup_Layout_layout_width,
                R.styleable.ViewGroup_Layout_layout_height);
        a.recycle();
    }

現在有了LayoutParams,如果attachToRoot為false時執行temp.setLayoutParams(params);將上面得到的LayoutParams給temp--也就是我們上面根據布局根目錄標簽創建的的那個View設置上。布局根目錄一般都是LinearLayout,RelativeLayout等這些ViewGroup,當然也可以是view
再往下看

if (DEBUG) {
    System.out.println("-----> start inflating children");
}

// Inflate all children under temp against its context.
rInflateChildren(parser, temp, attrs, true);

if (DEBUG) {
    System.out.println("-----> done inflating children");
}
/**
     * Recursive method used to inflate internal (non-root) children. This
     * method calls through to {@link #rInflate} using the parent context as
     * the inflation context.
     * <strong>Note:</strong> Default visibility so the BridgeInflater can
     * call it.
     */
    final void rInflateChildren(XmlPullParser parser, View parent, AttributeSet attrs,
            boolean finishInflate) throws XmlPullParserException, IOException {
        rInflate(parser, parent, parent.getContext(), attrs, finishInflate);
    }

跟剛才哪個merge標簽的情況一樣打包快遞,把子布局子view都一層層組裝起來,裝到temp里。繼續

// We are supposed to attach all the views we found (int temp)
// to root. Do that now.
if (root != null && attachToRoot) {
    root.addView(temp, params);
}

又到考點了,假如root不為空,并且attachToRoot為true,那么root就把生成的temp裝到自己里面addView,后面還有參數params。temp會被添加到root的最后,并且params設置給temp。

// Decide whether to return the root that was passed in or the
// top view found in xml.
if (root == null || !attachToRoot) {
    result = temp;
}

最后一個考點,如果root為空,那么attachToRoot就不用看了,attachToRoot為true或者false都沒有意義, 直接result = temp。注意這里temp是沒有被設置剛才的LayoutParams的,而LayoutParams是用來設置位置、高、寬等信息,也就意味著temp的這些屬性是全新的。

結論

但是使用的時候我們更關心的是各個參數傳遞給我們帶來的影響和效果。那么通過看源碼我們得到什么樣的結論呢?
先把郭神的結論貼出來:

  1. 如果root為null,attachToRoot將失去作用,設置任何值都沒有意義。
  2. 如果root不為null,attachToRoot設為true,則會給加載的布局文件的指定一個父布局,即root。
  3. 如果root不為null,attachToRoot設為false,則會將布局文件最外層的所有layout屬性進行設置,當該view被添加到父view當中時,這些layout屬性會自動生效。
  4. 在不設置attachToRoot參數的情況下,如果root不為null,attachToRoot參數默認為true。

關于上面還有一些補充說明,如果root不為null,布局文件最外層的layout關于LayoutParams設置的屬性和其他屬性都會被保留下來,attachToRoot設為true,則會給加載的布局文件的指定一個父布局,我們不需要自己在addView,否則會報錯;attachToRoot設為false,需要我們自己addView,root為null時,被加載的布局LayoutParams的屬性會被改變,但是其它屬性例如背景顏色什么的會被保留。

參考鏈接

http://blog.csdn.net/guolin_blog/article/details/12921889 http://blog.csdn.net/u012702547/article/details/52628453 http://blog.csdn.net/l540675759/article/details/78080656
https://www.cnblogs.com/coding-way/p/5257579.html
http://blog.csdn.net/jaysong2012/article/details/41117339
http://www.cnblogs.com/xiaoweiz/p/3788332.html
http://www.lxweimin.com/p/07fcd2517dbc

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,702評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,143評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,553評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,620評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,416評論 6 405
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,940評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,024評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,170評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,709評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,597評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,784評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,291評論 5 357
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,029評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,407評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,663評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,403評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,746評論 2 370

推薦閱讀更多精彩內容

  • 一、適用場景 ListViewListview是一個很重要的組件,它以列表的形式根據數據的長自適應展示具體內容,用...
    Geeks_Liu閱讀 10,699評論 1 28
  • RecyclerView Item 布局寬高無效問題探究 前言 這個問題很早之前就碰到過,后來通過google找到...
    TinyMen閱讀 427評論 0 0
  • 原文地址:http://www.lxweimin.com/p/de7f651170be 一、簡述 LayoutInf...
    AFinalStone閱讀 4,078評論 1 7
  • 有段時間沒寫博客了,感覺都有些生疏了呢。最近繁忙的工作終于告一段落,又有時間寫文章了,接下來還會繼續堅持每一周篇的...
    justin_pan閱讀 559評論 0 2
  • 不止一次,我掙扎在自己的世界里 不止一次,我認為這世上的一切都是虛假 或許我正活在自己的夢里 在幼年時的某次熟睡 ...
    梅千延閱讀 207評論 0 0