Android控件RecyclerView實(shí)現(xiàn)混排效果仿網(wǎng)易云音樂(lè)
前言
最近在使用網(wǎng)易云音樂(lè)的時(shí)候,看到如下圖的排版效果圖,自己也想實(shí)現(xiàn)一個(gè)

這里采用網(wǎng)上用法最多的方式,而且是比較簡(jiǎn)單的方式實(shí)現(xiàn)的,想要做項(xiàng)目的同學(xué)也可以快速入手搞定首頁(yè)界面,可以在最快的時(shí)間內(nèi)模仿出來(lái),且效果達(dá)到90%以上的相似
效果演示
至于圖片的加載你們可以根據(jù)網(wǎng)上的Api獲取相應(yīng)的圖片加載到對(duì)應(yīng)的位置,這里只是采用本地圖片來(lái)演示

實(shí)現(xiàn)分析
這里是采用RecyclerView的GridLayoutManager的一個(gè)SpanSize這么一個(gè)東西,從下圖很容易知道其意思

項(xiàng)目結(jié)構(gòu)
項(xiàng)目結(jié)構(gòu)可能對(duì)初學(xué)者感覺(jué)很龐大,不用擔(dān)心,這里我會(huì)按照下面的包名劃分,從最簡(jiǎn)單的開(kāi)始分析

