OSCHINA客戶端完全剖析(三)分頁加載和詳情

分頁加載

Meizitu的實(shí)現(xiàn)

這個(gè)功能的實(shí)現(xiàn)跟MobileAPI的返回大有關(guān)聯(lián)席覆,我曾經(jīng)在學(xué)習(xí)Meizitu的時(shí)候看過其實(shí)現(xiàn)颓影,然后練手重構(gòu)了一下Meizitu

它的MobileAPI是這樣的:

http://www.ourhfuu.com/meizitu.php?max_id=

http://www.ourhfuu.com/meizitu.php?since_id=

max_id是個(gè)輔助值斩个,標(biāo)明你現(xiàn)在所有圖片中id最大的那個(gè)书妻。分頁加載需要用到since_id,當(dāng)然哈肖,max_id用來對(duì)since_id進(jìn)行協(xié)助計(jì)算吻育。

先看個(gè)實(shí)例http://www.ourhfuu.com/meizitu.php?since_id=999

易知,其API返回結(jié)果為從998開始到979結(jié)束淤井,總共返回20條結(jié)果布疼。
不信你可以換成http://www.ourhfuu.com/meizitu.php?since_id=979 再試一下。

客戶端那邊币狠,Meizitu采用數(shù)據(jù)庫存儲(chǔ)+Loader游两,數(shù)據(jù)流上為

  1. 從網(wǎng)絡(luò)加載數(shù)據(jù)
  2. 加載好的數(shù)據(jù)保存至數(shù)據(jù)庫中
  3. Loader觸發(fā)觀察者效果,根據(jù)數(shù)據(jù)庫swapCursor()更新

因?yàn)镸obileAPI符合之前看到的特性漩绵,所以分頁加載數(shù)據(jù)再保存至數(shù)據(jù)庫時(shí)無需考慮存在數(shù)據(jù)重復(fù)的問題贱案,又因?yàn)槭褂昧薒oader,所以不用進(jìn)行手動(dòng)的更新UI顯示止吐。

總的來說宝踪,這樣做法非常簡潔明了,但問題就在于數(shù)據(jù)流還繞了一圈碍扔,沒有從網(wǎng)絡(luò)加載的數(shù)據(jù)返回后就直接更新到UI來得自然瘩燥。不過實(shí)際上也沒有什么問題就是了,反正已經(jīng)使用數(shù)據(jù)庫了不同,存數(shù)據(jù)庫這一步操作不能省略厉膀,而更新到UI的話是需要格式化從網(wǎng)絡(luò)返回的JSON數(shù)據(jù)的,直接讀數(shù)據(jù)庫讀的就是格式化后的數(shù)據(jù)二拐。

OSCHINA的實(shí)現(xiàn)

OSCHINA的API返回不是JSON站蝠,而是XML,但這不是關(guān)鍵卓鹿,關(guān)鍵在于其接口返回的數(shù)據(jù)有什么特性菱魔。

這里MobileAPI的參數(shù)比Meizitu多代碼更繞用法上也更復(fù)雜,最直觀的做法可以直接抓包然后改改參數(shù)看看效果吟孙。

不過澜倦,我這邊是直接看了看代碼分析的,先看綜合頁中的使用的API接口:

/**
     * 獲取新聞列表
     *
     * @param catalog
     *            類別 (1杰妓,2藻治,3)
     * @param page
     *            第幾頁
     * @param handler
     */
    public static void getNewsList(int catalog, int page,
            AsyncHttpResponseHandler handler) {
        RequestParams params = new RequestParams();
        params.put("catalog", catalog);
        params.put("pageIndex", page);
        params.put("pageSize", AppContext.PAGE_SIZE);
        if (catalog == NewsList.CATALOG_WEEK) {
            params.put("show", "week");
        } else if (catalog == NewsList.CATALOG_MONTH) {
            params.put("show", "month");
        }
        ApiHttpClient.get("action/api/news_list", params, handler);
    }

