Android練手小項目(KTReader)基于mvp架構(gòu)(三)

上路傳送眼:

Android練手小項目(KTReader)基于mvp架構(gòu)(二)

下路傳送眼:

Android練手小項目(KTReader)基于mvp架構(gòu)(四)

GIthub地址: https://github.com/yiuhet/KTReader

上篇文章中我們完成了知乎日報內(nèi)容的fragment割按。
而這次我們要做的的就是知乎日報詳情頁。

慣例先上效果圖:


效果圖

準(zhǔn)備工作

  • 添加依賴

compile 'com.jude:swipebackhelper:3.1.2' //右滑關(guān)閉詳情頁

  • 工具類
    utils.WebUtil.class(返回html結(jié)果的工具類):
public class WebUtil {
    private WebUtil() {
    }

    public static final String BASE_URL = "file:///android_asset/";
    public static final String MIME_TYPE = "text/html";
    public static final String ENCODING = "utf-8";
    public static final String FAIL_URL = "http//:daily.zhihu.com/";

    private static final String CSS_LINK_PATTERN = " <link href=\"%s\" type=\"text/css\" rel=\"stylesheet\" />";
    private static final String DIV_IMAGE_PLACE_HOLDER = "class=\"img-place-holder\"";

    public static String buildHtmlWithCss(String html, List<String> cssUrls) {
        StringBuilder result = new StringBuilder();
        for (String cssUrl : cssUrls) {
            result.append(String.format(CSS_LINK_PATTERN, cssUrl));
        }
        result.append(html.replace(DIV_IMAGE_PLACE_HOLDER, ""));
        return result.toString();
    }
}

Model層

  • 模型實體類ZhihuDetail直接使用GsonFormat工具快速生成
    (model.entity.ZhihuDetail)

  • 知乎日報Model接口
    model.ZhihuDetailModel:

public interface ZhihuDetailModel {

    void loadDetail(String id, OnZhihuDetailListener listener);
}
  • 獲取日報詳情的Model實現(xiàn)
public class ZhihuDetailModelImp1 implements ZhihuDetailModel {
    // /*獲取日報詳情的Model實現(xiàn)*/

    private ZhihuApi mZhihuApiService; //請求服務(wù)
    private ZhihuDetail mZhihuDetail;

    public ZhihuDetailModelImp1() {
        mZhihuDetail = new ZhihuDetail();
        mZhihuApiService = RetrofitManager
                .getInstence()
                .getRetrofit("http://news-at.zhihu.com/api/4/news/")
                .create(ZhihuApi.class); //創(chuàng)建請求服務(wù)
    }

    public ZhihuDetail getDetail() {
        return mZhihuDetail;
    }


    @Override
    public void loadDetail(String id, final OnZhihuDetailListener listener) {
        if (mZhihuApiService != null) {
            mZhihuApiService.getDetail(id)
                    .subscribeOn(Schedulers.io())
                    .observeOn(AndroidSchedulers.mainThread())
                    .subscribe(new Observer<ZhihuDetail>() {
                        @Override
                        public void onSubscribe(@NonNull Disposable d) {

                        }

                        @Override
                        public void onNext(@NonNull ZhihuDetail zhihuDetail) {
                            mZhihuDetail = zhihuDetail;
                            listener.onLoadZhihuDetailSuccess(zhihuDetail);//加載成功時 回調(diào)接口方法柴罐。
                        }

                        @Override
                        public void onError(@NonNull Throwable e) {
                            listener.onLoadDataError(e.toString());//加載失敗時 回調(diào)接口方法芹枷。
                        }

                        @Override
                        public void onComplete() {

                        }
                    });
        }
    }
}

View層

  • 創(chuàng)建回調(diào)接口
    view.ZhihuDetailView:
public interface ZhihuDetailView {
    void onStartGetData();

    void onGetDetailSuccess(ZhihuDetail zhihuDetail);

    void onGetDetailFailed(String error);
}
  • 創(chuàng)建ZhihuDetailActivity
    詳情頁包含的新知識點全在ZhihuDetailActivity上,所涵蓋的知識有:
    • WebView的使用
    • 右滑關(guān)閉activity(也不算知識點斤富,因為使用別人的開源圾旨,以后要改寫成自己的)
    • CollapsingToolbarLayout 和NestedScrollView的使用

