EasyRecyclerView扩展的自定义RecyclerView控件

介绍:

recycler3.gif

将开发中常用的RecyclerView的各种需求封装进库。提升开发效率。
重点在Adapter与viewholder的封装。他们之间彻底解耦。
adapter工作更少,仅负责业务逻辑。所以如果你使用mvp架构可以放进presenter层。
viewholder负责View展示与Adapter没有任何耦合,将可以到处复用。并不会影响运行效率。
并且adapter支持数据管理,Header与Footer添加,加载更多。没有更多。加载错误
使用了部分Malinskiy/SuperRecyclerView的代码,将更多功能交给了adapter实现。

项目地址:

https://github.com/Jude95/EasyRecyclerView

引用

compile 'com.jude:easyrecyclerview:4.4.0'

当然还要添加:

compile 'com.android.support:recyclerview-v7:24.2.0'

具体使用

布局引用:

<com.jude.easyrecyclerview.EasyRecyclerView
  android:id="@+id/recyclerView"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  app:layout_empty="@layout/view_empty"
  app:layout_progress="@layout/view_progress"
  app:layout_error="@layout/view_error"
  app:recyclerClipToPadding="true"
  app:recyclerPadding="8dp"
  app:recyclerPaddingTop="8dp"
  app:recyclerPaddingBottom="8dp"
  app:recyclerPaddingLeft="8dp"
  app:recyclerPaddingRight="8dp"
  app:scrollbarStyle="insideOverlay"//insideOverlay or insideInset or outsideOverlay or outsideInset
  app:scrollbars="none"//none or vertical or horizontal
  />

所有属性都不是必须。 注意EasyRecyclerView本质并不是一个RecyclerView

设置空白View&加载View&错误View

xml中

app:layout_empty="@layout/view_empty"
app:layout_progress="@layout/view_progress"
app:layout_error="@layout/view_error"

代码中

void setEmptyView(View emptyView)
void setProgressView(View progressView)
void setErrorView(View errorView)

然后可以随时显示他们

void showEmpty()
void showProgress()  
void showError()  
void showRecycler()

RecyclerArrayAdapter

这个Adapter与本RecyclerView没有任何耦合。你可以使用其他adapter。也可以把本adapter用于其他RecyclerView

整合了数据增删的功能

void add(T object);
void addAll(Collection<? extends T> collection);
void addAll(T ... items);
void insert(T object, int index);
void remove(T object)
void clear()
void sort(Comparator<? super T> comparator)

整合的Header与Footer的实现

void addHeader(ItemView view)
void addFooter(ItemView view)

ItemView不是view而是view生成器
对应Adapter的onCreate与onBind方法,所以onCreate后会多次onBind。
建议数据加载完毕后再add。onCreate里初始化UI。不使用onBind。

public interface ItemView {
     View onCreateView(ViewGroup parent);
     void onBindView(View itemView);
}

## Header与Footer完美适配

