WanAndroid实战——首页Banner

作者: Tom_Ji | 来源:发表于2019-03-07 14:55 被阅读19次

    写在前面的话

    之前一直在学习任玉刚老师的《Android开发艺术探索》,并且也有通过笔记的形式记录下自己认为符合自己需要的内容,一是加深记忆,二是方便以后查找复习。目前的工作内容主要是android原生应用的定制修改,基本上涉及不到使用网络的情况,但是现在的第三方app基本上无一例外的都需要使用网络,因此这方面的知识就需要弥补上来。这里要感谢我的同事王远志的帮助,大家也可以去看他的主页milovetingting,有问题也可以给他留言。

    准备工作

    项目使用的API由玩Android 开放API提供。创建项目,首先将需要用到的开源项目导入。在dependencies里面加入如下内容。

        //butterknife
        implementation 'com.jakewharton:butterknife:8.8.1'
        annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1'
        //banner
        implementation 'com.youth.banner:banner:1.4.10'
        //retrofit
        implementation 'com.squareup.retrofit2:retrofit:2.5.0'
        implementation 'com.squareup.retrofit2:converter-gson:2.5.0'
        implementation 'com.squareup.retrofit2:adapter-rxjava2:2.5.0'
        //rx
        implementation 'io.reactivex.rxjava2:rxjava:2.2.7'
        implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'
        //glide
        implementation 'com.github.bumptech.glide:glide:4.9.0'
        annotationProcessor 'com.github.bumptech.glide:compiler:4.9.0'
    

    这里指定使用java 8来进行编译,方便使用lambda表达式,在android里增加如下内容

    
    compileOptions {
            sourceCompatibility JavaVersion.VERSION_1_8
            targetCompatibility JavaVersion.VERSION_1_8
        }
    

    搭建MVP框架

    目前主流的app开发基本都是使用MVP模式来进行的,因此也去学习了一下MVP的相关知识,这里就直接使用。对于MVP模式的理解,可以这样来看,我们部门包括部长,组长,我,现在有一个开发任务,部长告诉组长,组长安排我来去完成,我完成开发任务之后告诉组长,组长再告诉部长,部长就可以说任务完成了。这里的我相当于M层(真正做事情的,获取数据等耗时操作),组长相当于P层(用来传递消息,在V和M之间的桥梁),部长相当于V层(用来展示数据),这样便于理解,现实中也是如此,尽量不要越级沟通。

    1.创建base文件夹,里面为基类BasePresenter和BaseActivity

    BasePresenter.java

    
    package com.tom.wanandroid.base;
    
    import java.lang.ref.WeakReference;
    
    import io.reactivex.disposables.CompositeDisposable;
    
    /**
     * <p>Title: BasePresenter</p>
     * <p>Description: </p>
     *
     * @author tom
     * @date 2019/3/7 09:59
     **/
    public abstract class BasePresenter<V> {
        WeakReference<V> mViewRef;
        //防止内存泄漏
        protected CompositeDisposable mCompositeDisposable = new CompositeDisposable();
    
        void attachView(V view) {
            mViewRef = new WeakReference<V>(view);
        }
    
        protected V getView() {
            return mViewRef == null ? null : mViewRef.get();
        }
    
        protected boolean isViewAttached() {
            return mViewRef != null && mViewRef.get() != null;
        }
    
        void detachView() {
            if (mViewRef != null) {
                mViewRef.clear();
                mViewRef = null;
            }
            mCompositeDisposable.clear();
        }
    
    
    }
    
    

    BaseActivity.java

    
    package com.tom.wanandroid.base;
    
    import android.os.Bundle;
    import android.support.annotation.Nullable;
    import android.support.v7.app.AppCompatActivity;
    
    import butterknife.ButterKnife;
    import butterknife.Unbinder;
    
    /**
     * <p>Title: BaseActivity</p>
     * <p>Description: </p>
     *
     * @author tom
     * @date 2019/3/7 10:04
     **/
    public abstract class BaseActivity<V, P extends BasePresenter<V>> extends AppCompatActivity {
        protected P mPresenter;
    
        protected Unbinder unbinder;
    
        /**
         * 获取布局id
         *
         * @return 布局id
         */
        protected abstract int getContentViewId();
    
        /**
         * 初始化
         *
         * @param savedInstanceState bundle
         */
        protected abstract void init(Bundle savedInstanceState);
    
        /**
         * 创建Presenter
         *
         * @return p
         */
        protected abstract P createPresenter();
    
    
        @Override
        protected void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
    
            setContentView(getContentViewId());
    
            unbinder = ButterKnife.bind(this);
    
            mPresenter = createPresenter();
    
            mPresenter.attachView((V) this);
    
            init(savedInstanceState);
        }
    
        @Override
        public void onDestroy() {
            super.onDestroy();
            unbinder.unbind();
            mPresenter.detachView();
        }
    }
    
    

    这两个基类基本在每次使用的时候就可以直接复制粘贴了,避免每次都要重写。

    2.创建Contract.java类,在contract包下,这个类里面主要定义接口,接口里面需要包含哪些方法时根据具体的根据业务来定义的,基本框架如下。

    Contract.java

    
    package com.tom.wanandroid.contract;
    
    /**
     * <p>Title: Contract</p>
     * <p>Description: </p>
     *
     * @author tom
     * @date 2019/3/7 10:13
     **/
    public class Contract {
        
        public interface IMainModel{
            
            
            
        }
    
        public interface IMainView{
    
        }
    
        public interface IMainPresenter{
    
        }
    }
    
    

    3.因为我们要获取到banner图的数据,所以需要有一个bean,数据根据API返回的数据来定义,API返回的是一个json串,因此我们可以创建一个对应的bean,也可以使用插件,我这里使用了GsonFormat插件。网页介绍如下图所示


    网页介绍

    可以使用http://www.wanandroid.com/banner/json查看示例。

    通过GsonFormat插件生成的类没有toString方法,这里把toString方法补上,方便调试。

    BannerBean.java

    
    package com.tom.wanandroid.bean;
    
    import java.util.List;
    
    /**
     * <p>Title: BannerBean</p>
     * <p>Description: </p>
     *
     * @author tom
     * @date 2019/3/7 10:28
     **/
    public class BannerBean {
    
        /**
         * data : [{"desc":"一起来做个App吧","id":10,"imagePath":"http://www.wanandroid
         * .com/blogimgs/50c115c2-cf6c-4802-aa7b-a4334de444cd.png","isVisible":1,"order":3,"title":"一起来做个App吧","type":0,
         * "url":"http://www.wanandroid.com/blog/show/2"},{"desc":"","id":4,"imagePath":"http://www.wanandroid
         * .com/blogimgs/ab17e8f9-6b79-450b-8079-0f2287eb6f0f.png","isVisible":1,"order":0,"title":"看看别人的面经,搞定面试~",
         * "type":1,"url":"http://www.wanandroid.com/article/list/0?cid=73"},{"desc":"","id":3,"imagePath":"http://www
         * .wanandroid.com/blogimgs/fb0ea461-e00a-482b-814f-4faca5761427.png","isVisible":1,"order":1,
         * "title":"兄弟,要不要挑个项目学习下?","type":1,"url":"http://www.wanandroid.com/project"},{"desc":"","id":6,
         * "imagePath":"http://www.wanandroid.com/blogimgs/62c1bd68-b5f3-4a3c-a649-7ca8c7dfabe6.png","isVisible":1,
         * "order":1,"title":"我们新增了一个常用导航Tab~","type":1,"url":"http://www.wanandroid.com/navi"},{"desc":"","id":18,
         * "imagePath":"http://www.wanandroid.com/blogimgs/00f83f1d-3c50-439f-b705-54a49fc3d90d.jpg","isVisible":1,
         * "order":1,"title":"公众号文章列表强势上线","type":1,"url":"http://www.wanandroid.com/wxarticle/list/408/1"},{"desc":"",
         * "id":2,"imagePath":"http://www.wanandroid.com/blogimgs/90cf8c40-9489-4f9d-8936-02c9ebae31f0.png",
         * "isVisible":1,"order":2,"title":"JSON工具","type":1,"url":"http://www.wanandroid.com/tools/bejson"},{"desc":"",
         * "id":5,"imagePath":"http://www.wanandroid.com/blogimgs/acc23063-1884-4925-bdf8-0b0364a7243e.png",
         * "isVisible":1,"order":3,"title":"微信文章合集","type":1,"url":"http://www.wanandroid.com/blog/show/6"}]
         * errorCode : 0
         * errorMsg :
         */
    
        private int errorCode;
        private String errorMsg;
        private List<DataBean> data;
    
        public int getErrorCode() {
            return errorCode;
        }
    
        public void setErrorCode(int errorCode) {
            this.errorCode = errorCode;
        }
    
        public String getErrorMsg() {
            return errorMsg;
        }
    
        public void setErrorMsg(String errorMsg) {
            this.errorMsg = errorMsg;
        }
    
        public List<DataBean> getData() {
            return data;
        }
    
        public void setData(List<DataBean> data) {
            this.data = data;
        }
    
        public static class DataBean {
            /**
             * desc : 一起来做个App吧
             * id : 10
             * imagePath : http://www.wanandroid.com/blogimgs/50c115c2-cf6c-4802-aa7b-a4334de444cd.png
             * isVisible : 1
             * order : 3
             * title : 一起来做个App吧
             * type : 0
             * url : http://www.wanandroid.com/blog/show/2
             */
    
            private String desc;
            private int id;
            private String imagePath;
            private int isVisible;
            private int order;
            private String title;
            private int type;
            private String url;
    
            public String getDesc() {
                return desc;
            }
    
            public void setDesc(String desc) {
                this.desc = desc;
            }
    
            public int getId() {
                return id;
            }
    
            public void setId(int id) {
                this.id = id;
            }
    
            public String getImagePath() {
                return imagePath;
            }
    
            public void setImagePath(String imagePath) {
                this.imagePath = imagePath;
            }
    
            public int getIsVisible() {
                return isVisible;
            }
    
            public void setIsVisible(int isVisible) {
                this.isVisible = isVisible;
            }
    
            public int getOrder() {
                return order;
            }
    
            public void setOrder(int order) {
                this.order = order;
            }
    
            public String getTitle() {
                return title;
            }
    
            public void setTitle(String title) {
                this.title = title;
            }
    
            public int getType() {
                return type;
            }
    
            public void setType(int type) {
                this.type = type;
            }
    
            public String getUrl() {
                return url;
            }
    
            public void setUrl(String url) {
                this.url = url;
            }
    
            @Override
            public String toString() {
                return "DataBean{" +
                        "desc='" + desc + '\'' +
                        ", id=" + id +
                        ", imagePath='" + imagePath + '\'' +
                        ", isVisible=" + isVisible +
                        ", order=" + order +
                        ", title='" + title + '\'' +
                        ", type=" + type +
                        ", url='" + url + '\'' +
                        '}';
            }
        }
    
        @Override
        public String toString() {
            return "BannerBean{" +
                    "errorCode=" + errorCode +
                    ", errorMsg='" + errorMsg + '\'' +
                    ", data=" + data +
                    '}';
        }
    }
    
    

    4.填充Contract.java类里面的接口

    根据实际的业务,这里要获取banner的数据,因此增加如下内容。

    Contract.java

    
    package com.tom.wanandroid.contract;
    
    import com.tom.wanandroid.bean.BannerBean;
    
    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();
    
    
        }
    
        public interface IMainView {
            /**
             * <p>View 获取到数据后进行显示</p>
             * @param bean banner的数据
             */
            void loadBanner(BannerBean bean);
    
        }
    
        public interface IMainPresenter{
    
            /**
             * <p>P层接口</p>
             */
            void loadBanner();
    
        }
    }
    
    

    5.接口已经定义好了,接下来就是分别创建MVP对应的类,来实现这些接口。先创建Model层,这里创建MainModel类,在包model下,实现IMainModel接口,主要作用是来获取数据,真正干活的地方。

    MainModel.java

    
    package com.tom.wanandroid.model;
    
    import com.tom.wanandroid.bean.BannerBean;
    import com.tom.wanandroid.contract.Contract;
    
    import io.reactivex.Observable;
    
    /**
     * <p>Title: MainModel</p>
     * <p>Description: </p>
     *
     * @author tom
     * @date 2019/3/7 10:54
     **/
    public class MainModel implements Contract.IMainModel {
        @Override
        public Observable<BannerBean> loadBanner() {
    
            return null;
        }
    
    }
    
    

    接着创建MainPresenter.java,继承BasePresenter,实现IMainPresenter接口,用来传话的地方。

    MainPresenter.java

    
    package com.tom.wanandroid.presenter;
    
    import com.tom.wanandroid.base.BasePresenter;
    import com.tom.wanandroid.contract.Contract;
    
    /**
     * <p>Title: MainPresenter</p>
     * <p>Description: </p>
     *
     * @author tom
     * @date 2019/3/7 10:59
     **/
    public class MainPresenter extends BasePresenter<Contract.IMainView> implements Contract.IMainPresenter {
    
        @Override
        public void loadBanner() {
    
        }
    }
    
    

    6.数据获取,获取banner数据是从服务器上获取的,这里使用retrofit开源框架来获取,retrofit的使用需要自己学习。因为网站开放API介绍的是使用get的无参方法,因此创建retrofit接口,用于使用该框架。

    IRetrofitData.java

    
    package com.tom.wanandroid.retrofit;
    
    import com.tom.wanandroid.bean.BannerBean;
    
    import io.reactivex.Observable;
    import retrofit2.http.GET;
    
    /**
     * <p>Title: IRetrofitData</p>
     * <p>Description: </p>
     *
     * @author tom
     * @date 2019/3/7 11:34
     **/
    public interface IRetrofitData {
        @GET("banner/json")
        Observable<BannerBean> loadBanner();
    }
    
    

    7.填充M层的方法,获取banner数据。

    MainModel.java

    
    package com.tom.wanandroid.model;
    
    import com.tom.wanandroid.bean.BannerBean;
    import com.tom.wanandroid.contract.Contract;
    import com.tom.wanandroid.retrofit.IRetrofitData;
    
    import io.reactivex.Observable;
    import retrofit2.Retrofit;
    import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
    import retrofit2.converter.gson.GsonConverterFactory;
    
    /**
     * <p>Title: MainModel</p>
     * <p>Description: </p>
     *
     * @author tom
     * @date 2019/3/7 10:54
     **/
    public class MainModel implements Contract.IMainModel {
    
        private static final String BASE_URL = "http://www.wanandroid.com";
    
        @Override
        public Observable<BannerBean> loadBanner() {
            Retrofit retrofit = new Retrofit.Builder().baseUrl(BASE_URL)
                    .addConverterFactory(GsonConverterFactory.create())
                    .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                    .build();
            IRetrofitData retrofitData = retrofit.create(IRetrofitData.class);
    
            return retrofitData.loadBanner();
        }
    
    }
    
    

    8.填充P层方法,将获取到的数据设置给V层,让View去显示数据。

    MainPresenter.java

    
    package com.tom.wanandroid.presenter;
    
    import com.tom.wanandroid.base.BasePresenter;
    import com.tom.wanandroid.bean.BannerBean;
    import com.tom.wanandroid.contract.Contract;
    import com.tom.wanandroid.model.MainModel;
    
    import io.reactivex.Observer;
    import io.reactivex.android.schedulers.AndroidSchedulers;
    import io.reactivex.disposables.Disposable;
    import io.reactivex.schedulers.Schedulers;
    
    /**
     * <p>Title: MainPresenter</p>
     * <p>Description: </p>
     *
     * @author tom
     * @date 2019/3/7 10:59
     **/
    public class MainPresenter extends BasePresenter<Contract.IMainView> implements Contract.IMainPresenter {
    
        Contract.IMainModel mModel;
    
        public MainPresenter() {
            mModel = new MainModel();
        }
    
        @Override
        public void loadBanner() {
    
            mModel.loadBanner()
                    .subscribeOn(Schedulers.io())
                    .observeOn(AndroidSchedulers.mainThread())
                    .subscribe(new Observer<BannerBean>() {
                        @Override
                        public void onSubscribe(Disposable d) {
                            mCompositeDisposable.add(d);
                        }
    
                        @Override
                        public void onNext(BannerBean bean) {
    
                            if (isViewAttached()) {
                                getView().loadBanner(bean);
                            }
                        }
    
                        @Override
                        public void onError(Throwable e) {
    
                            e.printStackTrace();
                        }
    
                        @Override
                        public void onComplete() {
    
                        }
                    });
        }
    }
    
    

    9.数据已经准备好并且传递给了View层,接下来就需要在View来展示数据,新建用来展示数据的Activity,继承BaseActivity,实现IMainView接口,轮播图的显示使用开源框架Banner,图片使用Glide框架。Banner使用

    这里我把重写的图片加载器放到了utils包里。

    GlideImageLoader.java

    
    package com.tom.wanandroid.utils;
    
    import android.content.Context;
    import android.widget.ImageView;
    
    import com.bumptech.glide.Glide;
    import com.youth.banner.loader.ImageLoader;
    
    /**
     * <p>Title: GlideImageLoader</p>
     * <p>Description: 重写图片加载器 </p>
     *
     * @author tom
     * @date 2019/3/7 14:21
     **/
    public class GlideImageLoader extends ImageLoader {
        @Override
        public void displayImage(Context context, Object path, ImageView imageView) {
    
            Glide.with(context).load(path).into(imageView);
        }
    }
    
    

    activity_main.xml 高度值banner_height我设置了200dp。

    
    <?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"/>
    
    </LinearLayout>
    

    MainActivity.java

    
    package com.tom.wanandroid.view;
    
    import android.os.Bundle;
    
    import com.tom.wanandroid.Constant;
    import com.tom.wanandroid.R;
    import com.tom.wanandroid.base.BaseActivity;
    import com.tom.wanandroid.bean.BannerBean;
    import com.tom.wanandroid.contract.Contract;
    import com.tom.wanandroid.presenter.MainPresenter;
    import com.tom.wanandroid.utils.GlideImageLoader;
    import com.youth.banner.Banner;
    import com.youth.banner.BannerConfig;
    
    import java.util.List;
    import java.util.stream.Collectors;
    
    import butterknife.BindView;
    
    public class MainActivity extends BaseActivity<Contract.IMainView, MainPresenter> implements Contract.IMainView {
    
        @BindView(R.id.main_banner)
        Banner mMainBanner;
    
        @Override
        protected int getContentViewId() {
            return R.layout.activity_main;
        }
    
        @Override
        protected void init(Bundle savedInstanceState) {
    
            mPresenter.loadBanner();
        }
    
        @Override
        protected MainPresenter createPresenter() {
            return new MainPresenter();
        }
    
    
        @Override
        public void loadBanner(BannerBean bean) {
    
    
            mMainBanner.setImageLoader(new GlideImageLoader());
    
            if (bean.getErrorCode() == Constant.BANNER_SUCCESS) {
    
                //设置banner样式
                mMainBanner.setBannerStyle(BannerConfig.CIRCLE_INDICATOR_TITLE_INSIDE);
    
                //获取图片路径
                List<String> images = bean.getData()
                        .stream()
                        .map(BannerBean.DataBean::getImagePath)
                        .collect(Collectors.toList());
    
                mMainBanner.setImages(images);
    
                //获取title
    
                List<String> titles = bean.getData()
                        .stream()
                        .map(BannerBean.DataBean::getTitle)
                        .collect(Collectors.toList());
    
                mMainBanner.setBannerTitles(titles);
    
    
                mMainBanner.start();
            }
    
        }
    
    
    }
    
    

    10.添加权限,添加使用开源库所需要的权限,因为目前只涉及到网络权限,所以只申请网络权限。在AndroidManifest.xml文件中添加。

    <uses-permission android:name="android.permission.INTERNET"/>
    

    至此,基本内容都写完了,跑一下看看,发现提示 "default Activity not found",检查发现AndroidManifest.xml中没有写默认启动的activity,因为目前只有一个Activity,添加如下内容,即可。

                <intent-filter>
                    <action android:name="android.intent.action.MAIN"/>
    
                    <category android:name="android.intent.category.LAUNCHER"/>
                </intent-filter>
    

    在此运行,符合续期效果。运行效果如图:

    banner效果图.gif

    至此,本节内容告一段落,接下来要继续实现后续部分。

    相关文章

      网友评论

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

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