附上一些資料:
WebView·開車指南
看妙黍,這個工具欄能伸縮折疊——Android CollapsingToolbarLayout使用
[Jude95/SwipeBackHelper]

具體解釋全都在代碼注釋里稚铣。
直接上代碼
ui.activity.ZhihuDetailActivity .class:

public class ZhihuDetailActivity extends MVPBaseActivity<ZhihuDetailView, ZhihuDetailPresenterImp1> implements ZhihuDetailView {

    @BindView(R.id.toolbar)
    Toolbar mToolbar;
    @BindView(R.id.toolbar_layout)
    CollapsingToolbarLayout mToolbarLayout;
    @BindView(R.id.prograss)
    ProgressBar mPrograss;
    @BindView(R.id.wv_zhihu)
    WebView mWvZhihu;
    @BindView(R.id.fab)
    FloatingActionButton mFab;
    @BindView(R.id.iv_title)
    ImageView mIvTitle;
    private String mZhihuId;

    @Override
    protected ZhihuDetailPresenterImp1 createPresenter() {
        return new ZhihuDetailPresenterImp1(this);
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        SwipeBackHelper.onCreate(this);
        ButterKnife.bind(this);
        initToolbar();
        initView();
    }

    private void initToolbar() {
        setSupportActionBar(mToolbar);
        getSupportActionBar().setDisplayHomeAsUpEnabled(true);
        getSupportActionBar().setHomeButtonEnabled(true);
    }

    private void initView() {
        mZhihuId = getIntent().getStringExtra("ZHIHUID");
        mPresenter.getDetail(mZhihuId);
        mFab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Snackbar.make(view, "已添加進收藏夾(待做功能)", Snackbar.LENGTH_LONG)
                        .setAction("Action", null).show();
            }
        });
        mWvZhihu.setVerticalScrollBarEnabled(true);
        mWvZhihu.setScrollBarStyle(View.SCROLLBARS_OUTSIDE_INSET);

        WebSettings settings = mWvZhihu.getSettings();
        //設(shè)置應(yīng)用緩存路徑衰伯,這個路徑必須是可以讓app寫入文件的铡羡。該方法應(yīng)該只被調(diào)用一次,重復(fù)調(diào)用會被無視~
        settings.setAppCachePath(getCacheDir().getAbsolutePath() + "/webViewCache");
        settings.setAppCacheEnabled(true); //啟用應(yīng)用緩存意鲸。
        settings.setDatabaseEnabled(true); //啟用數(shù)據(jù)庫緩存烦周。
        settings.setDomStorageEnabled(true); //開啟DOM緩存
        //用來設(shè)置WebView的緩存模式(這里使用的是 只要緩存可用就加載緩存)
        settings.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);
        settings.setJavaScriptEnabled(true); //設(shè)置WebView可以運行JavaScript。
        settings.setBuiltInZoomControls(true);//顯示或不顯示縮放按鈕(wap網(wǎng)頁不支持)临扮。
        //指定WebView的頁面布局顯示形式论矾,調(diào)用該方法會引起頁面重繪
        settings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.SINGLE_COLUMN);
        mWvZhihu.setWebChromeClient(new WebChromeClient());

    }

    @Override
    protected int getLayoutRes() {
        return R.layout.activity_zhihu_detail;
    }

    @Override
    public void onStartGetData() {
        mPrograss.setVisibility(View.VISIBLE);
    }

    @Override
    public void onGetDetailSuccess(ZhihuDetail zhihuDetail) {
        mPrograss.setVisibility(View.GONE);
        mToolbarLayout.setTitle(zhihuDetail.title);
        //在較為特殊的情況下,知乎日報可能將某個主題日報的站外文章推送至知乎日報首頁杆勇。
        if (zhihuDetail.body == null) {
            mWvZhihu.loadUrl(zhihuDetail.shareUrl);
        } else {
            Glide.with(this).load(zhihuDetail.image).into(mIvTitle);
            String data = WebUtil.buildHtmlWithCss(zhihuDetail.body, zhihuDetail.css);
            mWvZhihu.loadDataWithBaseURL(WebUtil.BASE_URL, data, WebUtil.MIME_TYPE, WebUtil.ENCODING, WebUtil.FAIL_URL);
        }
    }

    @Override
    public void onGetDetailFailed(String error) {
        mPrograss.setVisibility(View.GONE);
        toast(error);
    }

    @Override
    protected void onPostCreate(@Nullable Bundle savedInstanceState) {
        super.onPostCreate(savedInstanceState);
        SwipeBackHelper.onPostCreate(this);
    }

    @Override
    protected void onDestroy() {
        if (mWvZhihu != null) {
            //webview內(nèi)存泄露
            ((ViewGroup) mWvZhihu.getParent()).removeView(mWvZhihu);
            mWvZhihu.destroy();
            mWvZhihu = null;
        }
        super.onDestroy();
        SwipeBackHelper.onDestroy(this);
    }
}

