封装网络基本框架—练手项目天气


搭建基本的架构

使用开源库:RxJava、Retrofit、OKHttp:

首先集成开源库:

    compile 'com.squareup.retrofit2:retrofit:+'
    // retrofit
    compile 'com.google.code.gson:gson:+'
    // gson
    compile 'com.squareup.retrofit2:converter-gson:+'
    compile 'com.squareup.retrofit2:adapter-rxjava:+'
    compile 'com.squareup.okhttp3:logging-interceptor:+'
    compile 'com.squareup.okhttp3:okhttp:+'

然后开始封装方法,第一个就是把okhttp和Retrofit以及rxjava封装成一个简单的api:

public class HttpRequest {
​
   public APIservice apIservice;
​
    private volatile static HttpRequest sHttpRequest;
​
    public static HttpRequest getInstance() {
        if (sHttpRequest == null) {
            synchronized (HttpRequest.class) {
                if (sHttpRequest == null) {
                    sHttpRequest = new HttpRequest();
                }
            }
        }
        return sHttpRequest;
    }
​
    private HttpRequest() {
        HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
        interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
 //okhttp集成httplogging,retrofit集成okhttp和gson还有rxjava
        OkHttpClient okHttpClient = new OkHttpClient().newBuilder()
                // 拦截 信息 并打印出来
                .addInterceptor(interceptor)
                .build();
//注意这里不单单要集成gson还要集成rxjaava
        Retrofit retrofit = new Retrofit.Builder().baseUrl("https://free-api.heweather.com/")
                .addConverterFactory(GsonConverterFactory.create()).client(okHttpClient)
                .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                .build();
        apIservice = retrofit.create(APIservice.class);
    }
​
    public Observable<DesBean> getNowWeather(String location){
        return apIservice.getNowWeather(location,"0ea5fef053144b70a3abdc6391ecc028");
    }
​
    public Observable<ForeastBean> getForecast(String location){
        return apIservice.getForecast(location,"0ea5fef053144b70a3abdc6391ecc028");
    }
​
    public Observable<SuggestionEntity> getSuggestion(String location){
        return apIservice.getSuggestion(location,"0ea5fef053144b70a3abdc6391ecc028");
    }
}

具体每一个请求:

public interface APIservice {
​
    @GET("/v5/now")
    Observable<DesBean> getNowWeather(
            @Query("city") String city,
            @Query("key") String key
    );
​
    @GET("/v5/forecast")
    Observable<ForeastBean> getForecast(
            @Query("city") String city,
            @Query("key") String key
    );
​
​
    @GET("/v5/suggestion")
    Observable<SuggestionEntity> getSuggestion(
            @Query("city") String city,
            @Query("key") String key
    );
}

由于都是网络请求,所以封装rxjava的请求:

public class ObservableDecorator {
    public static <T> Observable<T> decorate(Observable<T> observable) {
​
        Observable<T> tObservable = observable.subscribeOn(Schedulers.io())
                .observeOn(Schedulers.immediate());
        return tObservable;
    }
}

具体使用:

        ObservableDecorator.decorate(HttpRequest.getInstance().getSuggestion(location)).subscribe(new Observer<SuggestionEntity>() {
            @Override
            public void onCompleted() {
​
            }
​
            @Override
            public void onError(Throwable e) {
​
            }
​
            @Override
            public void onNext(SuggestionEntity suggestionBean) {
                mView.successView(suggestionBean);
            }
        });

MVP的框架搭建

每一个模块的类结构:一个ui(fragemnt或者activity)一个总的协议类,一个具体的视图控制类

DesFragemnt功能很简单就是从网络请求数据并且显示到一个recyclerview中,协议类:

public interface DesContract {
​
    interface DesPresenter{
        void loadData(String location);
    }
​
    interface View<T>{
        void successView(T t);
        void setPresenter(DesPresenter presenter);
    }
}

基本的数据流程图(图形不是特别的严谨):

就是当fragment的onCreatView方法调用的时候,fragment开始加载数据,但是具体干活的不是fragment而是presenter,在传入参数的时候不仅仅传入的是地址,也包含自己(实现了Contract.view)所以当presenter完成数据加载之后能够直接调用fragemnt中更新视图的方法successView,从而完成视图的显示

