一款適合安卓開(kāi)發(fā)入門(mén)者學(xué)習(xí)的 知乎日?qǐng)?bào) APP
獻(xiàn)給Leonardo da Vinci
前言
本文的內(nèi)容主要是解析Tom ZhiHuDaily APP 的制作流程,以及代碼的具體實(shí)現(xiàn)活喊,若有什么不足之處英古,還請(qǐng)?zhí)岢鼋ㄗh查蓉,附上這個(gè) APP 的 Github 地址 Tom ZhiHuDaily 歡迎大家 star 和 fork.
Tom ZhiHuDaily使用的技術(shù):
- Rxjava & Retrofit
- Gson & RxAndroid
- Glide & Material Design
- ButterKnife & EventBus
- Banner & LitePal
Tom ZhiHuDaily可實(shí)現(xiàn)功能:
- 知乎Stories TopStories的展示
- 知乎TopStories的輪播
- 知乎Stories 的上翻下翻
- 知乎Stories 的收藏
- 閃屏頁(yè)屬性動(dòng)畫(huà)
本文的主要內(nèi)容
- 知乎TopStories的輪播的實(shí)現(xiàn)(Rxjava & Retrofit & Banner)
- DetailStories的實(shí)現(xiàn)(Glide & WebView)
- 知乎Stories 收藏的實(shí)現(xiàn)(Litepal)
- 知乎 長(zhǎng)評(píng)論 短評(píng)論的實(shí)現(xiàn)(viewpager + tabLayout)
- 閃屏頁(yè)屬性動(dòng)畫(huà)的實(shí)現(xiàn)
先來(lái)一波Tom ZhiHuDaily的展示吧毒涧,這款 APP 還是非常精美和優(yōu)雅的
- 上翻下翻 知乎Stories 和 WebView加載NewsDetail的效果
- 知乎TopStories的輪播 和 知乎Stories 的收藏的效果
1魂拦、利用Retrofit+Rxjava 從JSon數(shù)據(jù)轉(zhuǎn)成對(duì)象
private static Retrofit create() {
OkHttpClient.Builder builder = new OkHttpClient().newBuilder();
builder.readTimeout(10, TimeUnit.SECONDS);
builder.connectTimeout(9, TimeUnit.SECONDS);
return new Retrofit.Builder()
.baseUrl("http://news-at.zhihu.com/api/4/")
.client(builder.build())
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build();
}
Retrofit retrofit = create();
ClientApi api = retrofit.create(ClientApi.class);
Observable<Result> observable = api.getTodayNews();
observable.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<Result>() {
@Override
public void onSubscribe(Disposable d) {
}
@Override
public void onNext(Result result) {
storiesList.addAll(result.getStories());
storiesAdapter.notifyDataSetChanged();
int topSize=result.getTopStories().size();
for(int i=0;i<topSize;i++)
{
TopStories topStories=result.getTopStories().get(i);
bannerStories.add(convertStory(topStories));
bannerImages.add(topStories.getImage());
bannerTitles.add(topStories.getTitle());
}
mBanner.setImages(bannerImages);
mBanner.setBannerTitles(bannerTitles);
mBanner.start();
}
@Override
public void onError(Throwable e) {
}
@Override
public void onComplete() {
}
});
2、利用Banner實(shí)現(xiàn)TopStories輪播
<com.youth.banner.Banner
android:id="@+id/banner"
android:layout_width="match_parent"
android:layout_height="250dp">
</com.youth.banner.Banner>
public class GlideImageLoader extends ImageLoader
{
@Override
public void displayImage(Context context, Object path, ImageView imageView) {
Glide.with(context).load(path).into(imageView);
}
@Override
public ImageView createImageView(Context context) {
return null;
}
}
二做瞪、DetailStories的實(shí)現(xiàn)
1对粪、利用Material Design 生成布局
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fitsSystemWindows="true">
<android.support.design.widget.CollapsingToolbarLayout
android:id="@+id/collapsingToolbarLayout"
android:layout_width="match_parent"
android:layout_height="256dp"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:contentScrim="?attr/colorPrimary"
android:fitsSystemWindows="true"
app:expandedTitleMarginEnd="96dp"
app:expandedTitleTextAppearance="@style/CollapsingToolbarTitleStyle.About"
app:layout_scrollFlags="scroll|exitUntilCollapsed">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
app:layout_collapseMode="parallax"
app:layout_collapseParallaxMultiplier="0.8"
app:layout_scrollFlags="scroll|snap|enterAlways|enterAlwaysCollapsed">
<ImageView
android:id="@+id/iv_header"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:contentDescription="@null"
android:scaleType="centerCrop" />
<View
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="bottom"
android:background="@drawable/bg_shadow_mask" />
<TextView
android:id="@+id/tv_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:layout_marginBottom="16dp"
android:padding="16dp"
android:textColor="@android:color/white"
android:textSize="22sp"
tools:text="I am Fine" />
<TextView
android:id="@+id/tv_source"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end|bottom"
android:padding="8dp"
android:textColor="@color/color_source"
android:textSize="13sp"
tools:text="the source of pic" />
</FrameLayout>
<include layout="@layout/include_toolbar" />
</android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>
<android.support.v4.widget.NestedScrollView
android:id="@+id/nested_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="vertical"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<WebView
android:id="@+id/wv_news"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:isScrollContainer="false"
android:scrollbars="vertical" />
</android.support.v4.widget.NestedScrollView>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="128dp">
</FrameLayout>
</android.support.design.widget.CoordinatorLayout>
2、利用HtmlUtils生成WebView要加載的 htmlData
public static String createHtmlData(NewsDetail newsDetail, boolean isNight) {
final String css = HtmlUtil.createCssTag(newsDetail.getCss());
final String js = HtmlUtil.createJsTag(newsDetail.getJs());
final String body = handleHtml(newsDetail.getBody(), isNight).toString();
return createHtmlData(body, css, js);
}
public static StringBuffer handleHtml(String body, boolean isNight) {
StringBuffer stringBuffer = new StringBuffer();
stringBuffer.append("<html><head><link rel=\"stylesheet\" type=\"text/css\" href=\"css/detail.css\" ></head>");
stringBuffer.append(isNight ? "<body class=\"night\">" : "<body>");
stringBuffer.append(body);
stringBuffer.append("</body></html>");
return stringBuffer;
}
三装蓬、知乎Stories 收藏的實(shí)現(xiàn)
1著拭、利用litepal建立表
LitePal是一款開(kāi)源的Android數(shù)據(jù)庫(kù)框架,采用對(duì)象關(guān)系映射(ORM)模式牍帚,將常用的數(shù)據(jù)庫(kù)功能進(jìn)行封裝儡遮,可以不用寫(xiě)一行SQL語(yǔ)句就可以完成創(chuàng)建表、增刪改查的操作暗赶。
相關(guān)教程可以參見(jiàn)鏈接litepal
<litepal>
<dbname value="News"></dbname>
<version value="1"></version>
<list>
<mapping class="com.bignerdranch.myrxmeizi.bean.Stories"></mapping>
</list>
</litepal>
2鄙币、 利用litePal 進(jìn)行 增刪查詢(xún)
case R.id.favorite_one:
if(!isFavourite)
{
Stories stories1=new Stories();
stories1.setTitle(stories.getTitle());
stories1.setImages(stories.getImages());
stories1.setId(stories.getId());
stories1.setGaPrefix(stories.getGaPrefix());
stories1.setMultipic(stories.getMultipic());
stories1.setType(stories.getType());
stories1.save();
item.setIcon(R.drawable.fav_active);
isFavourite = true;
}else
{
LitePal.deleteAll(Stories.class,"mId = ?",stories.getId()+"");
item.setIcon(R.drawable.fav_normal);
isFavourite = false;
}
return true;
storiesList=new ArrayList<>();
storiesList= LitePal.findAll(Stories.class);
for(int i=0;i<storiesList.size();i++)
{
Log.i("MainActivityATS",storiesList.get(i).getTitle());
}
adapter=new FavoriteStoriesAdapter(getActivity(),storiesList);
recyclerView.setAdapter(adapter);
四、知乎 長(zhǎng)評(píng)論 短評(píng)論的實(shí)現(xiàn)
1蹂随、生成繼承 RecyclerView.Adapter的 CommentsAdapter
2十嘿、生成繼承 FragmentPagerAdapter的 CommentPagerAdapter
3、生成 CommentsFragment糙及,并用 CommentsFragment 構(gòu)建 長(zhǎng)評(píng)論 短評(píng)論的Fragments
CommentsFragment shortCommentsFragment = new CommentsFragment();
Bundle bundleForShortComments = new Bundle();
bundleForShortComments.putLong("id", id);
bundleForShortComments.putSerializable("storyExtra", storyExtra);
bundleForShortComments.putString("url", "http://news-at.zhihu.com/api/4/story/%1$s/short-comments");
bundleForShortComments.putInt("count", storyExtra.getShortComments());
shortCommentsFragment.setArguments(bundleForShortComments);
CommentsFragment longCommentsFragment = new CommentsFragment();
Bundle bundleForLongComments = new Bundle();
bundleForLongComments.putLong("id", id);
bundleForLongComments.putSerializable("storyExtra", storyExtra);
bundleForLongComments.putString("url", "http://news-at.zhihu.com/api/4/story/%1$s/long-comments");
bundleForShortComments.putInt("counts", storyExtra.getLongComments());
longCommentsFragment.setArguments(bundleForLongComments);
4详幽、生成 CommentsActivity,并 為viewPager 和tabLayout setAdapter
fragments.add(shortCommentsFragment);
fragments.add(longCommentsFragment);
CommentPagerAdapter adapter = new CommentPagerAdapter(getSupportFragmentManager(), fragments, tabList);
viewPager.setAdapter(adapter);
tabLayout.setupWithViewPager(viewPager);
tabLayout.setTabsFromPagerAdapter(adapter);
五浸锨、 閃屏頁(yè)屬性動(dòng)畫(huà)的實(shí)現(xiàn)
SimpleTarget target = new SimpleTarget<Bitmap>() {
@Override
public void onResourceReady(Bitmap bitmap, GlideAnimation glideAnimation) {
iv.setImageBitmap(bitmap);
iv.setPivotX(bitmap.getWidth() * 0.3f);
iv.setPivotY(bitmap.getHeight() * 0.25f);
ObjectAnimator objectAnimatorX = ObjectAnimator.ofFloat(iv, "scaleX", 1, 1.25f);
ObjectAnimator objectAnimatorY = ObjectAnimator.ofFloat(iv, "scaleY", 1, 1.25f);
AnimatorSet set = new AnimatorSet();
set.setDuration(2000).setStartDelay(1000);
set.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
startActivity(new Intent(SplashActivity.this, NewsListActivity.class));
finish();
}
});
set.playTogether(objectAnimatorX, objectAnimatorY);
set.start();
}
};
Glide.with(this).load(R.drawable.davinci2).asBitmap().into(target);
1.設(shè)置圖片和縮放中心坐標(biāo)
2.設(shè)置放大到最大XY坐標(biāo)
3.設(shè)置動(dòng)畫(huà)持續(xù)時(shí)間和延遲時(shí)間
4.設(shè)置在動(dòng)畫(huà)結(jié)束時(shí)跳轉(zhuǎn)Activity
5.設(shè)置同時(shí)執(zhí)行XY坐標(biāo)動(dòng)畫(huà)并開(kāi)啟屬性動(dòng)畫(huà)
以上便是我寫(xiě)這個(gè) APP 的具體實(shí)現(xiàn)思路,以及踩過(guò)的一些坑版姑,記錄下來(lái)柱搜,給大家看看,最后附上這個(gè) APP 的 Github 地址 Tom ZhiHuDaily
歡迎大家 star 和 fork剥险,如果有什么想法或者建議聪蘸,非常歡迎大家來(lái)討論
License
There is no fucking license.