另附上ZhihuDetailActivity的布局文件:

activity_zhihu_detail.xml:

<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"
    tools:context="com.example.yiuhet.ktreader.ui.activity.ZhihuDetailActivity">

    <android.support.design.widget.AppBarLayout
        android:id="@+id/app_bar"
        android:layout_width="match_parent"
        android:layout_height="@dimen/app_bar_height"
        android:fitsSystemWindows="true"
        android:theme="@style/AppTheme.AppBarOverlay">

        <android.support.design.widget.CollapsingToolbarLayout
            android:id="@+id/toolbar_layout"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:fitsSystemWindows="true"
            app:contentScrim="?attr/colorPrimary"
            app:expandedTitleMarginBottom="16dp"
            app:expandedTitleMarginStart="24dp"
            app:expandedTitleTextAppearance="@style/TextAppearance.AppCompat.Title"
            app:layout_scrollFlags="scroll|exitUntilCollapsed">

            <ImageView
                android:id="@+id/iv_title"
                android:layout_width="match_parent"
                android:scaleType="centerCrop"
                app:layout_collapseMode="parallax"
                app:layout_collapseParallaxMultiplier="0.5"
                android:fitsSystemWindows="true"
                android:layout_height="match_parent"/>
            <FrameLayout
                android:layout_width="match_parent"
                android:layout_height="48dp"
                android:layout_gravity="bottom"
                android:background="@color/title_mask"></FrameLayout>
            <android.support.v7.widget.Toolbar
                android:id="@+id/toolbar"
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize"
                app:layout_collapseMode="pin"
                app:layout_scrollFlags="scroll|enterAlways|snap"
                app:popupTheme="@style/AppTheme.PopupOverlay" />

        </android.support.design.widget.CollapsingToolbarLayout>
    </android.support.design.widget.AppBarLayout>

    <include layout="@layout/content_zhihu_detail" />

    <android.support.design.widget.FloatingActionButton
        android:id="@+id/fab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="@dimen/fab_margin"
        app:layout_anchor="@id/app_bar"
        app:layout_anchorGravity="bottom|end"
        app:srcCompat="@android:drawable/ic_dialog_email" />

</android.support.design.widget.CoordinatorLayout>

content_zhihu_detail.xml:

<android.support.v4.widget.NestedScrollView 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"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    tools:context="com.example.yiuhet.ktreader.ui.activity.ZhihuDetailActivity"
    tools:showIn="@layout/activity_zhihu_detail">

        <WebView
            android:id="@+id/wv_zhihu"
            android:layout_width="match_parent"
            android:layout_height="match_parent">
            <ProgressBar
                android:id="@+id/prograss"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_gravity="center"
                android:indeterminate="true"
                android:indeterminateTint="?android:colorAccent"/>
        </WebView>
</android.support.v4.widget.NestedScrollView>

ZhihuDetailActivity里的fab點擊事件預(yù)想是添加入收藏欄贪壳,等之后寫到收藏欄功能時會完成該功能
ps:從效果圖中,可以看出CollapsingToolbarLayout的title顯示不全蚜退,目前還沒找到解決辦法(貌似可以寫theme解決闰靴,回頭再實驗下),如果有知道的钻注,請告知蚂且,謝謝。

Presenter層

在ZhihuPresenterImp1類里實現(xiàn)數(shù)據(jù)和視圖的綁定

  • 先寫一個回調(diào)接口:
    (在Presenter層實現(xiàn)幅恋,給Model層回調(diào)杏死,更改View層的狀態(tài),確保Model層不直接操作View層)
    presenter.OnZhihuDetailListener :
public interface OnZhihuDetailListener {

    void onLoadZhihuDetailSuccess(ZhihuDetail zhihuDetail);

