Android学习笔记---MVP架构系列0x03

上次我们完成了界面的展现,这次我们把数据绑定到界面上,然后把整个项目都完善好.想查看完整的代码可以到这里

代码

MovieContract.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* 用于管理loadMovie功能模块的View与Presenter
*/

public class MovieContract {
/**
* loadMovie模块的View,提供对外的UI更新接口
*/

interface View extends BaseView<Presenter>{
void loadMore(ArrayList<Movie> movie);//上拉加载更多
void refresh(ArrayList<Movie> movie);//下拉刷新
void loadCompleted();//改变上拉加载状态
}
/**
* loadMovie模块的Presenter,负责获取数据并处理,更新UI
*/

interface Presenter extends BasePresenter{
void refresh();//刷新
void loadMore(int start);//加载更多
}
}

现在我们需要定义项目的数据对象,按照HTTP返回的JSON数据,我们定义了下面3个数据对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
/**
* 电影图片对象
*/

public class MovieImage{
String small;
String large;
String medium;
public String getSmall() {
return small;
}
public void setSmall(String small) {
this.small = small;
}
public String getLarge() {
return large;
}
public void setLarge(String large) {
this.large = large;
}
public String getMedium() {
return medium;
}
public void setMedium(String medium) {
this.medium = medium;
}
}

/**
* 一个电影对象,包含标题和图片URL
*/

public class Movie {
String title;
MovieImage images;
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public MovieImage getImages() {
return images;
}
public void setImages(MovieImage images) {
this.images = images;
}
}

/**
* 网络请求返回的Json数据
*/

public class HttpResult {
ArrayList<Movie> subjects;
public ArrayList<Movie> getSubjects() {
return subjects;
}
public void setSubjects(ArrayList<Movie> subjects) {
this.subjects = subjects;
}
}

接下来我们就可以完成数据请求和展现了,首先我们先完成数据的获取,按照Retrofit的请求方法完成数据的请求.
MovieService.java

1
2
3
4
public interface MovieService {
@GET("top250")
Call<HttpResult> getMovie(@Query("start") int start,@Query("count") int count);
}

MoviePresenter.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
/**
* 具体的loadMovie模块的Presenter
*/

public class MoviePresenter implements MovieContract.Presenter {
//持有View对象,用于对UI进行更新
MovieContract.View mView;
String BASIC_URL = "https://api.douban.com/v2/movie/";

public MoviePresenter(MovieContract.View view) {
this.mView = view;
mView.setPresenter(this);
}

@Override
public void start() {
}
@Override
public void refresh() {
MovieService service = getMovieService();
Call<HttpResult> call = service.getMovie(0, 25);
call.enqueue(new Callback<HttpResult>() {
@Override
public void onResponse(Call<HttpResult> call, Response<HttpResult> response) {
ArrayList<Movie> subjects = response.body().getSubjects();
mView.refresh(subjects);//更新UI
}
@Override
public void onFailure(Call<HttpResult> call, Throwable t) {
}
});
}

/**
* 获取MovieService
* @return
*/

private MovieService getMovieService() {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(BASIC_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
return retrofit.create(MovieService.class);
}
@Override
public void loadMore(int start) {
Log.e("start", start + "");
MovieService service = getMovieService();
Call<HttpResult> call = service.getMovie(start, 25);
call.enqueue(new Callback<HttpResult>() {
@Override
public void onResponse(Call<HttpResult> call, Response<HttpResult> response) {
ArrayList<Movie> subjects = response.body().getSubjects();
if (subjects != null) {
mView.loadMore(subjects);//更新UI
mView.loadCompleted();//改变加载状态
}
}
@Override
public void onFailure(Call<HttpResult> call, Throwable t) {
}
});
}
}

最后只要完成界面的显示处理即可
MovieFragment.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
public class MovieFragment extends Fragment implements MovieContract.View {

//持有Presenter的实例
MoviePresenter mPresenter;
ArrayList<Movie> movies = new ArrayList<>();
MovieAdapter mAdapter;
@BindView(R.id.mf_rv_list)
RecyclerView rv_list;
@BindView(R.id.mf_srl)
SwipeRefreshLayout srl;
@BindView(R.id.load_more)
LinearLayout load_more;
private boolean isLoading=false;

public static MovieFragment newInstance() {
return new MovieFragment();
}

public MovieFragment() {

}

@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.movie_frag, container, false);
ButterKnife.bind(this, root);
mAdapter = new MovieAdapter(getActivity(), movies);
initView();
return root;
}

private void initView() {
load_more.setVisibility(View.GONE);
srl.setColorSchemeColors(R.color.colorPrimary);
srl.post(new Runnable() {
@Override
public void run() {
srl.setRefreshing(true);
}
});
srl.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
mPresenter.refresh();
}
});
mPresenter.refresh();
final StaggeredGridLayoutManager layoutManager = new StaggeredGridLayoutManager(3, StaggeredGridLayoutManager.VERTICAL);
rv_list.setLayoutManager(layoutManager);
rv_list.setAdapter(mAdapter);
rv_list.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
int lastPositions[] = layoutManager.findLastVisibleItemPositions(new int[layoutManager.getSpanCount()]);
int lastVisiableItemPos = findMax(lastPositions);
if (lastVisiableItemPos == mAdapter.getItemCount()-1) {
if(!isLoading) {
isLoading=true;
load_more.setVisibility(View.VISIBLE);
mPresenter.loadMore(lastVisiableItemPos + 1);
}
}
}

@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
}
});
}

private int findMax(int[] lastPositions) {
int max = lastPositions[0];
for (int value : lastPositions) {
if (value > max) {
max = value;
}
}
return max;
}

@Override
public void setPresenter(MovieContract.Presenter presenter) {
mPresenter = (MoviePresenter) presenter;
}

@Override
public void loadMore(ArrayList<Movie> movie) {
movies.addAll(movie);
mAdapter.notifyDataSetChanged();
load_more.setVisibility(View.GONE);
}

@Override
public void refresh(ArrayList<Movie> movie) {
movies.clear();
movies.addAll(movie);
mAdapter.notifyDataSetChanged();
srl.setRefreshing(false);
}
@Override
public void loadCompleted() {
isLoading = false;
}
}

最后我们把其余部分的代码完善便可
MovieAdapter.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
/**
* Movie列表的Adapter
*/

public class MovieAdapter extends RecyclerView.Adapter<MovieVH> {

Context mContext;
private ArrayList<Movie> movies;

public MovieAdapter(Context context, ArrayList<Movie> movies) {
this.mContext = context;
this.movies = movies;
}

@Override
public MovieVH onCreateViewHolder(ViewGroup parent, int viewType) {
return new MovieVH(LayoutInflater.from(mContext).inflate(R.layout.item_movie, null));
}

@Override
public void onBindViewHolder(MovieVH holder, int position) {
String url = movies.get(position).getImages().getMedium();
Glide.with(mContext).load(url).placeholder(R.drawable.pictures_no).centerCrop().into(holder.iv_movie);
holder.tv_title.setText(movies.get(position).getTitle());

}

@Override
public int getItemCount() {
return movies == null ? 0 : movies.size();
}

}

class MovieVH extends RecyclerView.ViewHolder {
@BindView(R.id.im_iv_movie)
ImageView iv_movie;
@BindView(R.id.im_tv_title)
TextView tv_title;

public MovieVH(View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
}
}

完整的代码可以到我的Github上查看.

效果

最后我们的效果图如下
豆瓣电影Top250

总结

通过本次的一个简单的项目,介绍了MVP架构在Android上该如何实现.希望能对大家起到帮助.