使用RecyclerView实现GridView和ViewPager滑动的分页效果,与上篇GridView分页滑动效果相似。

效果图:

%title插图%num

简单的只能直接贴图全部代码了:

1.主函数代码:

package com.yechaoa.materialdesign.activity.recyclerview;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.AnimationSet;
import android.view.animation.AnimationUtils;
import android.view.animation.LayoutAnimationController;
import android.widget.TextView;
import android.widget.Toast;

import com.yechaoa.materialdesign.R;

import java.util.ArrayList;
import java.util.List;

/**
* 分页滑动效果
*/
public class RecycleViewActivity extends AppCompatActivity {
private PageRecyclerView mRecyclerView = null;
private List<String> dataList = null;
private PageRecyclerView.PageAdapter myAdapter = null;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

setContentView(R.layout.activity_recycleview);

initData();

mRecyclerView = (PageRecyclerView) findViewById(R.id.cusom_swipe_view);
// 设置指示器
mRecyclerView.setIndicator((PageIndicatorView) findViewById(R.id.indicator));
// 设置行数和列数
mRecyclerView.setPageSize(3, 3);
// 设置页间距
mRecyclerView.setPageMargin(30);
//
Animation myAnim = AnimationUtils.loadAnimation(this, R.anim.in_from_right);
myAnim.setFillAfter(true);//android动画结束后停在结束位置

AnimationSet set = new AnimationSet(false);
set.addAnimation(myAnim); //加入动画集合
LayoutAnimationController controller = new LayoutAnimationController(set, 1);
mRecyclerView.setLayoutAnimation(controller);
//
// mRecyclerView.setLayoutManager(new AutoGridLayoutManager(RecycleViewActivity.this,dataList.size()));
// 设置数据
mRecyclerView.setAdapter(myAdapter = mRecyclerView.new PageAdapter(dataList, new PageRecyclerView.CallBack() {
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(RecycleViewActivity.this).inflate(R.layout.item, parent, false);
return new MyHolder(view);
}

@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
((MyHolder)holder).tv.setText(dataList.get(position));
}
}));

}

private void initData() {
dataList = new ArrayList<>();
for (int i = 0; i < 50; i++) {
dataList.add(String.valueOf(i));
}
}

public class MyHolder extends RecyclerView.ViewHolder {

public TextView tv = null;

public MyHolder(View itemView) {
super(itemView);
tv = (TextView) itemView.findViewById(R.id.text);
tv.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(RecycleViewActivity.this, getAdapterPosition() + “”, Toast.LENGTH_SHORT).show();
}
});
tv.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
myAdapter.remove(getAdapterPosition());
return true;
}
});
}
}

}
2.自定义GridLayoutManager 试控件可以自适应内容的高度

import android.content.Context;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
import android.view.View;

/**
*
*
* 重写GridLayoutManager,在{@link
*RecyclerView#setLayoutManager(RecyclerView.LayoutManager)}使用
* 此类替换{@link GridLayoutManager},使{@link RecyclerView}能够自使用内容的高度
*
*/
public class AutoGridLayoutManager extends GridLayoutManager {

private int measuredWidth = 0;
private int measuredHeight = 0;

public AutoGridLayoutManager(Context context, AttributeSet attrs,
int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}

public AutoGridLayoutManager(Context context, int spanCount) {
super(context, spanCount);
}

public AutoGridLayoutManager(Context context, int spanCount,
int orientation, boolean reverseLayout) {
super(context, spanCount, orientation, reverseLayout);
}

@Override
public void onMeasure(RecyclerView.Recycler recycler,
RecyclerView.State state, int widthSpec, int heightSpec) {
//获取count判断,必须要有
int count = state.getItemCount();
if (count > 0) {
if (measuredHeight <= 0) {
View view = recycler.getViewForPosition(0);
if (view != null) {
measureChild(view, widthSpec, heightSpec);
measuredWidth = View.MeasureSpec.getSize(widthSpec);
measuredHeight = view.getMeasuredHeight() * getSpanCount();
}
}
setMeasuredDimension(measuredWidth, measuredHeight);
}else {
super.onMeasure(recycler, state, widthSpec, heightSpec);
}

}

}
3.自定义圆点指示器,ViewPager共用的控件类型:

