本篇文章已授權微信公衆号 study_tech(楊守樂)獨家釋出轉載請标明出處
前言
最近在使用網易雲音樂的時候,看到如下圖的排版效果圖,自己也想實作一個
這裡采用網上用法最多的方式,而且是比較簡單的方式實作的,想要做項目的同學也可以快速入手搞定首頁界面,可以在最快的時間内模仿出來,且效果達到90%以上的相似
效果示範
至于圖檔的加載你們可以根據網上的Api擷取相應的圖檔加載到對應的位置,這裡隻是采用本地圖檔來示範
實作分析
這裡是采用RecyclerView的GridLayoutManager的一個SpanSize這麼一個東西,從下圖很容易知道其意思
項目結構
項目結構可能對初學者感覺很龐大,不用擔心,這裡我會按照下面的包名劃分,從最簡單的開始分析
引入依賴
首先是在Gradle中引入對RecyclerView的依賴
View包
由于項目用到的圖檔是有規格限定的,是以需要對ImageView覆寫,得到我們想要尺寸的圖檔
- SquareImageView:正方形圖檔
- RectImageView:長方形圖檔
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,);
}
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, );
}
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);
// 設定大小為寬度的三分之二
int halfWidthMeasureSpec = MeasureSpec.makeMeasureSpec(width / * , widthMode);
super.onMeasure(widthMeasureSpec, halfWidthMeasureSpec);
}
}
Music包
這裡是我們存儲實體的地方,其中四種類型的劃分,分别對應項目展示中的前三個子產品的劃分,其中還有一個标題也算是一種類型,是以共四種
public class Music {
public int type;
public String title;
// 後期可加入Glide加載網絡圖檔Url
public int imageId;
public interface TYPE {
int TYPE_GRID_THREE = ;
int TYPE_GRID_TWO = ;
int TYPE_LIST = ;
int TYPE_TITLE = ;
}
}
Listener包
由于RecyclerView自身是沒有點選事件的,是以這個包是RecyclerView的點選事件接口
public interface OnItemClickListener {
void OnItemClick(int position);
}
Decoration包
由于RecyclerView是不提供分割線的,是以這個包是自定義的分割線
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
這裡存儲的是我們混排效果的控件,标題可能會有點差別,其他是一樣的效果,為了後期友善拓展,我們就把他們分開,不代碼複用
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包
這裡就是對所有ViewHolder的控制器,然而這裡并不是混排效果實作的最終地方,隻不過是填充資料的地方
public class RecyclerAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> implements View.OnClickListener {
private List<Music> mList;
private Context mContext;
private LayoutInflater mInflater;
/**
* 點選事件
*/
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);
//點選事件
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);
//點選事件
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);
//點選事件
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);
//點選事件
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
這裡就是我們實作混排效果的關鍵,我們會根據不同類型的資料,對RecyclerView的SpanSize的進行設定
public class MainActivity extends AppCompatActivity implements OnItemClickListener {
private RecyclerView ry;
private GridLayoutManager layoutManager;
private RecyclerAdapter mAdapter;
private static List<Music> mList;
/**
* 模拟本地資料
*/
static {
mList = new ArrayList<>();
for (int i = ; i < ; 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 = ; i < ; i++) {
Music music = new Music();
music.type = Music.TYPE.TYPE_GRID_THREE;
music.imageId = R.drawable.ic_cover;
music.title = "先不要降溫!我沒錢買衣服";
mList.add(music);
}
for (int i = ; i < ; 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 = ; i < ; 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 = ; i < ; 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 = ; i < ; i++) {
Music music = new Music();
music.type = Music.TYPE.TYPE_LIST;
music.imageId = R.drawable.ic_cover;
music.title = "去看《銀翼殺手2049》前,你應該知道的三件事";
mList.add(music);
}
for (int i = ; i < ; 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 = ; i < ; i++) {
Music music = new Music();
music.type = Music.TYPE.TYPE_GRID_THREE;
music.imageId = R.drawable.ic_cover;
music.title = "[BGM]一定聽過的神級背景配樂";
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, );
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 ;
} else if (type == Music.TYPE.TYPE_GRID_TWO) {
return ;
} else if (type == Music.TYPE.TYPE_LIST) {
return ;
} else if (type == Music.TYPE.TYPE_TITLE) {
return ;
}
return ;
}
});
ry.setLayoutManager(layoutManager);
ry.addItemDecoration(new SpacesItemDecoration());
// 填充資料
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布局檔案
這裡的布局很簡單,比如用到我們的正方形圖檔,長方形圖檔等,這裡就不做代碼貼出,詳細可以檢視源碼
源碼下載下傳
源碼下載下傳