11 | * Created by wangchenlong on 16/1/21. 12 | */ 13 | public class NewsApplication extends Application { 14 | private static NewsApplication sApplication; 15 | 16 | private static AppGraph sAppGraph; 17 | 18 | @Override public void onCreate() { 19 | super.onCreate(); 20 | sApplication = this; 21 | buildComponentAndInject(); 22 | } 23 | 24 | public static AppGraph component() { 25 | return sAppGraph; 26 | } 27 | 28 | // 初始化组件 29 | private static void buildComponentAndInject() { 30 | sAppGraph = AppComponent.Initializer.init(sApplication); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /app/src/main/java/me/chunyu/spike/springrainnews/injectors/components/AppComponent.java: -------------------------------------------------------------------------------- 1 | package me.chunyu.spike.springrainnews.injectors.components; 2 | 3 | import javax.inject.Singleton; 4 | 5 | import dagger.Component; 6 | import me.chunyu.spike.springrainnews.NewsApplication; 7 | import me.chunyu.spike.springrainnews.injectors.modules.AppModule; 8 | import me.chunyu.spike.springrainnews.injectors.modules.AppScope; 9 | 10 | /** 11 | * App的组件 12 | *
13 | * Created by wangchenlong on 16/1/21. 14 | */ 15 | @Singleton 16 | @Component(modules = AppModule.class) 17 | public interface AppComponent extends AppGraph { 18 | final class Initializer { 19 | private Initializer() { 20 | } // No instances. 21 | 22 | // 初始化组件 23 | public static AppComponent init(NewsApplication app) { 24 | return DaggerAppComponent.builder() 25 | .appModule(new AppModule(app)) 26 | .build(); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /app/src/main/java/me/chunyu/spike/springrainnews/injectors/components/AppGraph.java: -------------------------------------------------------------------------------- 1 | package me.chunyu.spike.springrainnews.injectors.components; 2 | 3 | import me.chunyu.spike.springrainnews.injectors.modules.AppScope; 4 | import me.chunyu.spike.springrainnews.uis.activities.MainActivity; 5 | import me.chunyu.spike.springrainnews.uis.adapters.MainListAdapter; 6 | 7 | /** 8 | * App的类图 9 | *
10 | * Created by wangchenlong on 16/1/21. 11 | */ 12 | public interface AppGraph { 13 | void inject(MainActivity mainActivity); // 注入MainActivity 14 | 15 | void inject(MainListAdapter mainListAdapter); // 注入MainListAdapter 16 | } 17 | -------------------------------------------------------------------------------- /app/src/main/java/me/chunyu/spike/springrainnews/injectors/modules/AppModule.java: -------------------------------------------------------------------------------- 1 | package me.chunyu.spike.springrainnews.injectors.modules; 2 | 3 | import android.content.Context; 4 | import android.content.res.Resources; 5 | 6 | import javax.inject.Singleton; 7 | 8 | import dagger.Module; 9 | import dagger.Provides; 10 | import me.chunyu.spike.springrainnews.NewsApplication; 11 | import me.chunyu.spike.springrainnews.mvp.models.Repository; 12 | import me.chunyu.spike.springrainnews.networks.RestDataSource; 13 | 14 | /** 15 | * App的模块 16 | *
17 | * Created by wangchenlong on 16/1/21. 18 | */ 19 | @Module 20 | public class AppModule { 21 | private final NewsApplication mApplication; 22 | 23 | public AppModule(NewsApplication application) { 24 | mApplication = application; 25 | } 26 | 27 | // 注入Application的Context 28 | @Provides @Singleton 29 | protected Context provideApplication() { 30 | return mApplication; 31 | } 32 | 33 | // 注入资源 34 | @Provides @Singleton 35 | protected Resources provideResources() { 36 | return mApplication.getResources(); 37 | } 38 | 39 | // 注入REST数据源 40 | @Provides @Singleton 41 | protected Repository provideDataRepository(RestDataSource restDataSource) { 42 | return restDataSource; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /app/src/main/java/me/chunyu/spike/springrainnews/injectors/modules/AppScope.java: -------------------------------------------------------------------------------- 1 | package me.chunyu.spike.springrainnews.injectors.modules; 2 | 3 | import java.lang.annotation.Documented; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | 7 | import javax.inject.Scope; 8 | 9 | /** 10 | * 主要的域 11 | *
12 | * Created by wangchenlong on 16/7/28. 13 | */ 14 | @Scope 15 | @Documented 16 | @Retention(RetentionPolicy.RUNTIME) 17 | public @interface AppScope { 18 | } 19 | -------------------------------------------------------------------------------- /app/src/main/java/me/chunyu/spike/springrainnews/mvp/models/AvengersCharacter.java: -------------------------------------------------------------------------------- 1 | package me.chunyu.spike.springrainnews.mvp.models; 2 | 3 | /** 4 | * 复仇者成员类 5 | *
6 | * Created by wangchenlong on 16/1/21. 7 | */ 8 | @SuppressWarnings("unused") 9 | public class AvengersCharacter { 10 | 11 | private int id; 12 | private String name; 13 | private String description; 14 | private ThumbnailEntity thumbnail; 15 | 16 | public void setId(int id) { 17 | this.id = id; 18 | } 19 | 20 | public void setName(String name) { 21 | this.name = name; 22 | } 23 | 24 | public void setDescription(String description) { 25 | this.description = description; 26 | } 27 | 28 | public void setThumbnail(ThumbnailEntity thumbnail) { 29 | this.thumbnail = thumbnail; 30 | } 31 | 32 | public int getId() { 33 | return id; 34 | } 35 | 36 | public String getName() { 37 | return name; 38 | } 39 | 40 | public String getDescription() { 41 | return description; 42 | } 43 | 44 | public ThumbnailEntity getThumbnail() { 45 | return thumbnail; 46 | } 47 | 48 | // 返回图片路径 49 | public String getThumbImage() { 50 | return String.format("%s.%s", thumbnail.path, thumbnail.extension); 51 | } 52 | 53 | public static class ThumbnailEntity { 54 | private String path; 55 | private String extension; 56 | 57 | public void setPath(String path) { 58 | this.path = path; 59 | } 60 | 61 | public void setExtension(String extension) { 62 | this.extension = extension; 63 | } 64 | 65 | public String getPath() { 66 | return path; 67 | } 68 | 69 | public String getExtension() { 70 | return extension; 71 | } 72 | } 73 | } 74 | 75 | 76 | -------------------------------------------------------------------------------- /app/src/main/java/me/chunyu/spike/springrainnews/mvp/models/Repository.java: -------------------------------------------------------------------------------- 1 | package me.chunyu.spike.springrainnews.mvp.models; 2 | 3 | import java.util.List; 4 | 5 | import rx.Observable; 6 | 7 | /** 8 | * 请求的库 9 | *
10 | * Created by wangchenlong on 16/1/21.
11 | */
12 | public interface Repository {
13 | Observable
8 | * Created by wangchenlong on 16/1/21.
9 | */
10 | @SuppressWarnings("EmptyMethod")
11 | public interface BasePresenter {
12 | void onCreate();
13 |
14 | void onResume();
15 |
16 | void onStop();
17 |
18 | void onDestroy();
19 |
20 | void attachView(BaseView v);
21 | }
22 |
--------------------------------------------------------------------------------
/app/src/main/java/me/chunyu/spike/springrainnews/mvp/presenters/MainPresenter.java:
--------------------------------------------------------------------------------
1 | package me.chunyu.spike.springrainnews.mvp.presenters;
2 |
3 | import javax.inject.Inject;
4 |
5 | import me.chunyu.spike.springrainnews.mvp.models.Repository;
6 | import me.chunyu.spike.springrainnews.mvp.views.BaseView;
7 | import me.chunyu.spike.springrainnews.mvp.views.MainView;
8 | import rx.Subscription;
9 | import rx.android.schedulers.AndroidSchedulers;
10 | import rx.schedulers.Schedulers;
11 |
12 | /**
13 | * 主页的展示逻辑
14 | *
15 | * Created by wangchenlong on 16/1/21.
16 | */
17 | public class MainPresenter implements BasePresenter {
18 |
19 | private final Repository mRepository; // 网络库
20 |
21 | private MainView mMainView; // 主页
22 |
23 | private Subscription mCharactersSubscription; // 订阅成员
24 |
25 | @Inject
26 | public MainPresenter(Repository repository) {
27 | mRepository = repository;
28 | }
29 |
30 | @Override public void onCreate() {
31 | loadData();
32 | }
33 |
34 | private void loadData() {
35 | mCharactersSubscription = mRepository.getCharacters(0)
36 | .subscribeOn(Schedulers.io())
37 | .observeOn(AndroidSchedulers.mainThread())
38 | .subscribe(avengersCharacters -> {
39 | mMainView.setListData(avengersCharacters);
40 | });
41 | }
42 |
43 | @Override public void onResume() {
44 |
45 | }
46 |
47 | @Override public void onStop() {
48 | mCharactersSubscription.unsubscribe();
49 | }
50 |
51 | @Override public void onDestroy() {
52 |
53 | }
54 |
55 | @Override public void attachView(BaseView v) {
56 | mMainView = (MainView) v; // 绑定视图
57 | }
58 |
59 |
60 |
61 | }
62 |
--------------------------------------------------------------------------------
/app/src/main/java/me/chunyu/spike/springrainnews/mvp/views/BaseView.java:
--------------------------------------------------------------------------------
1 | package me.chunyu.spike.springrainnews.mvp.views;
2 |
3 | /**
4 | * mvp的基本视图
5 | *
6 | * Created by wangchenlong on 16/1/21.
7 | */
8 | public interface BaseView {
9 | }
10 |
--------------------------------------------------------------------------------
/app/src/main/java/me/chunyu/spike/springrainnews/mvp/views/MainView.java:
--------------------------------------------------------------------------------
1 | package me.chunyu.spike.springrainnews.mvp.views;
2 |
3 | import java.util.List;
4 |
5 | import me.chunyu.spike.springrainnews.mvp.models.AvengersCharacter;
6 |
7 | /**
8 | * 主页的Views
9 | *
10 | * Created by wangchenlong on 16/1/21.
11 | */
12 | public interface MainView extends BaseView {
13 | void setListData(List
14 | * Created by wangchenlong on 16/1/21.
15 | */
16 | public class MarvelSigningInterceptor implements Interceptor {
17 | private final String mApiKey;
18 | private final String mApiSecret;
19 |
20 | public MarvelSigningInterceptor(String apiKey, String apiSecret) {
21 | mApiKey = apiKey;
22 | mApiSecret = apiSecret;
23 | }
24 |
25 | @Override public Response intercept(Interceptor.Chain chain) throws IOException {
26 | String marvelHash = MarvelApiUtils.generateMarvelHash(mApiKey, mApiSecret);
27 | Request oldRequest = chain.request();
28 |
29 | // 添加新的参数
30 | HttpUrl.Builder authorizedUrlBuilder = oldRequest.url()
31 | .newBuilder()
32 | .scheme(oldRequest.url().scheme())
33 | .host(oldRequest.url().host())
34 | .addQueryParameter(MarvelService.PARAM_API_KEY, mApiKey)
35 | .addQueryParameter(MarvelService.PARAM_TIMESTAMP, MarvelApiUtils.getUnixTimeStamp())
36 | .addQueryParameter(MarvelService.PARAM_HASH, marvelHash);
37 |
38 | // 新的请求
39 | Request newRequest = oldRequest.newBuilder()
40 | .method(oldRequest.method(), oldRequest.body())
41 | .url(authorizedUrlBuilder.build())
42 | .build();
43 |
44 | return chain.proceed(newRequest);
45 | }
46 | }
47 |
48 |
--------------------------------------------------------------------------------
/app/src/main/java/me/chunyu/spike/springrainnews/networks/services/MarvelService.java:
--------------------------------------------------------------------------------
1 | package me.chunyu.spike.springrainnews.networks.services;
2 |
3 | import java.util.List;
4 |
5 | import me.chunyu.spike.springrainnews.mvp.models.AvengersCharacter;
6 | import retrofit2.http.GET;
7 | import retrofit2.http.Query;
8 | import rx.Observable;
9 |
10 | /**
11 | * 网络请求
12 | *
13 | * Created by wangchenlong on 16/1/21.
14 | */
15 | public interface MarvelService {
16 | String END_POINT = "http://gateway.marvel.com/";
17 | String PARAM_API_KEY = "apikey";
18 | String PARAM_HASH = "hash";
19 | String PARAM_TIMESTAMP = "ts";
20 |
21 | @GET("/v1/public/characters")
22 | Observable
28 | * Created by wangchenlong on 16/1/22.
29 | */
30 | public class MainListAdapter extends RecyclerView.Adapter> getCharacters(int offset);
14 | }
15 |
--------------------------------------------------------------------------------
/app/src/main/java/me/chunyu/spike/springrainnews/mvp/presenters/BasePresenter.java:
--------------------------------------------------------------------------------
1 | package me.chunyu.spike.springrainnews.mvp.presenters;
2 |
3 | import me.chunyu.spike.springrainnews.mvp.views.BaseView;
4 |
5 | /**
6 | * 展示页面
7 | *
>() {
58 | }.getType(),
59 | deserializer)
60 | .create();
61 |
62 | // 添加Host, 添加Gson解析, 添加RxJava适配器, 添加OKHttp.
63 | Retrofit retrofitAdapter = new Retrofit.Builder()
64 | .baseUrl(MarvelService.END_POINT)
65 | .addConverterFactory(GsonConverterFactory.create(customGsonInstance))
66 | .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
67 | .client(client)
68 | .build();
69 |
70 | // mMarvelService是服务器接口, marvelApiAdapter是Retrofit对象
71 | mMarvelService = retrofitAdapter.create(MarvelService.class);
72 | }
73 |
74 | @Override
75 | public Observable
> getCharacters(int currentOffset) {
76 | // 调用接口, 返回Observable信息
77 | return mMarvelService.getCharacters(currentOffset);
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/app/src/main/java/me/chunyu/spike/springrainnews/networks/deserializers/MarvelResultsDeserializer.java:
--------------------------------------------------------------------------------
1 | package me.chunyu.spike.springrainnews.networks.deserializers;
2 |
3 | import com.google.gson.Gson;
4 | import com.google.gson.JsonDeserializationContext;
5 | import com.google.gson.JsonDeserializer;
6 | import com.google.gson.JsonElement;
7 | import com.google.gson.JsonParseException;
8 |
9 | import java.lang.reflect.Type;
10 | import java.util.List;
11 |
12 | public class MarvelResultsDeserializer
> {
13 | @Override
14 | public List
> getCharacters(@Query("offset") int offset);
23 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/chunyu/spike/springrainnews/uis/activities/MainActivity.java:
--------------------------------------------------------------------------------
1 | package me.chunyu.spike.springrainnews.uis.activities;
2 |
3 | import android.content.Context;
4 | import android.os.Bundle;
5 | import android.support.design.widget.FloatingActionButton;
6 | import android.support.design.widget.NavigationView;
7 | import android.support.design.widget.Snackbar;
8 | import android.support.v4.view.GravityCompat;
9 | import android.support.v4.widget.DrawerLayout;
10 | import android.support.v7.app.ActionBarDrawerToggle;
11 | import android.support.v7.app.AppCompatActivity;
12 | import android.support.v7.widget.LinearLayoutManager;
13 | import android.support.v7.widget.RecyclerView;
14 | import android.support.v7.widget.Toolbar;
15 | import android.view.Menu;
16 | import android.view.MenuItem;
17 | import android.widget.Toast;
18 |
19 | import java.util.List;
20 |
21 | import javax.inject.Inject;
22 |
23 | import butterknife.Bind;
24 | import butterknife.ButterKnife;
25 | import me.chunyu.spike.springrainnews.NewsApplication;
26 | import me.chunyu.spike.springrainnews.R;
27 | import me.chunyu.spike.springrainnews.mvp.models.AvengersCharacter;
28 | import me.chunyu.spike.springrainnews.mvp.presenters.MainPresenter;
29 | import me.chunyu.spike.springrainnews.mvp.views.MainView;
30 | import me.chunyu.spike.springrainnews.uis.adapters.MainListAdapter;
31 |
32 | public class MainActivity extends AppCompatActivity implements MainView {
33 |
34 | private static final String TAG = "DEBUG-WCL: " + MainActivity.class.getSimpleName();
35 |
36 | @Inject MainPresenter mMainPresenter;
37 |
38 | @Inject Context mAppContext;
39 |
40 | @Bind(R.id.toolbar) Toolbar mToolbar;
41 | @Bind(R.id.fab) FloatingActionButton mFab;
42 | @Bind(R.id.nav_view) NavigationView mNavView;
43 | @Bind(R.id.drawer_layout) DrawerLayout mDrawerLayout;
44 |
45 | @Bind(R.id.main_rv_list) RecyclerView mRvList; // 列表视图
46 |
47 | private MainListAdapter mListAdapter; // 列表视图的适配器
48 |
49 | @Override
50 | protected void onCreate(Bundle savedInstanceState) {
51 | super.onCreate(savedInstanceState);
52 | initUi(); // 初始化Ui
53 | initList();
54 | initDi(); // 初始化依赖注入
55 | initDefault(); // 默认配置
56 | initPresenter(); // 初始化展示
57 | }
58 |
59 | @Override protected void onResume() {
60 | super.onResume();
61 | mMainPresenter.onResume();
62 | }
63 |
64 | @Override protected void onStop() {
65 | super.onStop();
66 | mMainPresenter.onStop();
67 | }
68 |
69 | @Override protected void onDestroy() {
70 | super.onDestroy();
71 | mMainPresenter.onDestroy();
72 | }
73 |
74 | @Override
75 | public void onBackPressed() {
76 | DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
77 | if (drawer.isDrawerOpen(GravityCompat.START)) {
78 | drawer.closeDrawer(GravityCompat.START);
79 | } else {
80 | super.onBackPressed();
81 | }
82 | }
83 |
84 | @SuppressWarnings("StatementWithEmptyBody")
85 | public boolean onNavigationItemSelected(MenuItem item) {
86 | int id = item.getItemId();
87 |
88 | if (id == R.id.nav_camera) {
89 | // Handle the camera action
90 | } else if (id == R.id.nav_gallery) {
91 |
92 | } else if (id == R.id.nav_slideshow) {
93 |
94 | } else if (id == R.id.nav_manage) {
95 |
96 | } else if (id == R.id.nav_share) {
97 |
98 | } else if (id == R.id.nav_send) {
99 |
100 | }
101 |
102 | DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
103 | drawer.closeDrawer(GravityCompat.START);
104 | return true;
105 | }
106 |
107 |
108 | @Override
109 | public boolean onCreateOptionsMenu(Menu menu) {
110 | // Inflate the menu; this adds items to the action bar if it is present.
111 | getMenuInflater().inflate(R.menu.main, menu);
112 | return true;
113 | }
114 |
115 | @Override
116 | public boolean onOptionsItemSelected(MenuItem item) {
117 | // Handle action bar item clicks here. The action bar will
118 | // automatically handle clicks on the Home/Up button, so long
119 | // as you specify a parent activity in AndroidManifest.xml.
120 | int id = item.getItemId();
121 |
122 | //noinspection SimplifiableIfStatement
123 | if (id == R.id.action_settings) {
124 | return true;
125 | }
126 |
127 | return super.onOptionsItemSelected(item);
128 | }
129 |
130 | @Override public void setListData(List