下面記錄學習的內容
一.添加依賴項 (本人添加的依賴項如下)
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