直接就有一個(gè)第幾頁的參數(shù)page了,不過倒并不知道是不是跟Meizitu的API具有相同的特性巷挥。這也不算問題桩卵,后面直接看它怎么操作返回?cái)?shù)據(jù)就能推斷出來了。

經(jīng)過一番閱讀,發(fā)現(xiàn)網(wǎng)絡(luò)數(shù)據(jù)返回后會(huì)先進(jìn)行使用XStream的Bean解析雏节,然后會(huì)變成一個(gè)類型為Bean的List傳到BaseListFragment#executeOnLoadDataSuccess(List<T> data)方法中:

protected void executeOnLoadDataSuccess(List<T> data) {
        if (data == null) {
            data = new ArrayList<T>();
        }

        if (mResult != null && !mResult.OK()) {
            AppContext.showToast(mResult.getErrorMessage());
            // 注銷登陸胜嗓,密碼已經(jīng)修改,cookie钩乍,失效了
            AppContext.getInstance().Logout();
        }

        mErrorLayout.setErrorType(EmptyLayout.HIDE_LAYOUT);
        if (mCurrentPage == 0) {
            mAdapter.clear();
        }

        for (int i = 0; i < data.size(); i++) {
            if (compareTo(mAdapter.getData(), data.get(i))) {
                data.remove(i);
                i--;
            }
        }
        int adapterState = ListBaseAdapter.STATE_EMPTY_ITEM;
        if ((mAdapter.getCount() + data.size()) == 0) {
            adapterState = ListBaseAdapter.STATE_EMPTY_ITEM;
        } else if (data.size() == 0
                || (data.size() < getPageSize() && mCurrentPage == 0)) {
            adapterState = ListBaseAdapter.STATE_NO_MORE;
            mAdapter.notifyDataSetChanged();
        } else {
            adapterState = ListBaseAdapter.STATE_LOAD_MORE;
        }
        mAdapter.setState(adapterState);
        mAdapter.addData(data);
        // 判斷等于是因?yàn)樽詈笥幸豁?xiàng)是listview的狀態(tài)
        if (mAdapter.getCount() == 1) {

            if (needShowEmptyNoData()) {
                mErrorLayout.setErrorType(EmptyLayout.NODATA);
            } else {
                mAdapter.setState(ListBaseAdapter.STATE_EMPTY_ITEM);
                mAdapter.notifyDataSetChanged();
            }
        }
    }

步驟非常簡單明確

  1. mCurrentPage==0現(xiàn)在是不是分頁的話辞州,數(shù)據(jù)全部加載上去
        if (mCurrentPage == 0) {
            mAdapter.clear();
        }
  1. for循環(huán)去除現(xiàn)有的數(shù)據(jù)項(xiàng),也就是說寥粹,要么MobileAPI不具有Meizitu的返回特性变过,要么就是在某種情況下可能會(huì)有重復(fù)數(shù)據(jù)項(xiàng)
for (int i = 0; i < data.size(); i++) {
            if (compareTo(mAdapter.getData(), data.get(i))) {
                data.remove(i);
                i--;
            }
        }

compareTo的實(shí)現(xiàn)也是十分暴力,如果id存在規(guī)律的話涝涤,倒是可以采用二分之類的改進(jìn)一下:

protected boolean compareTo(List<? extends Entity> data, Entity enity) {
        int s = data.size();
        if (enity != null) {
            for (int i = 0; i < s; i++) {
                if (enity.getId() == data.get(i).getId()) {
                    return true;
                }
            }
        }
        return false;
    }
  1. 置adapter狀態(tài)媚狰,然后添加新的數(shù)據(jù)項(xiàng):
        int adapterState = ListBaseAdapter.STATE_EMPTY_ITEM;
        if ((mAdapter.getCount() + data.size()) == 0) {
            adapterState = ListBaseAdapter.STATE_EMPTY_ITEM;
        } else if (data.size() == 0
                || (data.size() < getPageSize() && mCurrentPage == 0)) {
            adapterState = ListBaseAdapter.STATE_NO_MORE;
            mAdapter.notifyDataSetChanged();
        } else {
            adapterState = ListBaseAdapter.STATE_LOAD_MORE;
        }
        mAdapter.setState(adapterState);
        mAdapter.addData(data);

看addData的實(shí)現(xiàn),直接在數(shù)據(jù)集的末尾追加阔拳,然后通知改變即可:

public void addData(List<T> data) {
        if (mDatas != null && data != null && !data.isEmpty()) {
            mDatas.addAll(data);
        }
        notifyDataSetChanged();
    }

另外

其他的就是一些細(xì)節(jié)需要進(jìn)行注意崭孤,比如滑動(dòng)狀態(tài)的細(xì)分處理。

對(duì)了衫生,ListView最下面總會(huì)有個(gè)footerView進(jìn)來表示“正在加載...”之類裳瘪,這個(gè)footerView的存在導(dǎo)致ListView永不為空土浸,且計(jì)算數(shù)值時(shí)也要考慮它的因素罪针,還有就是各種情況下的顯隱性了。


詳情

所謂的詳情界面黄伊,就是從資訊或者博客點(diǎn)擊item進(jìn)入后的Activity:

點(diǎn)擊過后的item項(xiàng)灰顯了泪酱,因?yàn)樗驯患尤肓说谝黄岬竭^的已讀列表之中。

跳轉(zhuǎn)的代碼看NewsFragment#onItemClick:

@Override
    public void onItemClick(AdapterView<?> parent, View view, int position,
            long id) {
        News news = mAdapter.getItem(position);
        if (news != null) {
            UIHelper.showNewsRedirect(view.getContext(), news);

            // 放入已讀列表
            saveToReadedList(view, NewsList.PREF_READED_NEWS_LIST, news.getId()
                    + "");
        }
    }

UIHelper也看過幾次了还最,可以得出結(jié)論墓阀,該應(yīng)用在設(shè)計(jì)上使用這個(gè)類作為中間層來統(tǒng)一管理UI切換的操作。
其中針對(duì)不同的url情況做了較多的判定封裝拓轻,就不進(jìn)行直接研讀了斯撮。因?yàn)樽畛R姷募礊樯厦娴慕貓D“資訊詳情”與“博客詳情”,所以我們直接查找字串然后定位具體實(shí)現(xiàn)的Activity即可扶叉。

最后發(fā)現(xiàn)實(shí)現(xiàn)類為這個(gè):

/**
 * 詳情activity(包括:資訊勿锅、博客、軟件枣氧、問答溢十、動(dòng)彈)
 *
 * @author FireAnt(http://my.oschina.net/LittleDY)
 * @created 2014年10月11日 上午11:18:41
 */