引入依賴
首先是在Gradle中引入對(duì)RecyclerView的依賴
compile 'com.android.support:recyclerview-v7:25.3.1'
View包
由于項(xiàng)目用到的圖片是有規(guī)格限定的,所以需要對(duì)ImageView覆寫,得到我們想要尺寸的圖片
SquareImageView:正方形圖片
RectImageView:長(zhǎng)方形圖片
public class SquareImageView extends android.support.v7.widget.AppCompatImageView {
public SquareImageView(Context context) {
this(context,null);
}
public SquareImageView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs,0);
}
public SquareImageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
setScaleType(ScaleType.FIT_XY);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, widthMeasureSpec);
}
}
public class RectImageView extends android.support.v7.widget.AppCompatImageView {
public RectImageView(Context context) {
this(context, null);
}
public RectImageView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public RectImageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
setScaleType(ScaleType.FIT_XY);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int width = MeasureSpec.getSize(widthMeasureSpec);
// 設(shè)置大小為寬度的三分之二
int halfWidthMeasureSpec = MeasureSpec.makeMeasureSpec(width / 3 * 2, widthMode);
super.onMeasure(widthMeasureSpec, halfWidthMeasureSpec);
}
}
Music包
這里是我們存儲(chǔ)實(shí)體的地方,其中四種類型的劃分,分別對(duì)應(yīng)項(xiàng)目展示中的前三個(gè)模塊的劃分,其中還有一個(gè)標(biāo)題也算是一種類型,所以共四種
public class Music {
public int type;
public String title;
// 后期可加入Glide加載網(wǎng)絡(luò)圖片Url
public int imageId;
public interface TYPE {
int TYPE_GRID_THREE = 0x01;
int TYPE_GRID_TWO = 0x02;
int TYPE_LIST = 0x03;
int TYPE_TITLE = 0x04;
}
}
Listener包
由于RecyclerView自身是沒(méi)有點(diǎn)擊事件的,所以這個(gè)包是RecyclerView的點(diǎn)擊事件接口
public interface OnItemClickListener {
void OnItemClick(int position);
}
Decoration包
由于RecyclerView是不提供分割線的,所以這個(gè)包是自定義的分割線
public class SpacesItemDecoration extends RecyclerView.ItemDecoration {
private int space;
public SpacesItemDecoration(int space) {
this.space = space;
}
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
outRect.left = space;
outRect.right = space;
outRect.bottom = space;
outRect.top = space;
}
}
ViewHolder
這里存儲(chǔ)的是我們混排效果的控件,標(biāo)題可能會(huì)有點(diǎn)區(qū)別,其他是一樣的效果,為了后期方便拓展,我們就把他們分開(kāi),不代碼復(fù)用
public class GridThreeViewHolder extends RecyclerView.ViewHolder {
public SquareImageView iv_icon;
public TextView tv_content;
public GridThreeViewHolder(View itemView) {
super(itemView);
iv_icon = (SquareImageView) itemView.findViewById(R.id.iv_icon);
tv_content = (TextView) itemView.findViewById(R.id.tv_content);
}
}
public class GridTwoViewHolder extends RecyclerView.ViewHolder {
public RectImageView iv_icon;
public TextView tv_content;
public GridTwoViewHolder(View itemView) {
super(itemView);
iv_icon = (RectImageView) itemView.findViewById(R.id.iv_icon);
tv_content = (TextView) itemView.findViewById(R.id.tv_content);
}
}
public class ListViewHolder extends RecyclerView.ViewHolder {
public RectImageView iv_icon;
public TextView tv_content;
public ListViewHolder(View itemView) {
super(itemView);
iv_icon = (RectImageView) itemView.findViewById(R.id.iv_icon);
tv_content = (TextView) itemView.findViewById(R.id.tv_content);
}
}
public class TitleViewHolder extends RecyclerView.ViewHolder {
public TextView tv_content;
public TitleViewHolder(View itemView) {
super(itemView);
tv_content = (TextView) itemView.findViewById(R.id.tv_content);
}
}
Adapter包
這里就是對(duì)所有ViewHolder的控制器,然而這里并不是混排效果實(shí)現(xiàn)的最終地方,只不過(guò)是填充數(shù)據(jù)的地方
public class RecyclerAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> implements View.OnClickListener {
private List<Music> mList;
private Context mContext;
private LayoutInflater mInflater;
/**
* 點(diǎn)擊事件
*/
private OnItemClickListener mOnItemClickListener;
public void setOnItemClickListener(OnItemClickListener onItemClickListener) {
this.mOnItemClickListener = onItemClickListener;
}
public RecyclerAdapter(Context context, List<Music> list) {
this.mList = list;
this.mContext = context;
this.mInflater = LayoutInflater.from(context);
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view;
RecyclerView.ViewHolder mViewHolder = null;
if (viewType == Music.TYPE.TYPE_GRID_THREE) {
view = mInflater.inflate(R.layout.item_grid_three, parent, false);
mViewHolder = new GridThreeViewHolder(view);
} else if (viewType == Music.TYPE.TYPE_GRID_TWO) {
view = mInflater.inflate(R.layout.item_grid_two, parent, false);
mViewHolder = new GridTwoViewHolder(view);
} else if (viewType == Music.TYPE.TYPE_LIST) {
view = mInflater.inflate(R.layout.item_list, parent, false);
mViewHolder = new ListViewHolder(view);
} else if (viewType == Music.TYPE.TYPE_TITLE) {
view = mInflater.inflate(R.layout.item_title, parent, false);
mViewHolder = new TitleViewHolder(view);
}
return mViewHolder;
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
switch (getItemViewType(position)) {
case Music.TYPE.TYPE_GRID_THREE:
GridThreeViewHolder gHolder_three = (GridThreeViewHolder) holder;
gHolder_three.tv_content.setText(mList.get(position).title);
gHolder_three.iv_icon.setImageResource(mList.get(position).imageId);
//點(diǎn)擊事件
gHolder_three.itemView.setOnClickListener(this);
gHolder_three.itemView.setTag(position);
break;
case Music.TYPE.TYPE_GRID_TWO:
GridTwoViewHolder gHolder_two = (GridTwoViewHolder) holder;
gHolder_two.tv_content.setText(mList.get(position).title);
gHolder_two.iv_icon.setImageResource(mList.get(position).imageId);
//點(diǎn)擊事件
gHolder_two.itemView.setOnClickListener(this);
gHolder_two.itemView.setTag(position);
break;
case Music.TYPE.TYPE_LIST:
ListViewHolder lHolder = (ListViewHolder) holder;
lHolder.tv_content.setText(mList.get(position).title);
lHolder.iv_icon.setImageResource(mList.get(position).imageId);
//點(diǎn)擊事件
lHolder.itemView.setOnClickListener(this);
lHolder.itemView.setTag(position);
break;
case Music.TYPE.TYPE_TITLE:
TitleViewHolder tHolder = (TitleViewHolder) holder;
tHolder.tv_content.setText(mList.get(position).title);
//點(diǎn)擊事件
tHolder.itemView.setOnClickListener(this);
tHolder.itemView.setTag(position);
break;
}
}
@Override
public int getItemViewType(int position) {
return mList.get(position).type;
}
@Override
public int getItemCount() {
return mList.size();
}
@Override
public void onClick(View v) {
if (mOnItemClickListener != null) {
int position = (int) v.getTag();
mOnItemClickListener.OnItemClick(position);
}
}
}
Activity
這里就是我們實(shí)現(xiàn)混排效果的關(guān)鍵,我們會(huì)根據(jù)不同類型的數(shù)據(jù),對(duì)RecyclerView的SpanSize的進(jìn)行設(shè)置
public class MainActivity extends AppCompatActivity implements OnItemClickListener {
private RecyclerView ry;
private GridLayoutManager layoutManager;
private RecyclerAdapter mAdapter;
private static List<Music> mList;
/**
* 模擬本地?cái)?shù)據(jù)
*/
static {
mList = new ArrayList<>();
for (int i = 0; i < 1; i++) {
Music music = new Music();
music.type = Music.TYPE.TYPE_TITLE;
music.imageId = R.drawable.ic_cover;
music.title = "推薦歌單";
mList.add(music);
}
for (int i = 0; i < 6; i++) {
Music music = new Music();
music.type = Music.TYPE.TYPE_GRID_THREE;
music.imageId = R.drawable.ic_cover;
music.title = "先不要降溫!我沒(méi)錢買衣服";
mList.add(music);
}
for (int i = 0; i < 1; i++) {
Music music = new Music();
music.type = Music.TYPE.TYPE_TITLE;
music.imageId = R.drawable.ic_cover;
music.title = "推薦MV";
mList.add(music);
}
for (int i = 0; i < 4; i++) {
Music music = new Music();
music.type = Music.TYPE.TYPE_GRID_TWO;
music.imageId = R.drawable.ic_cover;
music.title = "Perfect Day";
mList.add(music);
}
for (int i = 0; i < 1; i++) {
Music music = new Music();
music.type = Music.TYPE.TYPE_TITLE;
music.imageId = R.drawable.ic_cover;
music.title = "精選專欄";
mList.add(music);
}
for (int i = 0; i < 3; i++) {
Music music = new Music();
music.type = Music.TYPE.TYPE_LIST;
music.imageId = R.drawable.ic_cover;
music.title = "去看《銀翼殺手2049》前,你應(yīng)該知道的三件事";
mList.add(music);
}
for (int i = 0; i < 1; i++) {
Music music = new Music();
music.type = Music.TYPE.TYPE_TITLE;
music.imageId = R.drawable.ic_cover;
music.title = "最新音樂(lè)";
mList.add(music);
}
for (int i = 0; i < 6; i++) {
Music music = new Music();
music.type = Music.TYPE.TYPE_GRID_THREE;
music.imageId = R.drawable.ic_cover;
music.title = "[BGM]一定聽(tīng)過(guò)的神級(jí)背景配樂(lè)";
mList.add(music);
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ry = (RecyclerView) findViewById(R.id.ry);
layoutManager = new GridLayoutManager(this, 6);
layoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
@Override
public int getSpanSize(int position) {
int type = mList.get(position).type;
if (type == Music.TYPE.TYPE_GRID_THREE) {
return 2;
} else if (type == Music.TYPE.TYPE_GRID_TWO) {
return 3;
} else if (type == Music.TYPE.TYPE_LIST) {
return 6;
} else if (type == Music.TYPE.TYPE_TITLE) {
return 6;
}
return 0;
}
});
ry.setLayoutManager(layoutManager);
ry.addItemDecoration(new SpacesItemDecoration(2));
// 填充數(shù)據(jù)
mAdapter = new RecyclerAdapter(this, mList);
mAdapter.setOnItemClickListener(this);
ry.setAdapter(mAdapter);
}
@Override
public void OnItemClick(int position) {
String title = mList.get(position).title;
Toast.makeText(this, title, Toast.LENGTH_SHORT).show();
}
}
layout布局文件
這里的布局很簡(jiǎn)單,比如用到我們的正方形圖片,長(zhǎng)方形圖片等,這里就不做代碼貼出,詳細(xì)可以查看源碼
源碼下載
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- Android項(xiàng)目實(shí)戰(zhàn)教程之高仿網(wǎng)易云音樂(lè)啟動(dòng)頁(yè)實(shí)例代碼
- Android實(shí)現(xiàn)網(wǎng)易云推薦歌單界面
- Android實(shí)現(xiàn)網(wǎng)易云音樂(lè)的旋轉(zhuǎn)專輯View
- Android5.0以上實(shí)現(xiàn)全透明的狀態(tài)欄方法(仿網(wǎng)易云界面)
- Android仿網(wǎng)易云音樂(lè)播放界面
- 漂亮的Android音樂(lè)歌詞控件 仿網(wǎng)易云音樂(lè)滑動(dòng)效果
- Android實(shí)現(xiàn)網(wǎng)易云音樂(lè)高仿版流程
相關(guān)文章
Android實(shí)現(xiàn)ListView的A-Z字母排序和過(guò)濾搜索功能 實(shí)現(xiàn)漢字轉(zhuǎn)成拼音
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)ListView的A-Z字母排序和過(guò)濾搜索功能,實(shí)現(xiàn)漢字轉(zhuǎn)成拼音功能2017-06-06
Android開(kāi)發(fā)之ImageLoader使用詳解
這篇文章主要介紹了Android開(kāi)發(fā)之ImageLoader使用詳解的相關(guān)資料,需要的朋友可以參考下2016-01-01
Android開(kāi)發(fā)之React Navigation 導(dǎo)航欄樣式調(diào)整+底部角標(biāo)消息提示
這篇文章主要介紹了React Navigation 導(dǎo)航欄樣式調(diào)整+底部角標(biāo)消息提示的相關(guān)知識(shí),非常不錯(cuò),具有一定的參考借鑒價(jià)值 ,需要的朋友可以參考下2019-05-05
Android 嵌套Fragment的使用實(shí)例代碼
本文主要介紹Android Fragment,在這里提供了實(shí)例代碼跟效果圖,希望能幫助有需要的小伙伴2016-07-07
Android實(shí)現(xiàn)動(dòng)態(tài)高斯模糊效果
在Android開(kāi)發(fā)中常常會(huì)用到高斯模糊,但有的時(shí)候我們可能會(huì)需要一個(gè)圖片以不同的模糊程度展現(xiàn)出來(lái),那如何實(shí)現(xiàn)呢,一起通過(guò)本文來(lái)學(xué)習(xí)學(xué)習(xí)吧。2016-08-08
android監(jiān)聽(tīng)軟鍵盤的彈出與隱藏的示例代碼
本篇文章主要介紹了android監(jiān)聽(tīng)軟鍵盤的彈出與隱藏的示例代碼,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-02-02
關(guān)于Android發(fā)送短信獲取送達(dá)報(bào)告的問(wèn)題(推薦)
最近公司開(kāi)發(fā)一個(gè)項(xiàng)目,要求app能夠發(fā)送短信并獲取送達(dá)報(bào)告。實(shí)現(xiàn)代碼非常簡(jiǎn)單的,下面小編給大家分享關(guān)于Android發(fā)送短信獲取送達(dá)報(bào)告的問(wèn)題,感興趣的朋友一起看看吧2017-03-03
Android自定義滾動(dòng)選擇器實(shí)例代碼
本篇文章主要介紹了Android自定義滾動(dòng)選擇器實(shí)例代碼,具有一定的參考價(jià)值,有興趣的可以了解一下。2017-01-01

