WanAndroid实战——首页文章

作者: Tom_Ji | 来源:发表于2019-03-14 16:06 被阅读10次

    前情回顾:
    1.WanAndroid实战——首页Banner

    前面完成了首页Banner的实现,本次继续来完成首页文章的获取和显示。

    按照惯例,先上实现过的效果图。

    首页文章效果

    1.相关布局文件

    首页文章的展示使用的是recycleView,每一项使用的是cardview来进行,因此,先在gradle中引入需要的依赖。

    
        //cardview
        implementation 'com.android.support:cardview-v7:28.0.0'
    
        //recycleview
        implementation 'com.android.support:recyclerview-v7:28.0.0'
    

    activity_main.xml文件中添加recycleView的相关内容,修改后的文件如下:

    
    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout
        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"
        tools:context=".view.MainActivity"
        android:orientation="vertical">
    
        <com.youth.banner.Banner
            android:id="@+id/main_banner"
            android:layout_width="match_parent"
            android:layout_height="@dimen/banner_height"/>
    
    
        <android.support.v7.widget.RecyclerView
            android:id="@+id/article_content"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            />
    
    </LinearLayout>
    

    article_item.xml文件作为每一项的布局,这里使用cardView作为跟布局,文件内容和布局效果如下:

    布局效果
    <?xml version="1.0" encoding="utf-8"?>
    <android.support.v7.widget.CardView
        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="wrap_content"
        android:layout_margin="@dimen/card_view_margin"
        app:cardBackgroundColor="@color/card_view_bg_color"
        app:cardCornerRadius="@dimen/card_view_corner_radius"
        app:cardElevation="@dimen/card_view_elevation"
        >
    
        <android.support.constraint.ConstraintLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_margin="@dimen/article_item_constrain_margin">
    
            <android.support.constraint.Guideline
                android:id="@+id/guideline"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:orientation="vertical"
                app:layout_constraintGuide_percent="0.79"/>
    
            <TextView
                android:id="@+id/article_title"
                style="@style/article_card_style"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                app:layout_constraintEnd_toStartOf="@id/guideline"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toTopOf="parent"
                tools:text="title"
                />
    
    
            <TextView
                android:id="@+id/article_author"
                style="@style/article_card_style"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_marginTop="@dimen/article_item_text_margin_top"
                app:layout_constraintBottom_toTopOf="@id/article_classify"
                app:layout_constraintEnd_toStartOf="@id/guideline"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toBottomOf="@id/article_title"
                tools:text="tom"
                />
    
            <TextView
                android:id="@+id/article_classify"
                style="@style/article_card_style"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_marginTop="@dimen/article_item_text_margin_top"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintEnd_toStartOf="@id/guideline"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toBottomOf="@id/article_author"
                tools:text="分类"
                />
    
    
            <TextView
                android:id="@+id/article_time"
                style="@style/article_card_style"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:gravity="end"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintEnd_toEndOf="parent"
                tools:text="2010-00-00"
                />
    
    
            <ImageView
                android:id="@+id/article_collection"
                android:layout_width="@dimen/article_item_collection"
                android:layout_height="@dimen/article_item_collection"
                android:src="@drawable/article_collect_selector"
                app:layout_constraintBottom_toTopOf="@id/article_time"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintTop_toTopOf="parent"
                tools:ignore="contentDescription"
                />
    
    
        </android.support.constraint.ConstraintLayout>
    
    
    </android.support.v7.widget.CardView>
    
    1. 使用tools命名空间,用来展示布局效果,并且编译的时候不会编译进去,包括去掉一些警告,如ImageView的contentDescription属性。
    2. 使用Guideline,用来保证textView不会因为内容过长而遮挡其它控件的显示。

    2.准备adapter

    根据玩Android的开放API接口,我们知道首页数据的格式

    https://www.wanandroid.com/article/list/0/json

    方法:GET
    参数:页码,拼接在连接中,从0开始。
    点击查看示例:https://www.wanandroid.com/article/list/1/json。

    可以直接复制示例内容后通过GsonFormat自动生成,具体的可以查看我的上一篇文章。生成的类名为MainArticleBean.java,记得加上toString方法,方便自己调试。

    ArticleAdapter.java

    package com.tom.wanandroid.adapter;
    
    import android.content.Context;
    import android.support.annotation.NonNull;
    import android.support.v7.widget.RecyclerView;
    import android.text.Html;
    import android.view.LayoutInflater;
    import android.view.View;
    import android.view.ViewGroup;
    import android.widget.ImageView;
    import android.widget.TextView;
    
    import com.tom.wanandroid.R;
    import com.tom.wanandroid.bean.MainArticleBean;
    
    import java.util.List;
    
    import butterknife.BindView;
    import butterknife.ButterKnife;
    
    /**
     * <p>Title: ArticleAdapter</p>
     * <p>Description: 文章适配器</p>
     *
     * @author tom
     * @date 2019/3/9 10:15
     **/
    public class ArticleAdapter extends RecyclerView.Adapter<ArticleAdapter.ArticleHolder> {
    
        private Context mContext;
        private List<MainArticleBean.DataBean.DatasBean> mBeans;
    
        public void setBeans(MainArticleBean beans) {
            mBeans = beans.getData().getDatas();
        }
    
        public ArticleAdapter(RecyclerView recyclerView) {
            mContext = recyclerView.getContext();
        }
    
        @NonNull
        @Override
        public ArticleHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
    
            View view = LayoutInflater.from(mContext).inflate(R.layout.article_item, parent, false);
            return new ArticleHolder(view);
        }
    
        @Override
        public void onBindViewHolder(@NonNull ArticleHolder articleHolder, int i) {
    
            if (mBeans != null) {
                MainArticleBean.DataBean.DatasBean bean = mBeans.get(i);
                articleHolder.mArticleTitle.setText(Html.fromHtml(bean.getTitle(), Html.FROM_HTML_MODE_COMPACT));
    
                articleHolder.mArticleAuthor.setText(String.format(mContext.getResources().getString(R.string.article_author),bean.getAuthor()));
                articleHolder.mArticleTime.setText(bean.getNiceDate());
                String category = String.format(mContext.getResources().getString(R.string.article_category),
                        bean.getSuperChapterName(), bean.getChapterName());
                articleHolder.mArticleClassify.setText(Html.fromHtml(category, Html.FROM_HTML_MODE_COMPACT));
            }
        }
    
        @Override
        public int getItemCount() {
    
    
            return mBeans != null ? mBeans.size() : 0;
        }
    
        class ArticleHolder extends RecyclerView.ViewHolder {
    
    
            @BindView(R.id.article_title)
            TextView mArticleTitle;
            @BindView(R.id.article_author)
            TextView mArticleAuthor;
            @BindView(R.id.article_classify)
            TextView mArticleClassify;
            @BindView(R.id.article_collection)
            ImageView mArticleCollection;
            @BindView(R.id.article_time)
            TextView mArticleTime;
    
            ArticleHolder(@NonNull View itemView) {
                super(itemView);
                ButterKnife.bind(this, itemView);
            }
        }
    
    }
    
    

    这里有一点需要说明,因为获取的数据为网络数据,且部分数据会带有html中的标签并且使用unicode编码,如果不处理的话会直接显示出unicode码,不好看。这里使用的是Html.fromHtml方法,里面的参数官网上面有说明。

    3.在接口中增加相关的内容

    分别增加MVP层的接口,比较简单,直接给出代码Contract.java

    package com.tom.wanandroid.contract;
    
    import com.tom.wanandroid.bean.BannerBean;
    import com.tom.wanandroid.bean.MainArticleBean;
    
    import io.reactivex.Observable;
    
    /**
     * <p>Title: Contract</p>
     * <p>Description: </p>
     *
     * @author tom
     * @date 2019/3/7 10:13
     **/
    public class Contract {
    
        public interface IMainModel {
    
            /**
             * <p>获取banner数据</p>
             * @return banner数据
             */
            Observable<BannerBean> loadBanner();
    
            /**
             * <p>获取首页文章</p>
             * @param number 页码
             * @return 首页文章
             */
            Observable<MainArticleBean> loadArticle(int number);
    
    
        }
    
        public interface IMainView {
            /**
             * <p>View 获取到数据后进行显示</p>
             * @param bean banner的数据
             */
            void loadBanner(BannerBean bean);
    
    
            /**
             * <p>展示首页文章信息</p>
             * @param bean 首页文章数据
             */
            void loadArticle(MainArticleBean bean);
    
        }
    
        public interface IMainPresenter{
    
            /**
             * <p>首页banner</p>
             */
            void loadBanner();
    
    
            /**
             * <p>加载首页文章</p>
             */
            void loadArticle();
    
        }
    }
    
    

    因为使用retorfit来获取网络数据,首先还是要增加接口。

    IRetrofitData.java这里需要传入页码作为参数。

        /**
         * <p>获取首页文章数据</p>
         * @param number 页码
         * @return 返回首页文章
         */
        @GET("article/list/{number}/json")
        Observable<MainArticleBean> loadMainArticle(@Path("number") int number);
    

    4.实现接口

    接口加完以后,就来处理报错吧,哪里报错改哪里,so easy!

    MainModel.java和之前获取首页数据一样的套路。

        @Override
        public Observable<MainArticleBean> loadArticle(int number) {
            Retrofit retrofit = new Retrofit.Builder().baseUrl(BASE_URL)
                    .addConverterFactory(GsonConverterFactory.create())
                    .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                    .build();
            IRetrofitData retrofitData = retrofit.create(IRetrofitData.class);
    
    
            return retrofitData.loadMainArticle(number);
        }
    

    MainPresenter.java增加首页文章的相关内容,这里固定获取第一页的数据(传入参数为0)。

        @Override
        public void loadArticle() {
    
            mModel.loadArticle(0)
                    .subscribeOn(Schedulers.io())
                    .observeOn(AndroidSchedulers.mainThread())
                    .subscribe(new Observer<MainArticleBean>() {
                        @Override
                        public void onSubscribe(Disposable d) {
                            mCompositeDisposable.add(d);
                        }
    
                        @Override
                        public void onNext(MainArticleBean bean) {
                            if (isViewAttached()) {
                                getView().loadArticle(bean);
                            }
                        }
    
                        @Override
                        public void onError(Throwable e) {
                            e.printStackTrace();
                        }
    
                        @Override
                        public void onComplete() {
    
                        }
                    });
    
        }
    

    MainActivity.java,这里有一点小修改,将之前初始化数据的方法由init中放到了onresume中。

        @Override
        protected void onResume() {
            super.onResume();
            mPresenter.loadBanner();
            mPresenter.loadArticle();
        }
    
        @Override
        public void loadArticle(MainArticleBean bean) {
            mArticleContent.setLayoutManager(new LinearLayoutManager(this));
            if (bean.getErrorCode() == 0) {
    
                ArticleAdapter adapter = new ArticleAdapter(mArticleContent);
                adapter.setBeans(bean);
                mArticleContent.setAdapter(adapter);
    
            }
    
        }
    

    到这里为止,关键内容基本都OK了,程序跑起来就可以看到前面的效果图了。

    这里给出用到的资源文件

    colors.xml

    <?xml version="1.0" encoding="utf-8"?>
    <resources>
        <color name="colorPrimary">#008577</color>
        <color name="colorPrimaryDark">#00574B</color>
        <color name="colorAccent">#D81B60</color>
        <color name="card_view_bg_color">#F0F0F0</color>
    </resources>
    
    

    dimens.xml

    <?xml version="1.0" encoding="utf-8"?>
    <resources>
        <dimen name="banner_height">200dp</dimen>
        <dimen name="card_view_corner_radius">12dp</dimen>
        <dimen name="card_view_margin">15dp</dimen>
        <dimen name="card_view_elevation">10dp</dimen>
        <dimen name="article_item_constrain_margin">10dp</dimen>
        <dimen name="article_item_text_margin_top">15dp</dimen>
        <dimen name="article_item_collection">30dp</dimen>
    </resources>
    

    strings.xml中文和英文是两个夹子的,这里写在一起了。

    <!-- 英文-->
    <resources>
        <string name="app_name">WanAndroid</string>
    
        <string name="article_author">author:%1$s</string>
        <string name="article_category">category:%1$s/%2$s</string>
    </resources>
    
    
    <!-- 中文-->
    <?xml version="1.0" encoding="utf-8"?>
    <resources>
        <string name="app_name">玩Android</string>
        <string name="article_author">作者:%1$s</string>
        <string name="article_category">分类:%1$s/%2$s</string>
    </resources>
    
    

    用到的图标:

    collect_disable.png collect_enable.png

    相关文章

      网友评论

        本文标题:WanAndroid实战——首页文章

        本文链接:https://www.haomeiwen.com/subject/rffqmqtx.html