import android.content.Context;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.View;
import android.widget.LinearLayout;

import java.util.ArrayList;
import java.util.List;

/**
*
* 指示器类,获得此类实例后,可通过{@link PageIndicatorView#initIndicator(int)}方法初始化指
* 示器
*
*/
public class PageIndicatorView extends LinearLayout {

private Context mContext = null;
private int dotSize = 15; // 指示器的大小(dp)
private int margins = 4; // 指示器间距(dp)
private List<View> indicatorViews = null; // 存放指示器

public PageIndicatorView(Context context) {
this(context, null);
}

public PageIndicatorView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}

public PageIndicatorView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}

private void init(Context context) {
this.mContext = context;

setGravity(Gravity.CENTER);
setOrientation(HORIZONTAL);

dotSize = DimensionConvert.dip2px(context, dotSize);
margins = DimensionConvert.dip2px(context, margins);
}

/**
* 初始化指示器,默认选中*页
*
* @param count 指示器数量,即页数
*/
public void initIndicator(int count) {

if (indicatorViews == null) {
indicatorViews = new ArrayList<>();
} else {
indicatorViews.clear();
removeAllViews();
}
View view;
LayoutParams params = new LayoutParams(dotSize, dotSize);
params.setMargins(margins, margins, margins, margins);
for (int i = 0; i < count; i++) {
view = new View(mContext);
view.setBackgroundResource(android.R.drawable.presence_invisible);
addView(view, params);
indicatorViews.add(view);
}
if (indicatorViews.size() > 0) {
indicatorViews.get(0).setBackgroundResource(android.R.drawable.presence_online);
}
}

/**
* 设置选中页
*
* @param selected 页下标,从0开始
*/
public void setSelectedPage(int selected) {
for (int i = 0; i < indicatorViews.size(); i++) {
if (i == selected) {
indicatorViews.get(i).setBackgroundResource(android.R.drawable.presence_online);
} else {
indicatorViews.get(i).setBackgroundResource(android.R.drawable.presence_invisible);
}
}
}

}
4.自定义分页RecyclerView

import android.content.Context;
import android.graphics.Color;
import android.support.v4.view.PagerAdapter;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;

import java.util.List;
import java.util.Objects;

/**
* 横向分页的GridView效果
* 默认为1行,每页3列,如果要自定义行数和列数,请在调用{@link PageRecyclerView#setAdapter(Adapter)}方法前调用
* {@link PageRecyclerView#setPageSize(int, int)}方法自定义行数
*/
public class PageRecyclerView extends RecyclerView {

private Context mContext = null;

private PageAdapter myAdapter = null;

private int shortestDistance; // 超过此距离的滑动才有效
private float downX = 0; // 手指按下的X轴坐标
private float slideDistance = 0; // 滑动的距离
private float scrollX = 0; // X轴当前的位置

private int spanRow = 1; // 行数
private int spanColumn = 3; // 每页列数
private int totalPage = 0; // 总页数
private int currentPage = 1; // 当前页

private int pageMargin = 0; // 页间距

private PageIndicatorView mIndicatorView = null; // 指示器布局

public PageRecyclerView(Context context) {
this(context, null);
}

public PageRecyclerView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}

public PageRecyclerView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
defaultInit(context);
}

// 默认初始化
private void defaultInit(Context context) {
this.mContext = context;
setLayoutManager(new AutoGridLayoutManager(
mContext, spanRow, AutoGridLayoutManager.HORIZONTAL, false));
setOverScrollMode(OVER_SCROLL_NEVER);
}

