史上最强RecyclerView封装,泛型、反射、注解3大终极技能齐出

封装后的效果,比如写一个NewAdapter

@LayoutId(id = R.layout.item)
public class NewsAdapter extends CommonAdapter<String>{

    @DataSource
    List<String> list = Arrays.asList("","","","","","","","","");

    @BindView(id = R.id.imageView)
    ImageView imageView;
    @BindView(id = R.id.textView)
    TextView textView;

    @Override
    void onBind(String s) {
        textView.setText(s);
    }
}

史上最强RecyclerView封装,泛型、反射、注解3大终极技能齐出

我们需要写的代码量已经是少的可怜了


做的封装点:

泛型:(首先我们约束了数据参数是List)然后可以去除掉getItemCount和每次bind中都要在List中取数据的操作

反射、注解:反射拿到所有Field,并且拿到所有Field的注解,为我们省去了FVB操作;拿到类上的注解,为什么省去了create的操作

使用了通用的ViewHolder,里面没有任何东西


全部代码(所谓难者不会,会者不难,泛型反射注解很简单,可以这些堆砌起来,并且我做了一定的性能优化,而且我没写注释,萌新读者可能就 不是很看得懂了)

CommonAdapter

package com.example.demo;

import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import com.example.demo.anno.BindView;
import com.example.demo.anno.DataSource;
import com.example.demo.anno.LayoutId;

import java.lang.reflect.Field;
import java.util.List;

public abstract class CommonAdapter<T> extends RecyclerView.Adapter<CommonAdapter.ViewHolder> {

