从头开始学 RecyclerView(四) 类 ListView 添加 header 和 footer、多种 ViewHolder 的支持

1,405 阅读3分钟
原文链接: blog.csdn.net

来张图:

这里写图片描述

前言


基于前一篇文章中的封装类,来实现,类似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;
                }
            });
        }

    }
}

详情:github.com/aa86799/Rec…

注意


要注意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);
}