作为listview的升级版,这个技能是绝对值得点上的
recyclerview需要导包:
compile 'com.android.support:recyclerview-v7:26.0.+'
布局:
<android.support.v7.widget.RecyclerView
//可以设置rv的方向
android:orientation="Vertical"
android:id="@+id/rv"
android:layout_width="match_parent"
android:layout_height="300dp">
</android.support.v7.widget.RecyclerView>
创建adapter继承于RecyclerView.adapter<holder>
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.Holder> {
List<String> datas;
public MyAdapter(List<String> datas) {
this.datas = datas;
}
@Override
public int getItemCount() {
return datas.size();
}
//返回holder类
@Override
public Holder onCreateViewHolder(ViewGroup parent, int viewType) {
return new Holder(View.inflate(parent.getContext(),R.layout.tab_item,null));
}
//绑定数据
@Override
public void onBindViewHolder(Holder holder, int position) {
//通过该方法来展示不同类型的item
int type = getItemViewType(position);
switch (type){
case 1:
holder.iv.setImageResource(R.mipmap.ic_launcher);
break;
case 2:
holder.iv.setImageResource(R.mipmap.ic_launcher_round);
break;
}
holder.tv.setText(datas.get(position));
//顺便一提,rv并没有lv中setitemclick()的监听方法,需要监听item事件可以在这里通过
//view.onclick(内部类)的方式设置事件监听
}
//通过该方法可以设置在rv中展示不同类型的item
@Override
public int getItemViewType(int position) {
if ((position%2)==1){
return 1;
}else{
return 2;
}
}
//创建holder类继承于RecyclerView.ViewHolder,并通过传入的view来初始化子控件
public class Holder extends RecyclerView.ViewHolder {
ImageView iv;
TextView tv;
public Holder(View itemView) {
super(itemView);
setView(itemView);
}
public void setView(View view){
iv = view.findViewById(R.id.tab_imageview);
tv = view.findViewById(R.id.tab_textview);
}
}
}
展示:
//创建随机长度的数据
private void initData() {
datas=new ArrayList<>();
for (int i = 0; i < 100; i++) {
int times = new Random().nextInt(30);
StringBuilder sb =new StringBuilder();
for (int j = 0; j < times; j++) {
sb.append("哈哈");
}
datas.add(sb.toString()+i);
}
}
private void show() {
MyAdapter ada = new MyAdapter(datas);
rv.setAdapter(ada);
//线性布局
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
//可以设置方向
linearLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);
rv.setLayoutManager(linearLayoutManager);
rv.setLayoutManager(new GridLayoutManager(this,3));
//瀑布流布局
rv.setLayoutManager(new StaggeredGridLayoutManager(3,StaggeredGridLayoutManager.VERTICAL));
}
设置recyclerview的分割线
方法一:
在item布局中设置margin
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="50dp"
android:orientation="vertical"
>
<ImageView
android:id="@+id/tab_imageview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:layout_marginTop="10dp"
android:background="#0000ff"
/>
<TextView
android:id="@+id/tab_textview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginBottom="10dp"
//在recyclerview中设置不同的颜色
android:background="#0000ff"
android:textColor="@drawable/selector"
/>
</LinearLayout>
调用mRecyclerView.addItemDecoration();不过,需要手动实现分割线
//实现分割线
public class DivLineDemo extends RecyclerView.ItemDecoration{
//会在子类被绘制之前调用,所以会在item图层下面
@Override
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
super.onDraw(c, parent, state);
}
//在子类被绘制之后绘制,所以绘制的图形会覆盖item
@Override
public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
super.onDrawOver(c, parent, state);
}
//像padding和margin,可以通过outRect.set()为每个Item设置一定的偏移量,主要用于绘制Decorator。
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
//每个item距离下方的item有3dp的分割线,实现了类似margin的效果
outRect.set(0,0,0,3);
}
}
网络找来的一份线性布局的分割线demo
/**
* 调用了系统的分割线,可以在样式中自定义分隔线的样式
*/
-------------------------------------------------------------------------------------
public class DivLine extends RecyclerView.ItemDecoration {
private static final int[] ATTRS = new int[]{
android.R.attr.listDivider
};
public static final int HORIZONTAL_LIST = LinearLayoutManager.HORIZONTAL;
public static final int VERTICAL_LIST = LinearLayoutManager.VERTICAL;
private Drawable mDivider;
private int mOrientation;
private DivLine(){}
public DivLine(Context context, int orientation) {
final TypedArray a = context.obtainStyledAttributes(ATTRS);
mDivider = a.getDrawable(0);
a.recycle();
setOrientation(orientation);
}
public void setOrientation(int orientation) {
if (orientation != HORIZONTAL_LIST && orientation != VERTICAL_LIST) {
throw new IllegalArgumentException("invalid orientation");
}
mOrientation = orientation;
}
@Override
public void onDraw(Canvas c, RecyclerView parent) {
if (mOrientation == VERTICAL_LIST) {
drawVertical(c, parent);
} else {
drawHorizontal(c, parent);
}
}
public void drawVertical(Canvas c, RecyclerView parent) {
final int left = parent.getPaddingLeft();
final int right = parent.getWidth() - parent.getPaddingRight();
final int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) {
final View child = parent.getChildAt(i);
android.support.v7.widget.RecyclerView v = new android.support.v7.widget.RecyclerView(parent.getContext());
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
.getLayoutParams();
final int top = child.getBottom() + params.bottomMargin;
final int bottom = top + mDivider.getIntrinsicHeight();
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(c);
}
}
public void drawHorizontal(Canvas c, RecyclerView parent) {
final int top = parent.getPaddingTop();
final int bottom = parent.getHeight() - parent.getPaddingBottom();
final int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) {
final View child = parent.getChildAt(i);
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
.getLayoutParams();
final int left = child.getRight() + params.rightMargin;
final int right = left + mDivider.getIntrinsicHeight();
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(c);
}
}
@Override
public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) {
if (mOrientation == VERTICAL_LIST) {
outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());
} else {
outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0);
}
}
}
-----------------------------------------------------------------------------------
在style中自定义分割线的样式
<resources>
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
//自定义分割线样式
<item name="android:listDivider">@drawable/divider_bg</item>
</style>
</resources>
创建一个shape位图
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<gradient
android:centerColor="#ff00ff00"
android:endColor="#ff0000ff"
android:startColor="#ffff0000"
android:type="linear"/>
<size android:height="4dp"/>
</shape>
转自网络,gridlayoutmanger的分割线
----------------------------------------------------------------------------------------
public class DividerGridItemDecoration extends RecyclerView.ItemDecoration
{
private static final int[] ATTRS = new int[] { android.R.attr.listDivider };
private Drawable mDivider;
public DividerGridItemDecoration(Context context)
{
final TypedArray a = context.obtainStyledAttributes(ATTRS);
mDivider = a.getDrawable(0);
a.recycle();
}
@Override
public void onDraw(Canvas c, RecyclerView parent, State state)
{
drawHorizontal(c, parent);
drawVertical(c, parent);
}
private int getSpanCount(RecyclerView parent)
{
// 列数
int spanCount = -1;
LayoutManager layoutManager = parent.getLayoutManager();
if (layoutManager instanceof GridLayoutManager)
{
spanCount = ((GridLayoutManager) layoutManager).getSpanCount();
} else if (layoutManager instanceof StaggeredGridLayoutManager)
{
spanCount = ((StaggeredGridLayoutManager) layoutManager)
.getSpanCount();
}
return spanCount;
}
public void drawHorizontal(Canvas c, RecyclerView parent)
{
int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++)
{
final View child = parent.getChildAt(i);
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
.getLayoutParams();
final int left = child.getLeft() - params.leftMargin;
final int right = child.getRight() + params.rightMargin
+ mDivider.getIntrinsicWidth();
final int top = child.getBottom() + params.bottomMargin;
final int bottom = top + mDivider.getIntrinsicHeight();
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(c);
}
}
public void drawVertical(Canvas c, RecyclerView parent)
{
final int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++)
{
final View child = parent.getChildAt(i);
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
.getLayoutParams();
final int top = child.getTop() - params.topMargin;
final int bottom = child.getBottom() + params.bottomMargin;
final int left = child.getRight() + params.rightMargin;
final int right = left + mDivider.getIntrinsicWidth();
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(c);
}
}
private boolean isLastColum(RecyclerView parent, int pos, int spanCount,
int childCount)
{
LayoutManager layoutManager = parent.getLayoutManager();
if (layoutManager instanceof GridLayoutManager)
{
if ((pos + 1) % spanCount == 0)// 如果是最后一列,则不需要绘制右边
{
return true;
}
} else if (layoutManager instanceof StaggeredGridLayoutManager)
{
int orientation = ((StaggeredGridLayoutManager) layoutManager)
.getOrientation();
if (orientation == StaggeredGridLayoutManager.VERTICAL)
{
if ((pos + 1) % spanCount == 0)// 如果是最后一列,则不需要绘制右边
{
return true;
}
} else
{
childCount = childCount - childCount % spanCount;
if (pos >= childCount)// 如果是最后一列,则不需要绘制右边
return true;
}
}
return false;
}
private boolean isLastRaw(RecyclerView parent, int pos, int spanCount,
int childCount)
{
LayoutManager layoutManager = parent.getLayoutManager();
if (layoutManager instanceof GridLayoutManager)
{
childCount = childCount - childCount % spanCount;
if (pos >= childCount)// 如果是最后一行,则不需要绘制底部
return true;
} else if (layoutManager instanceof StaggeredGridLayoutManager)
{
int orientation = ((StaggeredGridLayoutManager) layoutManager)
.getOrientation();
// StaggeredGridLayoutManager 且纵向滚动
if (orientation == StaggeredGridLayoutManager.VERTICAL)
{
childCount = childCount - childCount % spanCount;
// 如果是最后一行,则不需要绘制底部
if (pos >= childCount)
return true;
} else
// StaggeredGridLayoutManager 且横向滚动
{
// 如果是最后一行,则不需要绘制底部
if ((pos + 1) % spanCount == 0)
{
return true;
}
}
}
return false;
}
@Override
public void getItemOffsets(Rect outRect, int itemPosition,
RecyclerView parent)
{
int spanCount = getSpanCount(parent);
int childCount = parent.getAdapter().getItemCount();
if (isLastRaw(parent, itemPosition, spanCount, childCount))// 如果是最后一行,则不需要绘制底部
{
outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0);
} else if (isLastColum(parent, itemPosition, spanCount, childCount))// 如果是最后一列,则不需要绘制右边
{
outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());
} else
{
outRect.set(0, 0, mDivider.getIntrinsicWidth(),
mDivider.getIntrinsicHeight());
}
}
}
----------------------------------------------------------------------------------------
Recycler添加头布局和脚布局
public class HeaderFooterAdapter extends RecyclerView.Adapter<HeaderFooterAdapter.Holder> {
//头布局和脚布局,并添加相应的get set方法
View header;
View footer;
public void setHeader(View view) {
header = view;
}
public void setFooter(View view) {
footer = view;
}
public View getHeader() {
return header;
}
public View getFooter() {
return footer;
}
//普通数据
List<String> datas;
public HeaderFooterAdapter(List<String> datas) {
this.datas = datas;
}
//通过判断头布局和脚布局是否为空,返回的count不同
@Override
public int getItemCount() {
int num = datas.size();
if (header != null)
num++;
if (footer != null)
num++;
return num++;
}
//返回holder类,头布局和脚布局直接返回
@Override
public Holder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == 8)
return new Holder(footer);
if (viewType == 9) {
return new Holder(header);
}
return new Holder(View.inflate(parent.getContext(), R.layout.tab_item, null));
}
//绑定数据
@Override
public void onBindViewHolder(Holder holder, int position) {
//通过该方法来展示不同类型的item
int type = getItemViewType(position);
switch (type) {
case 1:
holder.iv.setImageResource(R.mipmap.ic_launcher);
if (header != null)
holder.tv.setText(datas.get(position - 1));
else
holder.tv.setText(datas.get(position));
break;
case 2:
holder.iv.setImageResource(R.mipmap.ic_launcher_round);
if (header != null)
holder.tv.setText(datas.get(position - 1));
else
holder.tv.setText(datas.get(position));
break;
case 8:
break;
case 9:
break;
}
}
//通过该方法可以设置在rv中展示不同类型的item
@Override
public int getItemViewType(int position) {
if (header != null) {
if (position == 0) {
//9的意思是头布局
return 9;
}
}
if (footer != null) {
//footer不为空,且是最后一位
if (position == getItemCount() - 1) {
//8的意思是脚布局
return 8;
}
}
if ((position % 2) == 1) {
return 1;
} else {
return 2;
}
}
//创建holder类继承于RecyclerView.ViewHolder,并通过传入的view来初始化子控件
public class Holder extends RecyclerView.ViewHolder {
ImageView iv;
TextView tv;
public Holder(View itemView) {
super(itemView);
//如果是头布局和脚布局,则直接返回
if (itemView == header)
return;
if (itemView == footer)
return;
setView(itemView);
}
public void setView(View view) {
iv = view.findViewById(R.id.tab_imageview);
tv = view.findViewById(R.id.tab_textview);
}
}
}
自定义布局管理器
对于各种奇葩需求,有时候我们只能自己上了,还好rv中可以自定义layoutmanger
public class MyLayoutManager extends RecyclerView.LayoutManager {
final MyLayoutManager self = this;
//默认的布局的宽高参数
@Override
public RecyclerView.LayoutParams generateDefaultLayoutParams() {
return new RecyclerView.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
}
//布局的入口,这里我们做一个斜着对齐的linearlayoutmanager
@Override
public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
super.onLayoutChildren(recycler, state);
//该方法的作用是分离所有的item的视图,将原本rv中item全部解绑
detachAndScrapAttachedViews(recycler);
int offsetX = 0;
int offsetY = 0;
//获取item个数的方法getItemCount()
for (int i = 0; i < getItemCount(); i++) {
// 根据position获取一个碎片view,可以从回收的view中获取,也可能新构造一个
View scrap = recycler.getViewForPosition(i);
//遍历获取总高度,在处理滚动事件的时候有用
totalHeight+=scrap.getHeight();
//因为已经detach所以可以重新添加
addView(scrap);
// 计算此碎片view包含边距的尺寸
measureChildWithMargins(scrap, 0, 0);
int width = getDecoratedMeasuredWidth(scrap); // 获取此碎片view包含边距和装饰的宽度width
int height = getDecoratedMeasuredHeight(scrap); // 获取此碎片view包含边距和装饰的高度height
// 布局到RecyclerView容器中,所有的计算都是为了得出任意position的item的边界来布局
layoutDecorated(scrap, offsetX , offsetY, offsetX + width, offsetY + height);
offsetX += width;
offsetY += height;
if (offsetX+width>=getWidth())
offsetX=0;
}
}
//处理垂直方向上的滑动
@Override
public boolean canScrollVertically() {
return true;
}
//总偏移量
int verticalScrollOffset;
//所有item的总高度
int totalHeight;
@Override
public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) {
//列表向下滚动dy为正,列表向上滚动dy为负,这点与Android坐标系保持一致。
//实际滑动的距离
int travel = dy;
//如果滑动到最顶部,则设置为0
if (verticalScrollOffset + dy < 0) {
travel = 0;
}
//如果滑动到了最底部
else if (verticalScrollOffset + dy > totalHeight - getHeight()) {
travel = totalHeight - getHeight()
- verticalScrollOffset;
}
//将竖直方向的偏移量+travel
verticalScrollOffset += travel;
// 调用该方法通知view在y方向上移动指定距离,滑动和方向与item滚动的方向相反
offsetChildrenVertical(-travel);
return travel;
}
}
recyclerview加载多重item布局
//可惜不能用泛型,需要自己强转
public class ParentRecordAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
List<JsonParentRecord> mDatas = new ArrayList<>();
public void addDatas(List<JsonParentRecord> datas) {
mDatas.addAll(0,datas);
}
//通过判断type返回不同的viewholder
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == 0) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.directormailbox_get_item, parent, false);
ViewHolder0 viewHolder = new ViewHolder0(view);
return viewHolder;
} else {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.directormailbox_send_item, parent, false);
ViewHolder1 viewHolder = new ViewHolder1(view);
return viewHolder;
}
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
//判断类型,并进行强转
if (holder instanceof ViewHolder0) {
//todo
} else if (holder instanceof ViewHolder1) {
//todo
}
}
@Override
public int getItemCount() {
return mDatas.size();
}
public List<JsonParentRecord> getDatas() {
return mDatas;
}
//在这里返回不同类型的type
@Override
public int getItemViewType(int position) {
//0为学生,在左边
return mDatas.get(position).getType();
}
//创建多个viewholder类
class ViewHolder0 extends RecyclerView.ViewHolder {
//type0
TextView tv_content_stu;
TextView tv_time_stu;
public ViewHolder0(View layout) {
super(layout);
tv_content_stu = layout.findViewById(R.id.textview_directormailbox_get_coonten);
tv_time_stu = layout.findViewById(R.id.textview_directormailbox_get_time);
}
}
class ViewHolder1 extends RecyclerView.ViewHolder {
//type1
TextView tv_content_parents;
TextView tv_time_parents;
public ViewHolder1(View layout) {
super(layout);
tv_content_parents = layout.findViewById(R.id.textview_directormailbox_send_coonten);
tv_time_parents = layout.findViewById(R.id.textview_directormailbox_send_time);
layout.findViewById(R.id.textview_directormailbox_send_nimingshifou).setVisibility(View.GONE);
layout.findViewById(R.id.textview_directormailbox_send_biaoshi).setVisibility(View.GONE);
}
}
}