public class DetailActivity extends BaseActivity implements OnSendClickListener {

BaseActivity extends ActionBarActivity

這個(gè)類主要封裝了黑白主題的設(shè)置、ActionBar操作达吞、Toast操作(注意使用的是自定義的一個(gè)CommonToast)张弛、ProgressDialog等。

DetailActivity extends BaseActivity

非常容易理解,放了多個(gè)標(biāo)志位來區(qū)分進(jìn)行不同的Fragment操作:

public static final int DISPLAY_NEWS = 0;
    public static final int DISPLAY_BLOG = 1;
    public static final int DISPLAY_SOFTWARE = 2;
    public static final int DISPLAY_POST = 3;
    public static final int DISPLAY_TWEET = 4;
    public static final int DISPLAY_EVENT = 5;
    public static final int DISPLAY_TEAM_ISSUE_DETAIL = 6;
    public static final int DISPLAY_TEAM_DISCUSS_DETAIL = 7;
    public static final int DISPLAY_TEAM_TWEET_DETAIL = 8;
    public static final int DISPLAY_TEAM_DIARY = 9;
    public static final int DISPLAY_COMMENT = 10;

找到資訊詳情為:

@Override
    protected void init(Bundle savedInstanceState) {
        super.init(savedInstanceState);
        int displayType = getIntent().getIntExtra(BUNDLE_KEY_DISPLAY_TYPE,
                DISPLAY_NEWS);
        BaseFragment fragment = null;
        int actionBarTitle = 0;
        switch (displayType) {
            case DISPLAY_NEWS:
                actionBarTitle = R.string.actionbar_title_news;
                fragment = new NewsDetailFragment();
                break;

自頂向下分析吧吞鸭,因?yàn)锽aseFragment之前的篇章已分析過寺董,所以從這里開始:

CommonDetailFragment<T extends Serializable> extends BaseFragment

看持有的域:

    protected int mId;

    protected EmptyLayout mEmptyLayout;

    protected int mCommentCount = 0;

    protected WebView mWebView;

    protected T mDetail;

    private AsyncTask<String, Void, T> mCacheTask;

意料之中,使用WebView來實(shí)現(xiàn)網(wǎng)頁瀏覽瞒大,具有效果表現(xiàn)需要看服務(wù)器端的適配情況了螃征。

看布局文件,發(fā)現(xiàn)其標(biāo)題透敌、作者盯滚、時(shí)間三項(xiàng)是使用原生控件來做的:

        <ScrollView
            android:id="@+id/sv_news_container"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:fadingEdge="none"
            android:scrollbars="vertical">

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

                <LinearLayout
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:background="@color/white"
                    android:orientation="vertical"
                    android:padding="@dimen/space_8"
                    android:visibility="gone"
                    android:id="@+id/ll_header">

                    <TextView
                        android:id="@+id/tv_title"
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:textColor="@color/main_black"
                        android:textSize="@dimen/text_size_18"
                        android:textStyle="bold" />

                    <LinearLayout
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:layout_marginTop="@dimen/space_4"
                        android:gravity="center_vertical"
                        android:orientation="horizontal" >

                        <TextView
                            android:id="@+id/tv_time"
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content"
                            android:gravity="left|center_vertical"
                            android:textColor="@color/main_gray"
                            android:textSize="@dimen/text_size_12" />

                        <TextView
                            android:id="@+id/tv_source"
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content"
                            android:layout_marginLeft="@dimen/space_10"
                            android:clickable="true"
                            android:textColor="@color/lightblue"
                            android:textSize="@dimen/text_size_12" />

                    </LinearLayout>
                </LinearLayout>

                <WebView
                    android:id="@+id/webview"
                    android:layout_width="fill_parent"
                    android:layout_height="wrap_content" />
            </LinearLayout>
        </ScrollView>

看下executeOnLoadDataSuccess(T detail)中具體的載入代碼:

        mWebView.loadDataWithBaseURL("", this.getWebViewBody(detail), "text/html", "UTF-8", "");
        // 顯示存儲(chǔ)的字體大小
        mWebView.loadUrl(FontSizeUtils.getSaveFontSize());

使用loadDataWithBaseURL直接加載數(shù)據(jù),而這里的數(shù)據(jù)detail來源有兩種:一是緩存文件中的數(shù)據(jù)酗电,二是網(wǎng)絡(luò)請(qǐng)求返回的數(shù)據(jù)魄藕。getWebViewBody由子類實(shí)現(xiàn)。
下一行則是使用javascript代碼進(jìn)行文本的字號(hào)大小控制了撵术。

    public static String getSaveFontSize() {
        return getFontSize(getSaveFontSizeIndex());
    }

    public static String getFontSize(int fontSizeIndex) {
        String fontSize = "";
        switch (fontSizeIndex) {
            case 0:
                fontSize = "javascript:showSuperBigSize()";
                break;
            case 1:
                fontSize = "javascript:showBigSize()";
                break;
            case 2:
                fontSize = "javascript:showMidSize()";
                break;
            default:
                fontSize = "javascript:showSmallSize()";
                break;
        }
        return fontSize;
    }

js函數(shù)的代碼在assets/detail_page.js中背率,如:

function showBigSize() {
    var myBody = document.getElementById('article_body');
    myBody.style.fontSize="22px";
}

至于更清晰的調(diào)用路徑,首先嫩与,UIHelper 有定義該路徑字串:

public class UIHelper {

    /** 全局web樣式 */
    // 鏈接樣式文件寝姿,代碼塊高亮的處理
    public final static String linkCss = "<script type=\"text/javascript\" src=\"file:///android_asset/shCore.js\"></script>"
            + "<script type=\"text/javascript\" src=\"file:///android_asset/brush.js\"></script>"
            + "<script type=\"text/javascript\" src=\"file:///android_asset/client.js\"></script>"
            + "<script type=\"text/javascript\" src=\"file:///android_asset/detail_page.js\"></script>" // This line
            + "<script type=\"text/javascript\">SyntaxHighlighter.all();</script>"
            + "<script type=\"text/javascript\">function showImagePreview(var url){window.location.url= url;}</script>"
            + "<link rel=\"stylesheet\" type=\"text/css\" href=\"file:///android_asset/shThemeDefault.css\">"
            + "<link rel=\"stylesheet\" type=\"text/css\" href=\"file:///android_asset/shCore.css\">"
            + "<link rel=\"stylesheet\" type=\"text/css\" href=\"file:///android_asset/css/common.css\">";

    public final static String WEB_STYLE = linkCss;

然后在子類中進(jìn)行HTML文本構(gòu)造時(shí)會(huì)對(duì)其進(jìn)行加載:

body.append(UIHelper.WEB_STYLE).append(UIHelper.WEB_LOAD_IMAGES);

上面這句接著看后面的分析你就會(huì)看到的。

另外比較關(guān)鍵的部分是緩存添加與讀取划滋,其他則是添加收藏饵筑、提示登錄之類的業(yè)務(wù)相關(guān)流程。

NewsDetailFragment extends CommonDetailFragment<News>

設(shè)置獨(dú)特的CacheKey处坪、MobileAPI調(diào)用根资、數(shù)據(jù)解析等:

@Override
    protected String getCacheKey() {
        return "news_" + mId;
    }

@Override
    protected void sendRequestDataForNet() {
        OSChinaApi.getNewsDetail(mId, mDetailHeandler);
    }

    @Override
    protected News parseData(InputStream is) {
        return XmlUtils.toBean(NewsDetail.class, is).getNews();
    }

HTML數(shù)據(jù)文本構(gòu)造:

@Override
    protected String getWebViewBody(News detail) {
        StringBuffer body = new StringBuffer();
        body.append(UIHelper.WEB_STYLE).append(UIHelper.WEB_LOAD_IMAGES);
        body.append(ThemeSwitchUtils.getWebViewBodyString());
        // 添加title
        body.append(String.format("<div class='title'>%s</div>", mDetail.getTitle()));
        // 添加作者和時(shí)間
        String time = StringUtils.friendly_time(mDetail.getPubDate());
        String author = String.format("<a class='author' , mDetail.getAuthorId(), mDetail.getAuthor());
        body.append(String.format("<div class='authortime'>%s    %s</div>", author, time));
        // 添加圖片點(diǎn)擊放大支持
        body.append(UIHelper.setHtmlCotentSupportImagePreview(mDetail.getBody()));


        // 更多關(guān)于***軟件的信息
        String softwareName = mDetail.getSoftwareName();
        String softwareLink = mDetail.getSoftwareLink();
        if (!StringUtils.isEmpty(softwareName)
                && !StringUtils.isEmpty(softwareLink))
            body.append(String
                    .format("<div class='oschina_software' style='margin-top:8px;font-weight:bold'>更多關(guān)于: <a href='%s'>%s</a> 的詳細(xì)信息</div>",
                            softwareLink, softwareName));

        // 相關(guān)新聞
        if (mDetail != null && mDetail.getRelatives() != null
                && mDetail.getRelatives().size() > 0) {
            String strRelative = "";
            for (News.Relative relative : mDetail.getRelatives()) {
                strRelative += String.format(
                        "<li><a href='%s' style='text-decoration:none'>%s</a></li>",
                        relative.url, relative.title);
            }
            body.append("<p/><div style=\"height:1px;width:100%;background:#DADADA;margin-bottom:10px;\"/>"
                    + String.format("<br/> <b>相關(guān)資訊</b><ul class='about'>%s</ul>",
                    strRelative));
        }
        body.append("<br/>");
        // 封尾
        body.append("</div></body>");
        return  body.toString();
    }

下方工具欄的話,是使用了一個(gè)ToolbarFragment extends BaseFragment來封裝填充到DetailActivity中去的同窘。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末玄帕,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子想邦,更是在濱河造成了極大的恐慌裤纹,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,839評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件丧没,死亡現(xiàn)場離奇詭異鹰椒,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)骂铁,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門吹零,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人拉庵,你說我怎么就攤上這事灿椅。” “怎么了?”我有些...
    開封第一講書人閱讀 153,116評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵茫蛹,是天一觀的道長操刀。 經(jīng)常有香客問我,道長婴洼,這世上最難降的妖魔是什么骨坑? 我笑而不...
    開封第一講書人閱讀 55,371評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮柬采,結(jié)果婚禮上欢唾,老公的妹妹穿的比我還像新娘。我一直安慰自己粉捻,他們只是感情好礁遣,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,384評(píng)論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著肩刃,像睡著了一般祟霍。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上盈包,一...
    開封第一講書人閱讀 49,111評(píng)論 1 285
  • 那天沸呐,我揣著相機(jī)與錄音,去河邊找鬼呢燥。 笑死崭添,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的疮茄。 我是一名探鬼主播滥朱,決...
    沈念sama閱讀 38,416評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼根暑,長吁一口氣:“原來是場噩夢啊……” “哼力试!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起排嫌,我...
    開封第一講書人閱讀 37,053評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤畸裳,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后淳地,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體怖糊,經(jīng)...
    沈念sama閱讀 43,558評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,007評(píng)論 2 325
  • 正文 我和宋清朗相戀三年颇象,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了伍伤。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,117評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡遣钳,死狀恐怖扰魂,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤劝评,帶...
    沈念sama閱讀 33,756評(píng)論 4 324
  • 正文 年R本政府宣布姐直,位于F島的核電站,受9級(jí)特大地震影響蒋畜,放射性物質(zhì)發(fā)生泄漏声畏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,324評(píng)論 3 307
  • 文/蒙蒙 一姻成、第九天 我趴在偏房一處隱蔽的房頂上張望插龄。 院中可真熱鬧,春花似錦科展、人聲如沸辫狼。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽膨处。三九已至,卻和暖如春砂竖,著一層夾襖步出監(jiān)牢的瞬間真椿,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評(píng)論 1 262
  • 我被黑心中介騙來泰國打工乎澄, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留突硝,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,578評(píng)論 2 355
  • 正文 我出身青樓置济,卻偏偏與公主長得像解恰,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,877評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容

  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法愿卒,類相關(guān)的語法徒坡,內(nèi)部類的語法,繼承相關(guān)的語法腐宋,異常的語法,線程的語...
    子非魚_t_閱讀 31,587評(píng)論 18 399
  • 《ilua》速成開發(fā)手冊3.0 官方用戶交流:iApp開發(fā)交流(1) 239547050iApp開發(fā)交流(2) 1...
    葉染柒丶閱讀 10,538評(píng)論 0 11
  • ¥開啟¥ 【iAPP實(shí)現(xiàn)進(jìn)入界面執(zhí)行逐一顯】 〖2017-08-25 15:22:14〗 《//首先開一個(gè)線程檀轨,因...
    小菜c閱讀 6,358評(píng)論 0 17
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,518評(píng)論 25 707
  • 作者:柴田 貓沒日沒夜地嗜睡胸竞,而我成宿成宿地失眠; 有沒有想過参萄,可能是貓偷走了我們的睡眠卫枝? × 昨夜,我又失眠了讹挎。...
    柴田和球閱讀 582評(píng)論 1 3