OSCHINA客戶端完全剖析(二)

接上期,著重分析主干邏輯橄维,先看第一個綜合頁面尺铣。

綜合

五個Tab頁定義可以看MainTab ,雖然enum占用存儲較多争舞,但可讀性的提升也同樣顯而易見凛忿,以下是代碼片段:

public enum MainTab {

    NEWS(0, R.string.main_tab_name_news, R.drawable.tab_icon_new,
            NewsViewPagerFragment.class),

    TWEET(1, R.string.main_tab_name_tweet, R.drawable.tab_icon_tweet,
            TweetsViewPagerFragment.class),

    QUICK(2, R.string.main_tab_name_quick, R.drawable.tab_icon_new,
            null),

    EXPLORE(3, R.string.main_tab_name_explore, R.drawable.tab_icon_explore,
            ExploreFragment.class),

    ME(4, R.string.main_tab_name_my, R.drawable.tab_icon_me,
            MyInformationFragment.class);

綜合頁當(dāng)然就是NewsViewPagerFragment了,我們自頂向下逐步分析竞川。

BaseFragment extends Fragment

預(yù)定義了多種狀態(tài)店溢,有可能會在子類中用到:

public static final int STATE_NONE = 0;
    public static final int STATE_REFRESH = 1;
    public static final int STATE_LOADMORE = 2;
    public static final int STATE_NOMORE = 3;
    public static final int STATE_PRESSNONE = 4;// 正在下拉但還沒有到刷新的狀態(tài)
    public static int mState = STATE_NONE;

主要是調(diào)用Activity,提供ProgressDialog顯隱功能:

protected void hideWaitDialog() {
        FragmentActivity activity = getActivity();
        if (activity instanceof DialogControl) {
            ((DialogControl) activity).hideWaitDialog();
        }
    }

    protected ProgressDialog showWaitDialog() {
        return showWaitDialog(R.string.loading);
    }

BaseViewPagerFragment extends BaseFragment

官方描述:帶有導(dǎo)航條的基類委乌。
這一點從其持有的域也可以看出:

protected PagerSlidingTabStrip mTabStrip;
    protected ViewPager mViewPager;
    protected ViewPageFragmentAdapter mTabsAdapter;
    protected EmptyLayout mErrorLayout;

onCreateView()中加載以下布局:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="fill_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <!-- 導(dǎo)航標(biāo)題欄 -->
    <net.oschina.app.widget.PagerSlidingTabStrip
        android:id="@+id/pager_tabstrip"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="?attr/windows_bg"
        app:allowWidthFull="true"
        app:slidingBlock="?attr/sliding_block_bg" >
    </net.oschina.app.widget.PagerSlidingTabStrip>

    <View
        android:id="@+id/view_pager_line"
        android:layout_width="match_parent"
        android:layout_below="@id/pager_tabstrip"
        android:layout_height="1px"
        android:background="?attr/lineColor" />

    <android.support.v4.view.ViewPager
        android:id="@+id/pager"
        style="@style/fill_fill"
        android:layout_below="@id/view_pager_line">
    </android.support.v4.view.ViewPager>

    <net.oschina.app.ui.empty.EmptyLayout
        android:id="@+id/error_layout"
        style="@style/fill_fill"
        android:visibility="gone" />

</RelativeLayout>

其中PagerSlidingTabStrip extends HorizontalScrollView床牧,同樣的一個效果,還可以參見Google Contacts源碼中在PeopleActivity使用的ViewPagerTabs extends HorizontalScrollView:
http://androidxref.com/6.0.1_r10/xref/packages/apps/ContactsCommon/src/com/android/contacts/common/list/ViewPagerTabs.java

onViewCreated()中調(diào)用了如下兩行代碼遭贸,不用說戈咳,當(dāng)然是留給子類覆蓋的了:

        setScreenPageLimit();
        onSetupTabAdapter(mTabsAdapter);

NewsViewPagerFragment extends BaseViewPagerFragment

實現(xiàn)剛才說到的方法:

@Override
    protected void onSetupTabAdapter(ViewPageFragmentAdapter adapter) {
        String[] title = getResources().getStringArray(
                R.array.news_viewpage_arrays);
        adapter.addTab(title[0], "news", NewsFragment.class,
                getBundle(NewsList.CATALOG_ALL)); // 注一,用Bundle提供信息給Fragment
        adapter.addTab(title[1], "news_week", NewsFragment.class,
                getBundle(NewsList.CATALOG_WEEK));
        adapter.addTab(title[2], "latest_blog", BlogFragment.class,
                getBundle(BlogList.CATALOG_LATEST));
        adapter.addTab(title[3], "recommend_blog", BlogFragment.class,
                getBundle(BlogList.CATALOG_RECOMMEND));
    }