    class ViewHolder extends RecyclerView.ViewHolder {
        ViewHolder(View itemView) {
            super(itemView);
        }
    }

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(getClass().getAnnotation(LayoutId.class).id(), parent, false);
        return new ViewHolder(view);
    }

    @Override
    public void onBindViewHolder(CommonAdapter.ViewHolder holder, int position) {
        View view = holder.itemView;
        int len = bindViews.length;
        for (int i = 0; i < len; i ++) {
            BindView bindView = bindViews[i];
            if (bindView != null) {
                int viewId = bindView.id();
                View v = view.findViewById(viewId);
                Field f = fields[i];
                try {
                    f.set(this, v);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
        try {
            Object object = field.get(this);
            List list = (List) object;
            Object o = list.get(position);
            onBind((T) o);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }

    private int count;
    private BindView[] bindViews;
    private Field[] fields;
    private Field field;//这个是data source标记的field

    @Override
    public int getItemCount() {
        Field[] fields = getClass().getDeclaredFields();
        this.fields = fields;
        int len = fields.length;

        bindViews = new BindView[len];

        for (int i = 0; i < len; i++) {
            Field field = fields[i];

            if (count == 0) {
                DataSource dataSource = field.getAnnotation(DataSource.class);
                if (dataSource != null) {
                    this.field = field;
                    try {
                        Object object = field.get(this);
                        List list = (List) object;
                        count = list.size();
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                }
            }

            BindView bindView = field.getAnnotation(BindView.class);
            if (bindView != null) {
                bindViews[i] = bindView;
            }
        }

        return count;
    }

    abstract void onBind(T t);
}

注解1

package com.example.demo.anno;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface BindView {
    int id() default -1;
}

注解2

package com.example.demo.anno;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface DataSource {
}

注解3

package com.example.demo.anno;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface LayoutId {
    int id() default -1;
}



最后又手贱忍不住 封装了一下 现在的代码是这样的

@LayoutId(id = R.layout.item)
public class NewsAdapter extends CommonAdapter<Book>{

    @DataSource
    List<Book> list = Arrays.asList(
            new Book("元尊", R.drawable.star_empty),
            new Book("圣墟", R.drawable.star_full));

    @BindData(img_field = "bookRes")
    @BindView(id = R.id.imageView)
    ImageView imageView;

    @BindData(text_field = "bookName")
    @BindView(id = R.id.textView)
    TextView textView;
}

class Book {
    String bookName;
    int bookRes;

    public Book(String bookName, int bookRes) {
        this.bookName = bookName;
        this.bookRes = bookRes;
    }
}

去掉了bind方法。把setImageBitmap和setText两个行为封装了。原理是注解里标明字段,反射拿到Book对象字段对应的值,最终进行设置。

史上最强RecyclerView封装,泛型、反射、注解3大终极技能齐出


新增的注解

package com.example.demo.anno;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface BindData {
    String text_field() default "";
    String img_field() default "";
}


修改后的父adapter

package com.example.demo;

import android.content.Context;
import android.graphics.BitmapFactory;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;

import com.example.demo.anno.BindData;
import com.example.demo.anno.BindView;
import com.example.demo.anno.DataSource;
import com.example.demo.anno.LayoutId;

import java.io.File;
import java.lang.reflect.Field;
import java.util.List;

public class CommonAdapter<T> extends RecyclerView.Adapter<CommonAdapter.ViewHolder> {

    private Context context;

    class ViewHolder extends RecyclerView.ViewHolder {
        ViewHolder(View itemView) {
            super(itemView);
        }
    }

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        if (context == null) {
            context = parent.getContext();
        }

        View view = LayoutInflater.from(context).inflate(getClass().getAnnotation(LayoutId.class).id(), parent, false);
        return new ViewHolder(view);
    }

    @Override
    public void onBindViewHolder(CommonAdapter.ViewHolder holder, int position) {
        Object object = null;
        try {
            Object dataSource = field.get(this);
            List list = (List) dataSource;
            object = list.get(position);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

        View view = holder.itemView;
        int len = bindViews.length;
        for (int i = 0; i < len; i++) {
            BindView bindView = bindViews[i];
            if (bindView != null) {
                int viewId = bindView.id();
                View v = view.findViewById(viewId);
                Field f = fields[i];
                try {
                    f.set(this, v);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }

                BindData bindData = bindDatas[i];
                if (bindData != null) {
                    String img_field = bindData.img_field();
                    String text_field = bindData.text_field();
                    if (!img_field.equals("")) {
                        try {
                            assert object != null;
                            Field field = object.getClass().getDeclaredField(img_field);
                            int img_id = (int) field.get(object);
                            ImageView iv = (ImageView) v;
                            iv.setImageBitmap(BitmapFactory.decodeResource(context.getResources(), img_id));
                        } catch (NoSuchFieldException | IllegalAccessException e) {
                            e.printStackTrace();
                        }
                    } else if (!text_field.equals("")) {
                        try {
                            assert object != null;
                            Field field = object.getClass().getDeclaredField(text_field);
                            String text = (String) field.get(object);
                            TextView iv = (TextView) v;
                            iv.setText(text);
                        } catch (NoSuchFieldException | IllegalAccessException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }

        onBind((T) object);
    }

    private int count;
    private BindView[] bindViews;
    private BindData[] bindDatas;
    private Field[] fields;
    private Field field;//这个是data source标记的field

    @Override
    public int getItemCount() {
        Field[] fields = getClass().getDeclaredFields();
        this.fields = fields;
        int len = fields.length;

        bindViews = new BindView[len];
        bindDatas = new BindData[len];

        for (int i = 0; i < len; i++) {
            Field field = fields[i];

            if (count == 0) {
                DataSource dataSource = field.getAnnotation(DataSource.class);
                if (dataSource != null) {
                    this.field = field;
                    try {
                        Object object = field.get(this);
                        List list = (List) object;
                        count = list.size();
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                }
            }

            BindView bindView = field.getAnnotation(BindView.class);
            if (bindView != null) {
                bindViews[i] = bindView;
            }

            BindData bindData = field.getAnnotation(BindData.class);
            if (bindData != null) {
                bindDatas[i] = bindData;
            }
        }

        return count;
    }

    void onBind(T t) {

    }
}


再如果想封装的话,可以封装到xml中去,哈哈

温馨提示:采用了大量反射后,性能会不可避免的下降,望客官慎重考虑。作为一个苛刻的性能优化者,最好不要用反射注解来封装除非你的这一步操作可以为你节省许多的代码或者降低了很大的耦合。