目標
本篇是RecyclerView
的重構之路系列文章的第四篇, 講解的是IDouban項目 使用RecyclerView蚓让。
展示豆瓣 影院熱映 電影。讀完之后讥珍,您會知道RecyclerView
历极,CardView
的使用。
總體套路
熟悉ListView
, GridView
同學知道衷佃,要使用ListView
等控件趟卸,需要以下幾個步驟:
- 布局添加
ListView
- 構建
Adapter
, 設置setAdapter
- 構建數據集
List
- 處理OnItemClickListener
想要使用靈活性氏义,基本會選擇BaseAdapter
用于擴展锄列,里面的核心就是四個重寫的方法:
getCount, getItem, getItemId, getView
, 而getView
又是重中之重。其中還會涉及到優(yōu)化的問題惯悠, 我曾在課堂上直觀演示過不使用優(yōu)化的ListView
邻邮,內存消耗直線上升。
但是克婶,Google
使用support
包的方式發(fā)布了RecyclerView
, 在使用之前覺得筒严,也沒有什么好用的,寫得代碼還要多些情萤,但是隨著深入了解鸭蛙,發(fā)現Google
里的老司機,真是厲害筋岛,帶我裝逼规惰,帶我飛
。
初識RecyclerView
這不是詳細講解RecyclerView
的地方泉蝌,網上一堆堆的內容歇万,隨便你看。簡書里 就有好多文章介紹以及使用RV(RecyclerView)
勋陪。
這里提供幾個網頁:
- https://developer.android.com/training/material/lists-cards.html (中文的哦贪磺!)
- https://developer.android.com/reference/android/support/v7/widget/RecyclerView.html (api文檔)
- https://guides.codepath.com/android/using-the-recyclerview (強烈推薦實踐, 而且有新大陸)
一句話總結 什么是RecyvlerView
: 它是集ListView诅愚, GridView...
于一體的寒锚,能實現橫排劫映,豎排,以及瀑布流形式的效果刹前,在默認情況下啟用增添與刪除項目的動畫并可擴展動畫泳赋。
如何使用RecyclerView
- 布局添加
RecyclerView
- 構建
RecyclerView.Adapter
, 設置setAdapter
- 構建數據集
List
- 自己實現ItemClick事件
實現步驟
重點來講解具體的使用吧喇喉,讓你們久等了祖今。
CardView
俗稱 卡片
, 這個也是Google
使用support
包的形式新增加的拣技。需要添加 依賴:
compile 'com.android.support:cardview-v7:23.4.0'
這是我當前的版本
xml文件引入需要前綴: android.support.v7.widget.CardView
CardView
現階段千诬,我使用在MoviesFragment
, 以及RecyclerView
的布局文件中。
布局添加RecyclerView
構建RecyclerView.Adapter
Item布局
自定義ViewHolder
RecyclerView
強制要求使用ViewHolder
, 并且 RecyclerView.Adapter.onCreateViewHolder
返回就是ViewHolder
類型膏斤。So徐绑,
static class MoviesViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
ImageView mMovieImage;
TextView mMovieTitle;
RatingBar mMovieStars;
TextView mMovieRatingAverage;
Movies movie;
public MoviesViewHolder(View itemView) {
super(itemView);
mMovieImage = (ImageView) itemView.findViewById(R.id.movie_cover);
mMovieTitle = (TextView) itemView.findViewById(R.id.movie_title);
mMovieStars = (RatingBar) itemView.findViewById(R.id.rating_star);
mMovieRatingAverage = (TextView) itemView.findViewById(R.id.movie_average);
itemView.setOnClickListener(this);
}
public void updateMovie(Movies movie) {
if (movie == null) return;
this.movie = movie;
Context context = itemView.getContext();
Picasso.with(context)
.load(movie.getImages().getLarge())
.placeholder(context.getResources().getDrawable(R.mipmap.ic_launcher))
.into(mMovieImage);
mMovieTitle.setText(movie.getTitle());
final double average = movie.getRating().getAverage();
if (average == 0.0) {
mMovieStars.setVisibility(View.GONE);
mMovieRatingAverage.setText(context.getResources().getString(R.string.string_no_note));
} else {
mMovieStars.setVisibility(View.VISIBLE);
mMovieRatingAverage.setText(String.valueOf(average));
mMovieStars.setStepSize(0.5f);
mMovieStars.setRating((float) (movie.getRating().getAverage() / 2));
}
}
@Override
public void onClick(View v) {
Log.e(HomeActivity.TAG, "==> onClick....Item");
if (movie == null) return;
if (itemView == null) return;
Context context = itemView.getContext();
if (context == null) return;
Intent intent = new Intent(context, MovieDetailActivity.class);
intent.putExtra("movie", movie);
if (context instanceof Activity) {
Activity activity = (Activity) context;
Bundle bundle = ActivityOptionsCompat.makeSceneTransitionAnimation(activity, mMovieImage, "cover").toBundle();
ActivityCompat.startActivity(activity, intent, bundle);
}
}
}
說明:
1 MoviesViewHolder構造方法
用于保存itemView
, 并初始化其中的控件。
2 updateMovie
方法用于Adapter更新數據莫辨, 這里獨立成一個方法傲茄, 也為onClick方法提供數據支持。
3 onClick
方法沮榜,重寫View.OnClickListener的監(jiān)聽事件盘榨, 用于跳轉到電影詳情界面。
自定義Adapter
static class MoviesAdapter extends RecyclerView.Adapter<MoviesViewHolder> {
private List<Movies> movies;
private Context context;
@LayoutRes
private int layoutResId;
public MoviesAdapter(Context context, @NonNull List<Movies> movies, @LayoutRes int layoutResId) {
this.movies = movies;
this.layoutResId = layoutResId;
this.context = context;
}
@Override
public MoviesViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(context).inflate(layoutResId, parent, false);
return new MoviesViewHolder(itemView);
}
@Override
public void onBindViewHolder(MoviesViewHolder holder, int position) {
if (holder == null) return;
holder.updateMovie(movies.get(position));
}
@Override
public int getItemCount() {
return movies.size();
}
public void setData(List<Movies> movies) {
this.movies = movies;
notifyDataSetChanged();
}
}
說明:
Adapter
是RecyclerView
和數據的媒介敞映, 所以Adapter
中需要有List<Movies>
, 還需要設備上下文Context
(后續(xù)優(yōu)化较曼,不需要)磷斧, 以及外界傳入的itemView
的布局文件振愿, 以上基本就是MoviesAdapter
構造方法的用途。
繼承RecyclerView.Adapter
的類弛饭,需要重寫其中的三個方法:
-
onCreateViewHolder
, 執(zhí)行一次冕末,用于加載itemView
布局 -
onBindViewHolder
, 頻繁執(zhí)行,類似ListView
中的getView
-
getItemCount
, 用戶統(tǒng)計有多少item
這里還增加一個數據添加方法setData
侣颂,用于外界更新數據
RecyclerView設定
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
if (mRecyclerView != null) {
mRecyclerView.setHasFixedSize(true);
final GridLayoutManager layoutManager = new GridLayoutManager(getActivity().getApplicationContext(), 3);
mRecyclerView.setLayoutManager(layoutManager);
mMovieAdapter = new MoviesAdapter(getContext(), mMoviesList, R.layout.recyclerview_movies_item);
mRecyclerView.setAdapter(mMovieAdapter);
}
}
RecyclerView
的設定動作档桃,我是放在onActivityCreated
方法中。
final GridLayoutManager layoutManager = new GridLayoutManager(getActivity().getApplicationContext(), 3);
mRecyclerView.setLayoutManager(layoutManager);
RV 就是使用LayoutManger來顯示 豎直憔晒,還是橫排藻肄,還是瀑布流效果。IDouban我是使用GridLayoutManger
效果拒担,有3列嘹屯。
假如要ListView
形式的,采用LinearLayoutManager
, 瀑布流形式的 采用 StaggeredGridLayoutManager
其他代碼基本跟ListView
相似从撼!
構建數據集List
上一篇文章詳細解答了這個問題州弟,使用Retrofit
獲取到網絡數據,然后保存到List<Movies>
中, 何時存放在Adapter
List中,只需要在獲得數據的時刻婆翔,通過setData
方法把Movies列表傳入即可拯杠。
實現ItemClick事件
有點不方便的是 RV并沒提供OnItemClickListener
接口之類的, 這個在知乎上有人分析過啃奴。也給出一些實現方法潭陪。
我這里采用 在ViewHolder
中實現View.OnClickListener
并沒有在Adapter
的onBindViewHolder
中每次去new匿名內部類方式。
利用其中的itemView
來實現點擊Item響應事件纺腊。