    private Bundle getBundle(int newType) {
        Bundle bundle = new Bundle();
        bundle.putInt(BaseListFragment.BUNDLE_KEY_CATALOG, newType);
        return bundle;
    }

    @Override
    protected void setScreenPageLimit() {
        mViewPager.setOffscreenPageLimit(3);
    }

可以看出,資訊和熱點使用的同一個Fragment著蛙,博客和推薦也是同一個删铃。
從mViewPager.setOffscreenPageLimit(3);來看,所有Tab頁保留了下來踏堡。

比較有意思的是猎唁,它還實現(xiàn)了這樣一個接口:

/** 
 * 當(dāng)tabHost再次被點擊時
 * @author FireAnt(http://my.oschina.net/LittleDY)
 * @version 創(chuàng)建時間:2014年11月17日 上午11:00:15 
 * 
 */
public interface OnTabReselectListener {
    
    public void onTabReselect();
}

具體來說是看該Fragment中嵌套的Fragment是否有實現(xiàn)此接口然后決定是否調(diào)用:

@Override
    public void onTabReselect() {
        try {
            int currentIndex = mViewPager.getCurrentItem();
            Fragment currentFragment = getChildFragmentManager().getFragments()
                    .get(currentIndex);
            if (currentFragment != null
                    && currentFragment instanceof OnTabReselectListener) {
                OnTabReselectListener listener = (OnTabReselectListener) currentFragment;
                listener.onTabReselect();
            }
        } catch (NullPointerException e) {
        }
    }

現(xiàn)在,我們來看綜合頁中的NewsFragment顷蟆,同樣是自頂向下胖秒。

BaseListFragment<T extends Entity> extends BaseFragment

這是一個泛型類,Bean包下有大量extends Entity的實體慕的。
關(guān)于泛型可以參考這篇:http://www.reibang.com/p/f258c907019d

使用了可以進(jìn)行下拉刷新的布局,以下為代碼片段:

<!-- google 官方下拉刷新 -->

    <android.support.v4.widget.SwipeRefreshLayout
        android:id="@+id/swiperefreshlayout"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:visibility="visible" >

        <ListView
            android:id="@+id/listview"
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:divider="?attr/lineColor"
            android:listSelector="@color/transparent"
            android:dividerHeight="1px"
            android:scrollbars="none"
            android:scrollingCache="true" />
    </android.support.v4.widget.SwipeRefreshLayout>

提供了一個ParserTask extends AsyncTask挤渔,從代碼中可以看到肮街,其doInBackground中有如下調(diào)用:

new SaveCacheTask(getActivity(), data, getCacheKey()).execute();

這并不是一個好的實現(xiàn),因為AsyncTask應(yīng)該在主線程中創(chuàng)建判导,否則其內(nèi)部的靜態(tài)Handler將無法正常工作嫉父,具體可以參考《Android開發(fā)藝術(shù)探索》。
這里該應(yīng)用的事件流中應(yīng)該是ParserTask 本身有確保在主線程加載眼刃。

在onScrollStateChanged中提供滾動事件的特判绕辖,代碼較長就不貼了。

提供上一篇中提到的已讀列表功能:

/**
     * 保存已讀的文章列表
     *
     * @param view
     * @param prefFileName
     * @param key
     */
    protected void saveToReadedList(final View view, final String prefFileName,
            final String key) {
        // 放入已讀列表
        AppContext.putReadedPostList(prefFileName, key, "true");
        TextView tvTitle = (TextView) view.findViewById(R.id.tv_title);
        if (tvTitle != null) {
            tvTitle.setTextColor(AppContext.getInstance().getResources().getColor(ThemeSwitchUtils.getTitleReadedColor()));
        }
    }

onCreate()中讀取前文注一提供的信息擂红,實際使用當(dāng)然會是在子類中了:

@Override
    public void onCreate(android.os.Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Bundle args = getArguments();
        if (args != null) {
            mCatalog = args.getInt(BUNDLE_KEY_CATALOG, 0); // 注二仪际,子類中會使用此信息
        }
    }

onResume()中判斷了是否要進(jìn)行定時刷新操作:

@Override
    public void onResume() {
        super.onResume();
        if (onTimeRefresh()) {
            onRefresh();
        }
    }

NewsFragment extends BaseListFragment<News> implements OnTabReselectListener

明顯使用了News這個Bean,取決于具體業(yè)務(wù)情況昵骤,略去不表树碱。

Bean解析,可以看出數(shù)據(jù)使用的是xml格式变秦,這一方法的實際調(diào)用位置在基類的ParserTask doInBackground中:

@Override
    protected NewsList parseList(InputStream is) throws Exception {
        NewsList list = null;
        try {
            list = XmlUtils.toBean(NewsList.class, is);
        } catch (NullPointerException e) {
            list = new NewsList();
        }
        return list;
    }

覆蓋獲取數(shù)據(jù)的方法成榜,使用注二中提供的信息調(diào)用MobileAPI:

@Override
    protected void sendRequestData() {
        OSChinaApi.getNewsList(mCatalog, mCurrentPage, mHandler);
    }

之前提過的OnTabReselectListener有進(jìn)行實現(xiàn),可以看出是提供了一個刷新功能:

@Override
    public void onTabReselect() {
        onRefresh();
    }

而這個onRefresh()的實現(xiàn)是在基類BaseListFragment當(dāng)中蹦玫,也就是設(shè)置狀態(tài)赎婚,ListView回到頂部,然后獲取數(shù)據(jù):