public class DesFragment extends BaseFragment<DesEvent> implements DesContract.View<DesBean> {
//实现了DesContract.View
    @BindView(R.id.rv_des)
    RecyclerView mRvDes;
​
    private DesPresenter mDesPresenter;
​
    public static BaseFragment newInstance(){
        return new DesFragment();
    }
​
    @Override
    protected View initView(LayoutInflater inflater, ViewGroup container) {
        View view = inflater.inflate(R.layout.pager_des, null);
        ButterKnife.bind(this, view);
        return view;
    }
​
    /**
     * 真正的调用方法去加载网络的数据
     */
    @Override
    protected void initData() {
        mDesPresenter.loadData("北京");
    }
    @Override
    public void successView(DesBean desBean) {
 //根据数据更新视图
    }
​
    @Override
    public void setPresenter(DesContract.DesPresenter presenter) {
        mDesPresenter = (DesPresenter) presenter;
    }

presenter类:

public class DesPresenter implements DesContract.DesPresenter {
    private final DesContract.View mView;
​
    public DesPresenter(DesContract.View view) {
        mView = view;
        mView.setPresenter(this);
    }
//真正放完数据
    @Override
    public void loadData(String location) {
        ObservableDecorator.decorate(HttpRequest.getInstance().getNowWeather(location)).subscribe(new Observer<DesBean>() {
            @Override
            public void onCompleted() {
​
            }
​
            @Override
            public void onError(Throwable e) {
​
            }
​
            @Override
            public void onNext(DesBean desBean) {
            //加载数据成功,返回数据
            //调用fragemnt中搞的success方法
                mView.successView(desBean);
            }
        });
    }
}

控制器和view的实现类(fragment)都各自含有对方的实例对象

滑动指示器

使用android自己的appbarlayout和viewpager:

首先定义布局:

    <android.support.design.widget.TabLayout
        android:id="@+id/tabLayout"
        android:layout_width="match_parent"
        android:layout_height="56dp"
        app:tabIndicatorHeight="5dp"
        app:tabMode="scrollable"
        app:tabTextColor="@color/colorAccent"/>
​
    <android.support.v4.view.ViewPager
        android:id="@+id/viewpager"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior"
        />

注意viewpager一定要指定属性app:layout_behavior="@string/appbar_scrolling_view_behavior"

然后给viewpager设置adapter,并且给tablayout设置tab可以完成上面的布局:

        MainPagerAdapter pagerAdapter = new MainPagerAdapter(getSupportFragmentManager(), this);
        pagerAdapter.addItem(DesFragment.newInstance());
        pagerAdapter.addItem(SuggestionFragment.newInstance());
        pagerAdapter.addItem(SuggestionFragment.newInstance());
        mViewpager.setAdapter(pagerAdapter);
​
        //setupWithViewPager要在每一个tab之前调用否则不显示图形
        mTabLayout.setupWithViewPager(mViewpager);
//setcustomView中的参数就是一个view的实例
        mTabLayout.getTabAt(0).setCustomView(pagerAdapter.getTabView(0, mTabLayout));
        mTabLayout.getTabAt(1).setCustomView(pagerAdapter.getTabView(1, mTabLayout));
        mTabLayout.getTabAt(2).setCustomView(pagerAdapter.getTabView(2, mTabLayout));

沉浸式状态栏

实现效果如上图:

在res文件夹下新建两个文件夹v19和v21也就是4.4和5.0之后的样式

在每一个文件夹中新建style.xml:style.xml(19):

<resources>
​
    <style name="TranslucentTheme" parent="Theme.AppCompat.Light.NoActionBar">
        <item name="android:windowTranslucentStatus">true</item>
        <item name="android:windowTranslucentNavigation">true</item>
    </style></resources>

style.xml(21):

<resources>
​
    <style name="TranslucentTheme" parent="Theme.AppCompat.Light.NoActionBar">
        <item name="android:windowTranslucentStatus">false</item>
        <item name="android:windowTranslucentNavigation">true</item>
        <!--Android 5.x开始需要把颜色设置透明,否则导航栏会呈现系统默认的浅灰色-->
        <item name="android:statusBarColor">@android:color/transparent</item>
    </style>
</resources>

注意,还要在设置背景图片的View或者ViewGroup中设置android:fitsSystemWindows="true"

最后指定主题就可以设置成沉浸式状态栏

如果含有背景图片的View不是顶层的View那么他的所有父布局都要指定android:fitsSystemWindows="true"属性

手势滑View变化

实现如图中,温度的变化,也就是在滑动的时候,大小和透明度发生变化:

        mLinearLayout.setAlpha(1 - titlePercentage);
        mLinearLayout.setScaleX(1 - titlePercentage);
        mLinearLayout.setScaleY(1 - titlePercentage);

把上面的代码添加进入滑动监听的方法中即可


It's time for you to begin thinking out of the box