LinearLayoutManager,GridLayoutManager,StaggeredGridLayoutManager
在GridLayoutManager模式中需额外加一句
```java
//make adapter obtain a LookUp for LayoutManager,param is maxSpan。
gridLayoutManager.setSpanSizeLookup(adapter.obtainGridSpanSizeLookUp(2));

整合OnItemClickListener与OnItemLongClickListener

adapter.setOnItemClickListener(new RecyclerArrayAdapter.OnItemClickListener() {
    @Override
    public void onItemClick(int position) {
        //position不包含Header
    }
});

adapter.setOnItemLongClickListener(new RecyclerArrayAdapter.OnItemLongClickListener() {
    @Override
    public boolean onItemLongClick(int position) {
        return true;
    }
});

与在ViewHolder中给itemView设置OnClickListener等效。若重复设置ViewHolder中的设置会被替换。
如果在RecyclerView布局以后再设置。需要再使用'notifyDataSetChange()'。

下面的功能是在adapter最后添加一个footer。来显示各种状态。

加载更多

void setMore(final int res,OnMoreListener listener);
void setMore(final View view,OnMoreListener listener);

注意一定当添加0条数据或null时,会结束加载更多,显示没有更多。
也可以在最后一页手动调用adapter.stopMore();

加载错误

void setError(final int res,OnErrorListener listener)
void setError(final View view,OnErrorListener listener)
  1. adapter.pauseMore()暂停加载更多,显示错误View。
  2. 暂停时如果再次添加数据。自动恢复加载更多。
  3. 当错误View再次被显示时。会恢复成加载更多view。并回掉加载更多;
  4. adapter.resumeMore()继续加载更多,显示加载更多View,并立即回调加载更多。
  5. 比如你可以给错误View设置点击重试。点击调用resumeMore。

没有更多
在adapter里设置,当停止加载后就会显示在最后一个。

void setNoMore(final int res,OnNoMoreListener listener)
void setNoMore(final View view,OnNoMoreListener listener)

BaseViewHolder<M>

这个ViewHolder将每个item与adapter解耦。adapter只管实例化对应ViewHolder.每个Item的view生成,findviewbyid,UI修改都由viewHolder自己管理。
列如:

public class PersonViewHolder extends BaseViewHolder {
    private TextView mTv_name;
    private SimpleDraweeView mImg_face;
    private TextView mTv_sign;

    public PersonViewHolder(ViewGroup parent) {
        super(parent,R.layout.item_person);
        mTv_name = $(R.id.person_name);
        mTv_sign = $(R.id.person_sign);
        mImg_face = $(R.id.person_face);
    }

    @Override
    public void setData(final Person person){
        mTv_name.setText(person.getName());
        mTv_sign.setText(person.getSign());
        mImg_face.setImageURI(Uri.parse(person.getFace()));
    }
}

-----------------------------------------------------------------------

public class PersonAdapter extends RecyclerArrayAdapter {
    public PersonAdapter(Context context) {
        super(context);
    }

    @Override
    public BaseViewHolder OnCreateViewHolder(ViewGroup parent, int viewType) {
        return new PersonViewHolder(parent);
    }
}

Decoration

这里提供了2种常用Decoration供大家使用。
DividerDecoration
通常用在LinearLayoutManager的情况下。在item之间添加分割线。

DividerDecoration itemDecoration = new DividerDecoration(Color.GRAY, Util.dip2px(this,0.5f), Util.dip2px(this,72),0);//颜色 & 高度 & 左边距 & 右边距
itemDecoration.setDrawLastItem(true);//有时候你不想让最后一个item有分割线,默认true.
itemDecoration.setDrawHeaderFooter(false);//是否对Header于Footer有效,默认false.
recyclerView.addItemDecoration(itemDecoration);

这是效果:
222

SpaceDecoration
通常用于GridLayoutManager和StaggeredGridLayoutManager。在View之间添加间距。

SpaceDecoration itemDecoration = new SpaceDecoration((int) Utils.convertDpToPixel(8,this));//参数是距离宽度
itemDecoration.setPaddingEdgeSide(true);//是否为左右2边添加padding.默认true.
itemDecoration.setPaddingStart(true);//是否在给第一行的item添加上padding(不包含header).默认true.
itemDecoration.setPaddingHeaderFooter(false);//是否对Header于Footer有效,默认false.
recyclerView.addItemDecoration(itemDecoration);

这是效果:

另外:

111

虽然与我的库没什么关系,但很多人在问就写一下吧。item的水波纹效果
在你item的View加上这一条属性:

android:foreground="?android:attr/selectableItemBackground"

就好了...

实战:

布局文件:

main_activity.xml




 

view_more.xml



 
 

view_nomore.xml



 

具体步骤:

mainactivity-->setadapter-->viewholder-->picture

mainactivity

public class MainActivity extends AppCompatActivity {
    private EasyRecyclerView recyclerView;
    private ImageAdapter adapter;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        recyclerView = (EasyRecyclerView) findViewById(R.id.recyclerView);
        recyclerView.setLayoutManager(new StaggeredGridLayoutManager(2,StaggeredGridLayoutManager.VERTICAL));
        recyclerView.setAdapter(adapter = new ImageAdapter(this));

        //添加headrd
//        adapter.addHeader(new RecyclerArrayAdapter.ItemView() {
//            @Override
//            public View onCreateView(ViewGroup parent) {
//                RollPagerView header = new RollPagerView(MainActivity.this);
//                header.setHintView(new ColorPointHintView(MainActivity.this, Color.YELLOW,Color.GRAY));
//                header.setHintPadding(0, 0, 0, (int) convertDpToPixel(8, MainActivity.this));
//                header.setPlayDelay(2000);
//                header.setLayoutParams(new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, (int) Utils.convertDpToPixel(200, StaggeredGridActivity.this)));
//                header.setAdapter(new BannerAdapter(MainActivity.this));
//                return header;
//            }
//
//            @Override
//            public void onBindView(View headerView) {
//
//            }
//        });

        //添加边框
        SpaceDecoration itemDecoration = new SpaceDecoration((int) convertDpToPixel(8,this));
        itemDecoration.setPaddingEdgeSide(true);
        itemDecoration.setPaddingStart(true);
        itemDecoration.setPaddingHeaderFooter(false);
        recyclerView.addItemDecoration(itemDecoration);

        //更多加载
        adapter.setMore(R.layout.view_more, new RecyclerArrayAdapter.OnMoreListener() {
            @Override
            public void onMoreShow() {
                addData();
            }

            @Override
            public void onMoreClick() {

            }
        });
        //移除加载更多页脚
        //adapter.stopMore();
        //没有更多
        adapter.setNoMore(R.layout.view_nomore);

        //写刷新事件
        recyclerView.setRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
            @Override
            public void onRefresh() {
                recyclerView.postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        //adapter.clear();
                       // adapter.addAll(DataProvider.getPictures(0));
                    }
                },1000);
            }
        });

        //点击事件
        adapter.setOnItemClickListener(new RecyclerArrayAdapter.OnItemClickListener() {
            @Override
            public void onItemClick(int position) {
                //position不包含Header
                Toast.makeText(getApplicationContext(),
                     position+"", Toast.LENGTH_LONG).show();
            }
        });

        adapter.setOnItemLongClickListener(new RecyclerArrayAdapter.OnItemLongClickListener() {
            @Override
            public boolean onItemLongClick(int position) {
                Toast.makeText(getApplicationContext(),
                        position+"", Toast.LENGTH_LONG).show();
                return true;
            }
        });
        addData();
    }

    private void addData(){
        recyclerView.postDelayed(new Runnable() {
            @Override
            public void run() {
                adapter.addAll(DataProvider.getPictures(0));
            }
        },1000);
    }

    public static float convertDpToPixel(float dp, Context context){
        Resources resources = context.getResources();
        DisplayMetrics metrics = resources.getDisplayMetrics();
        float px = dp * (metrics.densityDpi / 160f);
        return px;
    }
}

ImageAdapter

public class ImageAdapter extends RecyclerArrayAdapter {
    public ImageAdapter(Context context) {
        super(context);
    }

    @Override
    public BaseViewHolder OnCreateViewHolder(ViewGroup parent, int viewType) {
        return new ImageViewHolder(parent);
    }
}

ImageViewHolder

public class ImageViewHolder extends BaseViewHolder {
    ImageView imgPicture;

    public ImageViewHolder(ViewGroup parent) {
        super(new ImageView(parent.getContext()));
        imgPicture = (ImageView) itemView;
        imgPicture.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
        imgPicture.setScaleType(ImageView.ScaleType.CENTER_CROP);
    }

    @Override
    public void setData(Picture data) {
        ViewGroup.LayoutParams params = imgPicture.getLayoutParams();

        DisplayMetrics dm = getContext().getResources().getDisplayMetrics();
        int width = dm.widthPixels/2;//宽度为屏幕宽度一半
        int height = data.getHeight()*width/data.getWidth();//计算View的高度

        params.height = height;
        imgPicture.setLayoutParams(params);
        //imgPicture.setImageResource(R.mipmap.ic_launcher);
        Glide.with(getContext())
                .load(data.getSrc()+"?imageView2/0/w/"+ width)
                .into(imgPicture);
    }
}

Picture

public class Picture {
    int width;
    int height;
    String src;

    public Picture(int width, int height, String src) {
        this.width = width;
        this.height = height;
        this.src = src;
    }

    public int getWidth() {
        return width;
    }

    public void setWidth(int width) {
        this.width = width;
    }

    public int getHeight() {
        return height;
    }

    public void setHeight(int height) {
        this.height = height;
    }

    public String getSrc() {
        return src;
    }

    public void setSrc(String src) {
        this.src = src;
    }
}

DataProvider

public class DataProvider {

    static final Picture[] VIRTUAL_PICTURE = {
            new Picture(566,800,"http://o84n5syhk.bkt.clouddn.com/57154327_p0.png"),
            new Picture(2126,1181,"http://o84n5syhk.bkt.clouddn.com/57180221_p0.jpg"),
            new Picture(1142,800,"http://o84n5syhk.bkt.clouddn.com/57174070_p0.jpg"),
            new Picture(550,778,"http://o84n5syhk.bkt.clouddn.com/57166531_p0.jpg"),
            new Picture(1085,755,"http://o84n5syhk.bkt.clouddn.com/57151022_p0.jpg"),
            new Picture(656,550,"http://o84n5syhk.bkt.clouddn.com/57172236_p0.jpg"),
            new Picture(1920,938,"http://o84n5syhk.bkt.clouddn.com/57174564_p0.jpg"),
            new Picture(1024,683,"http://o84n5syhk.bkt.clouddn.com/57156832_p0.jpg"),
            new Picture(723,1000,"http://o84n5syhk.bkt.clouddn.com/57151474_p0.png"),
            new Picture(2000,1667,"http://o84n5syhk.bkt.clouddn.com/57156623_p0.png"),
    };
    public static ArrayList getPictures(int page){
        ArrayList arrayList = new ArrayList<>();
        for (int i = 0; i < VIRTUAL_PICTURE.length; i++) {
            arrayList.add(VIRTUAL_PICTURE[i]);
        }
        return arrayList;
    }
}

测试:

my.gif

使用注意事项:

由于ViewHolder使用的是ViewGroup作为parent
所以布局文件需要在最外面放置一个相对布局作为父容器。
类似: