来张图:
前言
基于前一篇文章中的封装类,来实现,类似ListView添加header和footer效果,及多种ViewHolder的支持。实际上,header或footer也是一种ViewHolder
源码分析
在ListView中,一般使用BaseAdapter。BaseAdapter中有个方法public int getItemViewType(int position)
,一般重写它,根据position的不同,自定义不同的type。然后在getView()中,根据viewType的不同,创建不同的view。
RecyclerView其实也是类似的。
RecyclerView.Adapter中,有方法public int getItemViewType(int position)
,来设定viewType;在createViewHolder()中,使用,源码:
public final VH createViewHolder(ViewGroup parent, int viewType) {
TraceCompat.beginSection(TRACE_CREATE_VIEW_TAG);
final VH holder = onCreateViewHolder(parent, viewType);
holder.mItemViewType = viewType;
TraceCompat.endSection();
return holder;
}
该方法是final的,即不能重写。所以要重写的是 public abstract VH onCreateViewHolder(ViewGroup parent, int viewType)
。
接着在bindViewHolder()中,根据viewType的不同,来绑定数据。该方法也是final的,能重写的方法是:public abstract void onBindViewHolder(VH holder, int position)
结合封装代码分析
先来看AbsAdapter的部分源码:
public static final int VIEW_TYPE_HEADER = 1024;
public static final int VIEW_TYPE_FOOTER = 1025;
@Override
public final BaseHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == VIEW_TYPE_HEADER) {
return new BaseHolder(headerView);
} else if (viewType == VIEW_TYPE_FOOTER) {
return new BaseHolder(footerView);
} else {
return createCustomViewHolder(parent, viewType);
}
}
public abstract VH createCustomViewHolder(ViewGroup parent, int viewType);
@Override
public final void onBindViewHolder(BaseHolder holder, int position) {
switch (holder.getItemViewType()) {
case VIEW_TYPE_HEADER:
case VIEW_TYPE_FOOTER:
break;
default:
bindCustomViewHolder((VH) holder, position);
break;
}
}
public abstract void bindCustomViewHolder(VH holder, int position);
public void addHeaderView(View headerView) {...}
public void addFooterView(View footerView) {...}
BaseAdapter的部分源码:
@Override
public final int getItemViewType(int position) {
if (headerView != null && position == 0) {
return VIEW_TYPE_HEADER;
} else if (footerView != null && position == dataList.size() + getHeaderExtraViewCount()) {
return VIEW_TYPE_FOOTER;
} else {
return getCustomViewType(position);
}
}
public abstract int getCustomViewType(int position);
通过前面的分析,直接重写BaseAdapter,实现三个抽象方法即可。
示例
package com.stone.recyclerview.c04_headerfooter;
import android.animation.ValueAnimator;
import android.app.Activity;
import android.content.Context;
import android.graphics.Color;
import android.os.Bundle;
import android.support.annotation.LayoutRes;
import android.support.annotation.Nullable;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.TextView;
import com.stone.recyclerview.R;
import com.stone.recyclerview.c02_click.RecyclerItemClickListener;
import com.stone.recyclerview.c03_abstract.base.BaseAdapter;
import com.stone.recyclerview.c03_abstract.base.BaseHolder;
import com.stone.recyclerview.c05_decoration.DividerItemDecoration;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
/**
* desc : RecyclerView 添加 click事件
* author : stone
* email : aa86799@163.com
* time : 29/03/2017 20 37
*/
public class MainActivity extends Activity {
private List<String> mList;
private RecyclerView mRecyclerView;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
initData();
initViews();
addItemClickWay3();
}
private void initData() {
mList = new ArrayList<>();
for (int i = 'A'; i < 'z'; i++) {
mList.add("" + (char) i);
}
}
private void initViews() {
mRecyclerView = new RecyclerView(this);
setContentView(mRecyclerView);
mRecyclerView.setLayoutParams(new FrameLayout.LayoutParams(-1, -1));
//set layoutManager
// LinearLayoutManager layoutManager = new LinearLayoutManager(this);
LinearLayoutManager layoutManager = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false);
mRecyclerView.setLayoutManager(layoutManager);
//add ItemDecoration
mRecyclerView.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL_LIST));
// mRecyclerView.addItemDecoration(new android.support.v7.widget.DividerItemDecoration(this,
// android.support.v7.widget.DividerItemDecoration.VERTICAL));
// mRecyclerView.addItemDecoration(new RectDecoration());
}
/*
在holder构造初始化时,添加click监听
*/
private void addItemClickWay3() {
final ClickAdapter3 adapter = new ClickAdapter3(this, mList);
ImageView imageView = new ImageView(this);
imageView.setLayoutParams(new RecyclerView.LayoutParams(-1, 600));
imageView.setScaleType(ImageView.ScaleType.FIT_CENTER);
imageView.setImageResource(R.drawable.cat);
adapter.addHeaderView(imageView);
adapter.setListener(new RecyclerItemClickListener.OnItemClickListener() {
@Override
public void onItemClick(View view, int position) {
clickAnim(view);
System.out.println("onItemClick " + position + adapter.getItem(position));
}
@Override
public void onItemLongClick(View view, int position) {
System.out.println("onItemLongClick " + position + "__" + adapter.getItem(position));
}
});
mRecyclerView.setAdapter(adapter);
}
/**
* 随机颜色
*/
private static Random random = new Random();
private static int getColor() {
StringBuilder sb = new StringBuilder();
String temp;
for (int i = 0; i < 4; i++) {
temp = Integer.toHexString(random.nextInt(0xFF));
if (temp.length() == 1) {
temp = "0" + temp;
}
sb.append(temp);
}
return Color.parseColor("#" + sb.toString());
}
private static void clickAnim(final View view) {
ValueAnimator anim = ValueAnimator.ofFloat(1, 1.2f, 1);
anim.setDuration(300);
anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float rate = animation.getAnimatedFraction();
float value = (float) animation.getAnimatedValue();
view.setScaleX(value);
view.setScaleY(value);
}
});
anim.start();
}
private static class SimplifyVHWithListener extends BaseHolder {
public SimplifyVHWithListener(ViewGroup parent, @LayoutRes int resId) {
super(parent, resId);
}
public SimplifyVHWithListener(View view, final RecyclerItemClickListener.OnItemClickListener listener) {
super(view);
view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (listener != null) {
listener.onItemClick(v, getAdapterPosition());
}
}
});
view.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
if (listener != null) {
listener.onItemLongClick(v, getAdapterPosition());
}
return true;
}
});
}
}
private static class ClickAdapter3 extends BaseAdapter<String, BaseHolder> {
public static final int VIEW_TYPE_TEXT = 1;
public static final int VIEW_TYPE_IMAGE = 2;
private RecyclerItemClickListener.OnItemClickListener mListener;
public ClickAdapter3(Context context) {
super(context);
}
public ClickAdapter3(Context context, List<String> list) {
super(context, list);
}
public void setListener(RecyclerItemClickListener.OnItemClickListener listener) {
mListener = listener;
}
@Override
public int getCustomViewType(int position) {
if (position % 2 == 0) {
return VIEW_TYPE_TEXT;
} else {
return VIEW_TYPE_IMAGE;
}
}
@Override
public BaseHolder createCustomViewHolder(ViewGroup parent, int viewType) {
switch (viewType) {
case VIEW_TYPE_TEXT:
return new SimplifyVHWithListener(
LayoutInflater.from(parent.getContext()).inflate(R.layout.basic_simple, null, false),
mListener
);
case VIEW_TYPE_IMAGE:
return new ImageViewHolder(
LayoutInflater.from(parent.getContext()).inflate(R.layout.image_holder, null, false),
mListener);
}
return null;
}
@Override
public void bindCustomViewHolder(BaseHolder holder, final int position) {
holder.itemView.setFocusable(true);//加了这句,电视上就能滚动了
switch (getItemViewType(position)) {
case VIEW_TYPE_TEXT: {
TextView tvTitle = holder.getView(R.id.tv_title);
tvTitle.setText(getItem(position));
View vImg = holder.getView(R.id.v_img);
vImg.setBackgroundColor(getColor());
}
break;
case VIEW_TYPE_IMAGE: {
TextView tvTitle = holder.getView(R.id.tv_title);
tvTitle.setText(getItem(position));
ImageView imageView = holder.getView(R.id.iv_image);
imageView.setImageResource(R.drawable.scenery);
}
break;
}
}
}
private static class ImageViewHolder extends BaseHolder {
public ImageViewHolder(ViewGroup parent, @LayoutRes int resId) {
super(parent, resId);
}
public ImageViewHolder(View view, final RecyclerItemClickListener.OnItemClickListener listener) {
super(view);
view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (listener != null) {
listener.onItemClick(v, getAdapterPosition());
}
}
});
view.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
if (listener != null) {
listener.onItemLongClick(v, getAdapterPosition());
}
return true;
}
});
}
}
}
注意
要注意position的问题。添加了header或footer,那么在获取数据时,要做相应的处理。
前面的封装代码已经把这个考虑进去了:
/**
* 根据位置获取一条数据
*
* @param position View的位置
* @return 数据
*/
public M getItem(int position) {
if (headerView != null && position == 0
|| position >= dataList.size() + getHeaderExtraViewCount()) {
return null;
}
return headerView == null ? dataList.get(position) : dataList.get(position - 1);
}