/**
* 设置行数和每页列数
*
* @param spanRow 行数,<=0表示使用默认的行数
* @param spanColumn 每页列数,<=0表示使用默认每页列数
*/
public void setPageSize(int spanRow, int spanColumn) {
this.spanRow = spanRow <= 0 ? this.spanRow : spanRow;
this.spanColumn = spanColumn <= 0 ? this.spanColumn : spanColumn;
setLayoutManager(new AutoGridLayoutManager(
mContext, this.spanRow, AutoGridLayoutManager.HORIZONTAL, false));
}

/**
* 设置页间距
*
* @param pageMargin 间距(px)
*/
public void setPageMargin(int pageMargin) {
this.pageMargin = pageMargin;
}

/**
* 设置指示器
*
* @param indicatorView 指示器布局
*/
public void setIndicator(PageIndicatorView indicatorView) {
this.mIndicatorView = indicatorView;
}

@Override
protected void onMeasure(int widthSpec, int heightSpec) {
super.onMeasure(widthSpec, heightSpec);
shortestDistance = getMeasuredWidth() / 9;//3
}

@Override
public void setAdapter(Adapter adapter) {
super.setAdapter(adapter);
this.myAdapter = (PageAdapter) adapter;
update();
}

// 更新页码指示器和相关数据
private void update() {
// 计算总页数
int temp = ((int) Math.ceil(myAdapter.dataList.size() / (double) (spanRow * spanColumn)));
if (temp != totalPage) {
mIndicatorView.initIndicator(temp);
// 页码减少且当前页为*后一页
if (temp < totalPage && currentPage == totalPage) {
currentPage = temp;
// 执行滚动
smoothScrollBy(-getWidth(), 0);
}
mIndicatorView.setSelectedPage(currentPage – 1);
totalPage = temp;
}
}

@Override
public boolean onTouchEvent(MotionEvent event) {

switch (event.getAction()) {
case MotionEvent.ACTION_MOVE:
if (currentPage == totalPage && downX – event.getX() > 0) {
return true;
}
break;
case MotionEvent.ACTION_DOWN:
downX = event.getX();
break;
case MotionEvent.ACTION_UP:
slideDistance = event.getX() – downX;
if (Math.abs(slideDistance) > shortestDistance) {
// 滑动距离足够,执行翻页
if (slideDistance > 0) {
// 上一页
currentPage = currentPage == 1 ? 1 : currentPage – 1;
} else {
// 下一页
currentPage = currentPage == totalPage ? totalPage : currentPage + 1;
}
// 修改指示器选中项
mIndicatorView.setSelectedPage(currentPage – 1);
}
// 执行滚动
smoothScrollBy((int) ((currentPage – 1) * getWidth() – scrollX), 0);
return true;
default:
break;
}

return super.onTouchEvent(event);
}

@Override
public void onScrolled(int dx, int dy) {
scrollX += dx;
super.onScrolled(dx, dy);
}

/**
* 数据适配器
*/
public class PageAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {

private List<?> dataList = null;
private CallBack mCallBack = null;
private int itemWidth = 0;
private int itemCount = 0;

/**
* 实例化适配器
*
* @param data
* @param callBack
*/
public PageAdapter(List<?> data, CallBack callBack) {
this.dataList = data;
this.mCallBack = callBack;
itemCount = dataList.size() + spanRow * spanColumn;
}

@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (itemWidth <= 0) {
// 计算Item的宽度
itemWidth = (parent.getWidth() – pageMargin * 2) / spanColumn;
}

RecyclerView.ViewHolder holder = mCallBack.onCreateViewHolder(parent, viewType);

holder.itemView.measure(0, 0);
holder.itemView.getLayoutParams().width = itemWidth;
holder.itemView.getLayoutParams().height = holder.itemView.getMeasuredHeight();

return holder;
}

