有了第一篇博客和上篇博客的介绍,结合大家的实践,相信对OscHub的基本框架有了清晰的认识。在这篇文章中,我们将完善应用的基本框架,让大家有一个对OscHub有个直观的认识。相信在学习了这篇文章后,朋友们对原代码(开源中国客户端)也会有更好进一步的了解。
这篇文章也将是“抄袭”开源中国客户端的一个终结,后续文章中,我将和大家一起研究该应用使用到的主要技术,包括View的自定义、表情符号的处理、网页内容的展示以及个别功能点的实现。在学习这些内容的同时,其实也是对OscHub的一个完善,最后呈现给大家的必将是一款高仿软件。
话不多说,开始今天内容的介绍。
上次我们已经在综合Tab中加入了两个子Tabs,包括资讯和热点,接下来我们来添加剩下的两个子Tabs*博客和推荐*。
其实,这两个子Tabs的实现,依旧遵照NewsFragment
的实现思想,继承我们的BaseListFragment
,根据传入的参数不同,来分别获取博客和推荐的列表内容:
public class BlogFragment extends BaseListFragment<Blog> implements
OnTabReselectListener {
public static final String BUNDLE_BLOG_TYPE = "BUNDLE_BLOG_TYPE";
protected static final String TAG = BlogFragment.class.getSimpleName();
private static final String CACHE_KEY_PREFIX = "bloglist_";
private String blogType;
@Override
protected BlogAdapter getListAdapter() {
return new BlogAdapter();
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Bundle args = getArguments();
if (args != null) {
blogType = args.getString(BUNDLE_BLOG_TYPE);
}
}
@Override
protected BlogList parseList(InputStream is) throws Exception {
BlogList list = XmlUtils.toBean(BlogList.class, is);
return list;
}
@Override
protected BlogList readList(Serializable seri) {
return ((BlogList) seri);
}
@Override
protected void sendRequestData() {
OSChinaApi.getBlogList(blogType, mCurrentPage, mHandler);
}
@Override
public void onItemClick(AdapterView<?> parent, View view, int position,
long id) {
}
@Override
public void onTabReselect() {
onRefresh();
}
}
同样地,我们需要一个对应的Adapter来适配我们的列表:
public class BlogAdapter extends ListBaseAdapter<Blog> {
@Override
protected View getRealView(int position, View convertView, ViewGroup parent) {
ViewHolder vh = null;
if (convertView == null || convertView.getTag() == null) {
convertView = getLayoutInflater(parent.getContext()).inflate(
R.layout.list_cell_news, null);
vh = new ViewHolder(convertView);
convertView.setTag(vh);
} else {
vh = (ViewHolder) convertView.getTag();
}
Blog blog = mDatas.get(position);
vh.tip.setVisibility(View.VISIBLE);
if (blog.getDocumenttype() == Blog.DOC_TYPE_ORIGINAL) {
vh.tip.setImageResource(R.drawable.widget_original_icon);
} else {
vh.tip.setImageResource(R.drawable.widget_repaste_icon);
}
vh.title.setText(blog.getTitle());
vh.description.setVisibility(View.GONE);
String description = blog.getBody();
if (null != description && !StringUtils.isEmpty(description)) {
vh.description.setVisibility(View.VISIBLE);
vh.description.setText(description.trim());
}
vh.source.setText(blog.getAuthor());
vh.time.setText(StringUtils.friendly_time(blog.getPubDate()));
vh.comment_count.setText(blog.getCommentCount() + "");
return convertView;
}
static class ViewHolder {
@Bind(R.id.tv_title)
TextView title;
@Bind(R.id.tv_description)
TextView description;
@Bind(R.id.tv_source)
TextView source;
@Bind(R.id.tv_time)
TextView time;
@Bind(R.id.tv_comment_count)
TextView comment_count;
@Bind(R.id.iv_tip)
ImageView tip;
public ViewHolder(View view) {
ButterKnife.bind(this, view);
}
}
}
有了这两个类,我们其实就已经完成了综合Tab的所有子Tabs的内容。不要忘记在NewsViewPagerFragment
中加入我们新添加的两个Tabs哦:
@Override
protected void onSetupTabAdapter(ViewPageFragmentAdapter adapter) {
String[] title = getResources().getStringArray(
R.array.news_viewpage_arrays);
adapter.addTab("news", title[0], NewsFragment.class,
getBundle(NewsList.CATALOG_ALL));
adapter.addTab("news_week", title[1], NewsFragment.class,
getBundle(NewsList.CATALOG_WEEK));
adapter.addTab("latest_blog", title[2], BlogFragment.class,
getBundle(BlogList.CATALOG_LATEST));
adapter.addTab("recommend_blog", title[3], BlogFragment.class,
getBundle(BlogList.CATALOG_RECOMMEND));
}
好了,介绍完了综合Tab的内容,下面我们继续介绍与其实现极其类似的另一个Tab——动弹。
动弹Tab同样需要一个ViewPagerFragment
类和其子Tabs用到的Fragment
类。在原代码中,它们分别是TweetsViewPagerFragment
类和TweetsFragment
类。与综合Tab不同的是,动弹Tab虽然包括了3个子Tabs,但是它们使用了同一个Fragment
类,即TweetsFragment
。
TweetsViewPagerFragment
类的实现与NewsViewPagerFragment
类的实现如出一辙,实现上并没有什么特殊的变化:
public class TweetsViewPagerFragment extends BaseViewPagerFragment implements OnTabReselectListener {
@Override
protected void onSetupTabAdapter(ViewPageFragmentAdapter adapter) {
String[] title = getResources().getStringArray(
R.array.tweets_viewpage_arrays);
adapter.addTab("new_tweets", title[0], TweetsFragment.class,
getBundle(TweetsList.CATALOG_LATEST));
adapter.addTab("hot_tweets", title[1], TweetsFragment.class,
getBundle(TweetsList.CATALOG_HOT));
adapter.addTab("my_tweets", title[2], TweetsFragment.class,
getBundle(TweetsList.CATALOG_ME));
}
private Bundle getBundle(int catalog) {
Bundle bundle = new Bundle();
bundle.putInt(BaseListFragment.BUNDLE_KEY_CATALOG, catalog);
return bundle;
}
@Override
public void initData() {
}
@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) {
}
}
}
而TweetsFragment
类的实现上,也完全遵照NewsFragment
的实现思路:
public class TweetsFragment extends BaseListFragment<Tweet> implements
OnItemLongClickListener, OnTabReselectListener {
protected static final String TAG = TweetsFragment.class.getSimpleName();
private static final String CACHE_KEY_PREFIX = "tweetslist_";
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
protected TweetAdapter getListAdapter() {
return new TweetAdapter();
}
public String getTopic() {
Bundle bundle = getArguments();
if (bundle != null) {
String str = bundle.getString("topic");
if (str != null) {
return str;
}
}
return "";
}
@Override
protected TweetsList parseList(InputStream is) throws Exception {
TweetsList list = XmlUtils.toBean(TweetsList.class, is);
return list;
}
@Override
protected TweetsList readList(Serializable seri) {
return ((TweetsList) seri);
}
@Override
protected void sendRequestData() {
Bundle bundle = getArguments();
if (bundle != null) {
String str = bundle.getString("topic");
if (str != null) {
OSChinaApi.getTweetTopicList(mCurrentPage, str, mHandler);
return;
}
}
OSChinaApi.getTweetList(mCatalog, mCurrentPage, mHandler);
}
@Override
public void onItemClick(AdapterView<?> parent, View view, int position,
long id) {
}
protected void requestData(boolean refresh) {
if (mCatalog > 0) {
mErrorLayout.setErrorType(EmptyLayout.NETWORK_ERROR);
mErrorLayout.setErrorMessage(getString(R.string.unlogin_tip));
} else {
super.requestData();
}
}
@Override
public void initView(View view) {
super.initView(view);
mListView.setOnItemLongClickListener(this);
mErrorLayout.setOnLayoutClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mCatalog > 0) {
} else {
mErrorLayout.setErrorType(EmptyLayout.NETWORK_LOADING);
requestData(true);
}
}
});
}
@Override
public boolean onItemLongClick(AdapterView<?> parent, View view,
int position, long id) {
return false;
}
@Override
public void onTabReselect() {
onRefresh();
}
}
唯一不同的是,其在展示内容的适配器上。在展示用户的动弹时,不仅仅要展示发布动弹的用户头像,还要展示其多样的动弹内容。现在的问题时,我们不能预知用户会发送什么类型的动弹,所以在原代码中引入了LinkMovementMethod
类和URLSpan
类,也提供了一些处理内容的工具类,比如InputHelper
、TypefaceUtils
等工具类。这些类或者方法主要用于处理用户发送内容中的链接和表情符号等。由于这些类与具体的业务关系不大,我这里暂不铺开陈述,有兴趣的朋友可以参考源码来学习。在后续的博客中,我也将会把一些个人觉得比较关键和重要的类和方法介绍给大家,并结合原代码讲解其使用的场景。
@Override
protected View getRealView(final int position, View convertView,
final ViewGroup parent) {
context = parent.getContext();
final ViewHolder vh;
if (convertView == null || convertView.getTag() == null) {
convertView = getLayoutInflater(parent.getContext()).inflate(
R.layout.list_cell_tweet, null);
vh = new ViewHolder(convertView);
convertView.setTag(vh);
} else {
vh = (ViewHolder) convertView.getTag();
}
final Tweet tweet = mDatas.get(position);
vh.del.setVisibility(View.GONE);
vh.author.setText(tweet.getAuthor());
vh.time.setText(StringUtils.friendly_time(tweet.getPubDate()));
vh.content.setMovementMethod(MyLinkMovementMethod.a());
vh.content.setFocusable(false);
vh.content.setDispatchToParent(true);
vh.content.setLongClickable(false);
Spanned span = Html.fromHtml(tweet.getBody().trim());
if (!StringUtils.isEmpty(tweet.getAttach())) {
SpannableString str = new SpannableString("c");
vh.content.setText(str);
span = InputHelper.displayEmoji(context.getResources(), span);
vh.content.append(span);
} else {
span = InputHelper.displayEmoji(context.getResources(), span);
vh.content.setText(span);
}
MyURLSpan.parseLinkText(vh.content, span);
vh.commentcount.setText(tweet.getCommentCount() + "");
tweet.setLikeUsers(context, vh.likeUsers, true);
final ViewHolder vh1 = vh;
if (tweet.getLikeUser() == null) {
vh.tvLikeState.setVisibility(View.GONE);
}
TypefaceUtils.setTypeface(vh.tvLikeState);
if (tweet.getIsLike() == 1) {
vh.tvLikeState.setTextColor(AppContext.getInstance().getResources().getColor(R.color.day_colorPrimary));
} else {
vh.tvLikeState.setTextColor(AppContext.getInstance().getResources().getColor(R.color.gray));
}
PlatfromUtil.setPlatFromString(vh.platform, tweet.getAppclient());
return convertView;
}
有了上面的两个类,我们的应用框架基本上就已经完整了,其它列表的实现不过这两类列表的变种(除了开源软件->分类列表,这个在后面我会重点介绍,这里暂且不表)。
另外两个主Tabs——发现Tab和我Tab主要是几个功能的入口,每一个功能的实现有其自己的特点。我个人觉得没必要针对它们的实现来一一给大家介绍,所以后面我会挑选个人觉得比较好的功能来跟大家一起学习。
好了,我们的“抄袭”开源中国客户端之路就告一段落了,后面我会根据不同的功能还和大家一起学习研究,同时也会将之前遗留的个人觉得比较重要的知识点来与大家一起分享,比如列表的下拉刷新,圆形头像的处理等等。敬请期待。
最后,附上本期的运行效果图和源码下载地址:
源码下载,请出门左转。
Commit版本:60f828