    void onLoadDataError(String error);
}
  • 再寫一個presenter接口:
    presenter.ZhihuDetailPresenter :
public interface ZhihuDetailPresenter {
    void getDetail(String id);
}
  • 最后寫Prestener實現(xiàn)類:
    presenter.imp1.ZhihuDetailPresenterImp1 .class:
public class ZhihuDetailPresenterImp1 extends BasePresenter<ZhihuDetailView> implements ZhihuDetailPresenter,OnZhihuDetailListener{
    /*Presenter作為中間層,持有View和Model的引用*/
    private ZhihuDetailView mZhihuDetailView;
    private ZhihuDetailModelImp1 zhihuDetailModelImp1;
    String id;

    public ZhihuDetailPresenterImp1(ZhihuDetailView zhihuDetailView) {
        mZhihuDetailView = zhihuDetailView;
        zhihuDetailModelImp1 = new ZhihuDetailModelImp1();
    }


    @Override
    public void getDetail(String id) {
        mZhihuDetailView.onStartGetData();
        zhihuDetailModelImp1.loadDetail(id, this);
    }


    @Override
    public void onLoadZhihuDetailSuccess(ZhihuDetail zhihuDetail) {

        mZhihuDetailView.onGetDetailSuccess(zhihuDetail);
    }

    @Override
    public void onLoadDataError(String error) {
        mZhihuDetailView.onGetDetailFailed(error);
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末淑翼,一起剝皮案震驚了整個濱河市腐巢,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌玄括,老刑警劉巖冯丙,帶你破解...
    沈念sama閱讀 210,978評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異遭京,居然都是意外死亡胃惜,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,954評論 2 384
  • 文/潘曉璐 我一進店門哪雕,熙熙樓的掌柜王于貴愁眉苦臉地迎上來船殉,“玉大人,你說我怎么就攤上這事热监∞嘞遥” “怎么了?”我有些...
    開封第一講書人閱讀 156,623評論 0 345
  • 文/不壞的土叔 我叫張陵孝扛,是天一觀的道長。 經(jīng)常有香客問我幽崩,道長苦始,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,324評論 1 282
  • 正文 為了忘掉前任慌申,我火速辦了婚禮陌选,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘蹄溉。我一直安慰自己咨油,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 65,390評論 5 384
  • 文/花漫 我一把揭開白布柒爵。 她就那樣靜靜地躺著役电,像睡著了一般。 火紅的嫁衣襯著肌膚如雪棉胀。 梳的紋絲不亂的頭發(fā)上法瑟,一...
    開封第一講書人閱讀 49,741評論 1 289
  • 那天,我揣著相機與錄音唁奢,去河邊找鬼霎挟。 笑死,一個胖子當(dāng)著我的面吹牛麻掸,可吹牛的內(nèi)容都是我干的酥夭。 我是一名探鬼主播,決...
    沈念sama閱讀 38,892評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼熬北!你這毒婦竟也來了千所?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,655評論 0 266
  • 序言:老撾萬榮一對情侶失蹤蒜埋,失蹤者是張志新(化名)和其女友劉穎淫痰,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體整份,經(jīng)...
    沈念sama閱讀 44,104評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡待错,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,451評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了烈评。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片火俄。...
    茶點故事閱讀 38,569評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖讲冠,靈堂內(nèi)的尸體忽然破棺而出瓜客,到底是詐尸還是另有隱情,我是刑警寧澤竿开,帶...
    沈念sama閱讀 34,254評論 4 328
  • 正文 年R本政府宣布谱仪,位于F島的核電站,受9級特大地震影響否彩,放射性物質(zhì)發(fā)生泄漏疯攒。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,834評論 3 312
  • 文/蒙蒙 一列荔、第九天 我趴在偏房一處隱蔽的房頂上張望敬尺。 院中可真熱鬧,春花似錦贴浙、人聲如沸砂吞。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,725評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蜻直。三九已至,卻和暖如春笨奠,著一層夾襖步出監(jiān)牢的瞬間袭蝗,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,950評論 1 264
  • 我被黑心中介騙來泰國打工般婆, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留到腥,地道東北人。 一個月前我還...
    沈念sama閱讀 46,260評論 2 360
  • 正文 我出身青樓蔚袍,卻偏偏與公主長得像乡范,于是被迫代替她去往敵國和親配名。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,446評論 2 348

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