    // 下拉刷新數(shù)據(jù)
    @Override
    public void onRefresh() {
        if (mState == STATE_REFRESH) {
            return;
        }
        // 設(shè)置頂部正在刷新
        mListView.setSelection(0);
        setSwipeRefreshLoadingState();
        mCurrentPage = 0;
        mState = STATE_REFRESH;
        requestData(true);
    }

具體獲取數(shù)據(jù)的時候樱溉,視情況看是讀緩存還是發(fā)請求獲取挣输。
緩存的實現(xiàn)是CacheManager,非常純粹的文件讀寫饺窿,略去不表歧焦。

根據(jù)業(yè)務(wù)情況,提供定時刷新的閾值給基類:

@Override
    protected long getAutoRefreshTime() {
        // 最新資訊兩小時刷新一次
        if (mCatalog == NewsList.CATALOG_ALL) {

            return 2 * 60 * 60;
        }
        return super.getAutoRefreshTime();
    }

另外三個頁面也是采用完全類似的方式,讀者可以自行分析绢馍。
至此向瓷,綜合頁面分析完畢。下期再見舰涌。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末猖任,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子瓷耙,更是在濱河造成了極大的恐慌朱躺,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,126評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件搁痛,死亡現(xiàn)場離奇詭異长搀,居然都是意外死亡,警方通過查閱死者的電腦和手機鸡典,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評論 2 382
  • 文/潘曉璐 我一進(jìn)店門源请,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人彻况,你說我怎么就攤上這事谁尸。” “怎么了纽甘?”我有些...
    開封第一講書人閱讀 152,445評論 0 341
  • 文/不壞的土叔 我叫張陵良蛮,是天一觀的道長。 經(jīng)常有香客問我悍赢,道長决瞳,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,185評論 1 278
  • 正文 為了忘掉前任左权,我火速辦了婚禮瞒斩,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘涮总。我一直安慰自己胸囱,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 64,178評論 5 371
  • 文/花漫 我一把揭開白布瀑梗。 她就那樣靜靜地躺著烹笔,像睡著了一般。 火紅的嫁衣襯著肌膚如雪抛丽。 梳的紋絲不亂的頭發(fā)上谤职,一...
    開封第一講書人閱讀 48,970評論 1 284
  • 那天,我揣著相機與錄音亿鲜,去河邊找鬼允蜈。 笑死冤吨,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的饶套。 我是一名探鬼主播漩蟆,決...
    沈念sama閱讀 38,276評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼妓蛮!你這毒婦竟也來了怠李?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,927評論 0 259
  • 序言:老撾萬榮一對情侶失蹤蛤克,失蹤者是張志新(化名)和其女友劉穎捺癞,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體构挤,經(jīng)...
    沈念sama閱讀 43,400評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡髓介,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,883評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了筋现。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片版保。...
    茶點故事閱讀 37,997評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖夫否,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情叫胁,我是刑警寧澤凰慈,帶...
    沈念sama閱讀 33,646評論 4 322
  • 正文 年R本政府宣布,位于F島的核電站驼鹅,受9級特大地震影響微谓,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜输钩,卻給世界環(huán)境...
    茶點故事閱讀 39,213評論 3 307
  • 文/蒙蒙 一豺型、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧买乃,春花似錦姻氨、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至功戚,卻和暖如春娶眷,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背啸臀。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評論 1 260
  • 我被黑心中介騙來泰國打工届宠, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 45,423評論 2 352
  • 正文 我出身青樓豌注,卻偏偏與公主長得像伤塌,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子幌羞,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,722評論 2 345

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