@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
if (spanColumn == 1) {
// 每个Item距离左右两侧各pageMargin
holder.itemView.getLayoutParams().width = itemWidth + pageMargin * 2;
holder.itemView.setPadding(pageMargin, 0, pageMargin, 0);
} else {
int m = position % (spanRow * spanColumn);
if (m < spanRow) {
// 每页左侧的Item距离左边pageMargin
holder.itemView.getLayoutParams().width = itemWidth + pageMargin;
holder.itemView.setPadding(pageMargin, 0, 0, 0);
} else if (m >= spanRow * spanColumn – spanRow) {
// 每页右侧的Item距离右边pageMargin
holder.itemView.getLayoutParams().width = itemWidth + pageMargin;
holder.itemView.setPadding(0, 0, pageMargin, 0);
} else {
// 中间的正常显示
holder.itemView.getLayoutParams().width = itemWidth;
holder.itemView.setPadding(0, 0, 0, 0);
}
}

if (position < dataList.size()) {
holder.itemView.setVisibility(View.VISIBLE);
mCallBack.onBindViewHolder(holder, position);
} else {
holder.itemView.setVisibility(View.INVISIBLE);
}

}

@Override
public int getItemCount() {
return itemCount;
}

/**
* 删除Item
*
* @param position 位置
*/
public void remove(int position) {
if (position < dataList.size()) {
// 删除数据
dataList.remove(position);
itemCount–;
// 删除Item
notifyItemRemoved(position);
// 更新界面上发生改变的Item
notifyItemRangeChanged(position, currentPage * spanRow * spanColumn);
// 更新页码指示器
update();
}
}

}

public interface CallBack {

/**
* 创建VieHolder
*
* @param parent
* @param viewType
*/
RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType);

/**
* 绑定数据到ViewHolder
*
* @param holder
* @param position
*/
void onBindViewHolder(RecyclerView.ViewHolder holder, int position);

}

}

5.像素转换工具类:

import android.content.Context;

/**
* 像素转换工具
*/
public class DimensionConvert {

/**
* 根据手机的分辨率从 dp 的单位 转成为 px(像素)
*
* @param context
* @param dpValue 要转换的dp值
*/
public static int dip2px(Context context, float dpValue) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (dpValue * scale + 0.5f);
}

/**
* 根据手机的分辨率从 px(像素) 的单位 转成为 dp
*
* @param context
* @param pxValue 要转换的px值
*/
public static int px2dip(Context context, float pxValue) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (pxValue / scale + 0.5f);
}
}
6.布局:

activity_recyclerview.xml

<?xml version=”1.0″ encoding=”utf-8″?>
<LinearLayout xmlns:android=”http://schemas.android.com/apk/res/android”
android:layout_width=”match_parent”
android:orientation=”vertical”
android:layout_height=”match_parent”>

<com.yechaoa.materialdesign.activity.recyclerview.PageRecyclerView
android:id=”@+id/cusom_swipe_view”
android:layout_width=”match_parent”
android:layout_height=”match_parent” />

<com.yechaoa.materialdesign.activity.recyclerview.PageIndicatorView
android:id=”@+id/indicator”
android:layout_width=”match_parent”
android:layout_marginBottom=”20dp”
android:layout_height=”wrap_content”
android:layout_gravity=”bottom”/>

</LinearLayout>
item.xml

<?xml version=”1.0″ encoding=”utf-8″?>
<RelativeLayout xmlns:android=”http://schemas.android.com/apk/res/android”
android:layout_width=”match_parent”
android:layout_height=”match_parent”>

<ImageView
android:layout_centerHorizontal=”true”
android:src=”@drawable/topinfo_ban_bg”
android:layout_width=”wrap_content”
android:layout_height=”150dp”/>

<TextView
android:id=”@+id/text”
android:layout_centerHorizontal=”true”
android:layout_width=”match_parent”
android:layout_height=”150dp”
android:text=”MUSIC”
android:textColor=”@color/orange”
android:gravity=”center” />

</RelativeLayout>