接上期,着重分析主干逻辑,先看第一个综合页面。

五个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);
综合页当然就是NewsViewPagerFragment了,我们自顶向下逐步分析。
BaseFragment extends Fragment
预定义了多种状态,有可能会在子类中用到:
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;// 正在下拉但还没有到刷新的状态
public static int mState = STATE_NONE;
主要是调用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
官方描述:带有导航条的基类。
这一点从其持有的域也可以看出:
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" >
<!-- 导航标题栏 -->
<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()中调用了如下两行代码,不用说,当然是留给子类覆盖的了:
setScreenPageLimit();
onSetupTabAdapter(mTabsAdapter);
NewsViewPagerFragment extends BaseViewPagerFragment
实现刚才说到的方法:
@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页保留了下来。
比较有意思的是,它还实现了这样一个接口:
/**
* 当tabHost再次被点击时
* @author FireAnt(http://my.oschina.net/LittleDY)
* @version 创建时间:2014年11月17日 上午11:00:15
*
*/
public interface OnTabReselectListener {
public void onTabReselect();
}
具体来说是看该Fragment中嵌套的Fragment是否有实现此接口然后决定是否调用:
@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) {
}
}
现在,我们来看综合页中的NewsFragment,同样是自顶向下。
BaseListFragment<T extends Entity> extends BaseFragment
这是一个泛型类,Bean包下有大量extends Entity的实体。
关于泛型可以参考这篇:http://www.jianshu.com/p/f258c907019d
使用了可以进行下拉刷新的布局,以下为代码片段:
<!-- 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中有如下调用:
new SaveCacheTask(getActivity(), data, getCacheKey()).execute();
这并不是一个好的实现,因为AsyncTask应该在主线程中创建,否则其内部的静态Handler将无法正常工作,具体可以参考《Android开发艺术探索》。
这里该应用的事件流中应该是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()中读取前文注一提供的信息,实际使用当然会是在子类中了:
@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()中判断了是否要进行定时刷新操作:
@Override
public void onResume() {
super.onResume();
if (onTimeRefresh()) {
onRefresh();
}
}
NewsFragment extends BaseListFragment<News> implements OnTabReselectListener
明显使用了News这个Bean,取决于具体业务情况,略去不表。
Bean解析,可以看出数据使用的是xml格式,这一方法的实际调用位置在基类的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;
}
覆盖获取数据的方法,使用注二中提供的信息调用MobileAPI:
@Override
protected void sendRequestData() {
OSChinaApi.getNewsList(mCatalog, mCurrentPage, mHandler);
}
之前提过的OnTabReselectListener有进行实现,可以看出是提供了一个刷新功能:
@Override
public void onTabReselect() {
onRefresh();
}
而这个onRefresh()的实现是在基类BaseListFragment当中,也就是设置状态,ListView回到顶部,然后获取数据:
// 下拉刷新数据
@Override
public void onRefresh() {
if (mState == STATE_REFRESH) {
return;
}
// 设置顶部正在刷新
mListView.setSelection(0);
setSwipeRefreshLoadingState();
mCurrentPage = 0;
mState = STATE_REFRESH;
requestData(true);
}
具体获取数据的时候,视情况看是读缓存还是发请求获取。
缓存的实现是CacheManager,非常纯粹的文件读写,略去不表。
根据业务情况,提供定时刷新的阈值给基类:
@Override
protected long getAutoRefreshTime() {
// 最新资讯两小时刷新一次
if (mCatalog == NewsList.CATALOG_ALL) {
return 2 * 60 * 60;
}
return super.getAutoRefreshTime();
}
另外三个页面也是采用完全类似的方式,读者可以自行分析。
至此,综合页面分析完毕。下期再见。
网友评论