下面记录学习的内容
一.添加依赖项 (本人添加的依赖项如下)
implementation 'com.android.support:cardview-v7:27.1.1'
implementation 'com.android.support:design:27.1.1'
implementation 'com.squareup.retrofit2:retrofit:2.3.0'
implementation 'com.google.code.gson:gson:2.6.2'
implementation 'io.reactivex:rxjava:1.1.0'
implementation 'io.reactivex:rxandroid:1.1.0'
implementation 'com.squareup.retrofit2:converter-gson:2.1.0'
implementation 'com.squareup.retrofit2:adapter-rxjava:2.1.0'
implementation 'com.nostra13.universalimageloader:universal-image-loader:1.9.3'
二.定义数据bean
定义 Movie 类
public class Movie {
public String title;
public String original_title;
public String year;
public MovieImage images;
public static class MovieImage{
public String small;
public String large;
public String medium;
}
}
定义 Movie list 类
public class MovieSubject {
public int start;
public List<Movie> subjects;
public String title;
}
三.Retrofit + Rxjava
定义一个 Retrofit 相关的类
个人认为可以分为下面几个步骤:
1. 创建 OKHttpClient
2. 创建Retrofit
3. 添加 函数 接口 ApiService
4. 实现调用的函数
5. 定义接口类
6. 调用函数getTop250获取数据
public class RetrofitServiceManager {
private static final int DEFAULT_TIME_OUT = 5;//超时时间 5s
private static final int DEFAULT_READ_TIME_OUT = 10;
private Retrofit mRetrofit;
private OkHttpClient.Builder builder;
private ApiService apiService = null;
private RetrofitServiceManager(){
//1. 创建 OKHttpClient
builder = new OkHttpClient.Builder()
.connectTimeout(DEFAULT_TIME_OUT, TimeUnit.SECONDS)
.writeTimeout(DEFAULT_READ_TIME_OUT,TimeUnit.SECONDS)
.readTimeout(DEFAULT_READ_TIME_OUT,TimeUnit.SECONDS);
//2. 创建Retrofit
mRetrofit = new Retrofit.Builder()
.client(builder.build())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.baseUrl("https://api.douban.com/v2/movie/")
.build();
//3.添加 函数 接口 ApiService
apiService = mRetrofit.create(ApiService.class);
}
private static class SingletonHolder{
private static final RetrofitServiceManager INSTANCE = new RetrofitServiceManager();
}
public static RetrofitServiceManager getInstance(){
return SingletonHolder.INSTANCE;
}
//4.实现调用的函数
public void getTop250(int start, int count, Subscriber<MovieSubject> subscriber){
apiService.getTop250(start,count)
.subscribeOn(Schedulers.io())
.unsubscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(subscriber);
}
}
Apiservice 接口定义如下
//5.定义接口类
public interface ApiService {
//获取豆瓣Top250 榜单
@GET("top250")
Observable<MovieSubject> getTop250(@Query("start") int start, @Query("count")int count);
}
注意 MovieSubject 相关的几个地方要一致,MovieSubject 就是要返回的数据类型,要从网络端得到的数据类型
private void getMovieList(){
//6.调用函数getTop250获取数据
RetrofitServiceManager.getInstance().getTop250(0, 10, new Subscriber<MovieSubject>() {
@Override
public void onCompleted() {
//数据传输完成
Log.e(TAG,"MainActivity onCompleted");
}
@Override
public void onError(Throwable e) {
//e.getMessage()可以打印出网络报错的原因,如手机网络权限内打开
Log.e(TAG,"MainActivity onError: "+ e.getMessage());
}
@Override
public void onNext(MovieSubject movieSubject) {
//传输数据
Log.e(TAG,"MainActivity onNext");
myRecyclerViewAdapter.setmMovies(movieSubject.subjects);
//通知RecyclerViewAdapter适配器更新数据
myRecyclerViewAdapter.notifyDataSetChanged();
}
});
}
四.RecyclerView使用
这个个人认为可以参照 listview的使用,会更容易理解;适配器的定义如下:
public class MyRecyclerViewAdapter extends RecyclerView.Adapter<MyRecyclerViewAdapter.MovieHolder> {
private List<Movie> mMovies;
private String TAG = "MyRecyclerViewAdapter";
private Context context = null;
public MyRecyclerViewAdapter(Context context){
this.context = context;
}
@NonNull
@Override
public MovieHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
//创建RecyclerView的list中每个item的布局,放在 moive_item中
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.moive_item,parent,false);
MovieHolder holder = new MovieHolder(view);
return holder;
}
public void setmMovies(List<Movie> mMovies) {
this.mMovies = mMovies;
}
@Override
public void onBindViewHolder(@NonNull MovieHolder holder, int position) {
//Log.e(TAG,"xtk onBindViewHolder");
Movie movie = mMovies.get(position);
Log.e(TAG,"xtk onBindViewHolder:" + movie.title);
ImageLoader.getInstance().displayImage(movie.images.small,holder.mImageView);
holder.time.setText("上映时间:"+movie.year + "年");
holder.title.setText(movie.title);
holder.subTitle.setText(movie.original_title);
}
@Override
public int getItemCount() {
//return 0;
int count = mMovies == null ? 0:mMovies.size();
Log.e(TAG,"xtk getItemCount:" + String.valueOf(count));
return count;
}
//RecyclerView的每个item的要显示内容
public static class MovieHolder extends RecyclerView.ViewHolder{
public ImageView mImageView;
public TextView title;
public TextView subTitle;
public TextView time;
public MovieHolder(View itemView) {
super(itemView);
mImageView = (ImageView) itemView.findViewById(R.id.movie_image);
title = (TextView) itemView.findViewById(R.id.movie_title);
subTitle = (TextView) itemView.findViewById(R.id.movie_sub_title);
time = (TextView) itemView.findViewById(R.id.movie_time);
}
}
}
通知数据更新 myRecyclerViewAdapter.notifyDataSetChanged()
其中收到更新数据后,函数调用如下
1.getItemCount() :得到要显示的 list数目
2.onCreateViewHolder:创建需要的item个数即布局情况
3.onBindViewHolder:数据加载到每个 item上面
item的布局如下(moive_item.xml):
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="120dp"
android:background="@drawable/item_shape"
>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
>
<ImageView
android:id="@+id/movie_image"
android:layout_width="80dp"
android:layout_height="100dp"
android:layout_marginTop="10dp"
android:layout_marginBottom="10dp"
android:layout_marginLeft="15dp"
/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_marginLeft="10dp"
android:layout_toRightOf="@+id/movie_image"
>
<TextView
android:id="@+id/movie_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="18sp"
android:textColor="@android:color/black"
android:text="hello"
/>
<TextView
android:id="@+id/movie_sub_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="18sp"
android:layout_marginTop="8dp"
android:textColor="@android:color/black"
android:text="hello"
/>
<TextView
android:id="@+id/movie_time"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:textSize="18sp"
android:textColor="@android:color/black"
/>
</LinearLayout>
</RelativeLayout>
</LinearLayout>
五.MainActivity代码如下
public class MainActivity extends AppCompatActivity {
private RecyclerView recyclerView = null;
private MyRecyclerViewAdapter myRecyclerViewAdapter = null;
private String TAG = "MainActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.e(TAG,"MainActivity onCreate");
Toolbar toolbar = (Toolbar) findViewById(R.id.main_toolbar);
toolbar.setNavigationOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
finish();
}
});
toolbar.setTitle("hell");
recyclerView = (RecyclerView) findViewById(R.id.recycler_view);
recyclerView.addItemDecoration(new MovieDecoration());
//定义recyclerView 的layout,否则可能会出现不显示数据
LinearLayoutManager manager = new LinearLayoutManager(this);
manager.setOrientation(LinearLayoutManager.VERTICAL);
recyclerView.setLayoutManager(manager);
myRecyclerViewAdapter = new MyRecyclerViewAdapter(this);
recyclerView.setAdapter(myRecyclerViewAdapter);
getMovieList();
}
private void getMovieList(){
RetrofitServiceManager.getInstance().getTop250(0, 10, new Subscriber<MovieSubject>() {
@Override
public void onCompleted() {
Log.e(TAG,"MainActivity onCompleted");
}
@Override
public void onError(Throwable e) {
Log.e(TAG,"MainActivity onError: "+ e.getMessage());
}
@Override
public void onNext(MovieSubject movieSubject) {
Log.e(TAG,"MainActivity onNext");
myRecyclerViewAdapter.setmMovies(movieSubject.subjects);
myRecyclerViewAdapter.notifyDataSetChanged();
}
});
}
//RecyclerView item间距布置
public static class MovieDecoration extends RecyclerView.ItemDecoration{
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
outRect.set(0,0,0,20);
}
}
}
六.ImageLoader 初始化配置
public class MovieApplication extends Application {
private static final int MEMORY_SIZE = 5 * 1024 * 1024;
private static final int DISK_SIZE = 20 * 1024 * 1024;
@Override
public void onCreate() {
super.onCreate();
// 初始化 Image-Loader
DisplayImageOptions options = new DisplayImageOptions.Builder()
.cacheInMemory(true)
.cacheOnDisk(true)
.build();
ImageLoaderConfiguration configuration = new ImageLoaderConfiguration.Builder(this)
.memoryCache(new LruMemoryCache(MEMORY_SIZE))
.diskCache(new UnlimitedDiscCache(new File(getCacheDir(),"caches")))
.diskCacheSize(DISK_SIZE)
.defaultDisplayImageOptions(options)
.build();
ImageLoader.getInstance().init(configuration);
}
}
七.AndroidMainfest中添加下面内容
android:name=".MovieApplication"
八、activity_main 文件
<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"
android:orientation="vertical"
android:background="@android:color/darker_gray"
tools:context=".MainActivity">
<android.support.v7.widget.Toolbar
android:id="@+id/main_toolbar"
android:layout_width="match_parent"
android:layout_height="50dp"
android:background="@color/colorPrimary"
/>
<android.support.v7.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="10dp"
/>
</LinearLayout>
调试中遇到的问题:
1.没有打开网络权限
2.LinearLayout布局没有设置方向导致数据一直显示不出来
3.ImageLoader 加载网络图片要先初始化配置
4.出现错误 call to OpenGL ES API with no current context,该问题不影响功能
本人还在Android学习的初级阶段,因此写的内容都非常简单且大部分为代码的堆叠
本代码参考内容(有的部分是直接拿来)
1.https://www.jianshu.com/p/811ba49d0748
可能有人更喜欢直接看代码(上传时积分处没有选 0 的项,就选择了1):
https://download.csdn.net/download/shiluohuashengmi/10475752