今天开始讲RecycleView的系列教程。分割线,分组,局部刷新,动态添加,缓存原理,抖音效果,瀑布流。嵌套,动画等等
从月薪3000到年薪60万。从专科生到深圳一线大厂。关注我就能达到大师级水平,这话我终于敢说了, 年薪60万不是梦!
请问:简书怎么可以把代码格式调整?我贴出来换格式了。你们直接去Github下载工程!
功能:
支持无限循环
支持画廊效果
支持中间放大并定格
支持方向切换
支持回弹效果
效果分析
1.优先实现圆弧效果
2.然后实现放大效果
3.实现定格效果
原理:使用recyclerview的话你只需通过setAdapter方法改变数据,setLayoutManager方法改变样式即可,
重写LayoutManager。我们自定义ViewPagerLayoutManager extends LinearLayoutManager 然后二次封装:
/**
* 可缩放的弧形菜单
*/
public class CircleScaleLayoutManagerextends ViewPagerLayoutManager {
圆弧效果
给每个item设置旋转角度
itemView.setRotation(targetOffset);
@Override
protected void setItemViewProperty(View itemView, float targetOffset) {
switch (gravity) {
case RIGHT:
case TOP:
if (flipRotate) {
itemView.setRotation(targetOffset);
}else {
itemView.setRotation(360 - targetOffset);
}
break;
case LEFT:
case BOTTOM:
default:
if (flipRotate) {
itemView.setRotation(360 - targetOffset);
}else {
itemView.setRotation(targetOffset);
}
break;
}
}
然后实现放大效果 itemView.setScaleX(scale);
@Override
protected void setItemViewProperty(View itemView, float targetOffset) {
float scale =1f;
switch (gravity) {
case RIGHT:
case TOP:
if (flipRotate) {
itemView.setRotation(targetOffset);
if (targetOffset -angleInterval) {
float diff = Math.abs(Math.abs(itemView.getRotation() -angleInterval) -angleInterval);
scale = (centerScale -1f) / -angleInterval * diff +centerScale;
}
}else {
itemView.setRotation(360 - targetOffset);
if (targetOffset -angleInterval) {
float diff = Math.abs(Math.abs(360 - itemView.getRotation() -angleInterval) -angleInterval);
scale = (centerScale -1f) / -angleInterval * diff +centerScale;
}
}
break;
case LEFT:
case BOTTOM:
default:
if (flipRotate) {
itemView.setRotation(360 - targetOffset);
if (targetOffset -angleInterval) {
float diff = Math.abs(Math.abs(360 - itemView.getRotation() -angleInterval) -angleInterval);
scale = (centerScale -1f) / -angleInterval * diff +centerScale;
}
}else {
itemView.setRotation(targetOffset);
if (targetOffset -angleInterval) {
float diff = Math.abs(Math.abs(itemView.getRotation() -angleInterval) -angleInterval);
scale = (centerScale -1f) / -angleInterval * diff +centerScale;
}
}
break;
}
itemView.setScaleX(scale);
itemView.setScaleY(scale);
}
3.实现定格效果:通过监听RecyclerView的滚动方法
private final RecyclerView.OnScrollListenermScrollListener =
new RecyclerView.OnScrollListener() {
boolean mScrolled =false;
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
final ViewPagerLayoutManager layoutManager =
(ViewPagerLayoutManager) recyclerView.getLayoutManager();
final ViewPagerLayoutManager.OnPageChangeListener onPageChangeListener =
layoutManager.onPageChangeListener;
if (onPageChangeListener !=null) {
onPageChangeListener.onPageScrollStateChanged(newState);
}
if (newState == RecyclerView.SCROLL_STATE_IDLE &&mScrolled) {
mScrolled =false;
if (!snapToCenter) {
snapToCenter =true;
snapToCenterView(layoutManager, onPageChangeListener);
}else {
snapToCenter =false;
}
}
}
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
if (dx !=0 || dy !=0) {
mScrolled =true;
}
}
};
@Override
public boolean onFling(int velocityX, int velocityY) {
ViewPagerLayoutManager layoutManager = (ViewPagerLayoutManager)mRecyclerView.getLayoutManager();
if (layoutManager ==null) {
return false;
}
RecyclerView.Adapter adapter =mRecyclerView.getAdapter();
if (adapter ==null) {
return false;
}
if (!layoutManager.getInfinite() &&
(layoutManager.mOffset == layoutManager.getMaxOffset()
|| layoutManager.mOffset == layoutManager.getMinOffset())) {
return false;
}
final int minFlingVelocity =mRecyclerView.getMinFlingVelocity();
mGravityScroller.fling(0, 0, velocityX, velocityY,
Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE);
if (layoutManager.mOrientation == ViewPagerLayoutManager.VERTICAL
&& Math.abs(velocityY) > minFlingVelocity) {
final int currentPosition = layoutManager.getCurrentPositionOffset();
final int offsetPosition = (int) (mGravityScroller.getFinalY() /
layoutManager.mInterval / layoutManager.getDistanceRatio());
ScrollHelper.smoothScrollToPosition(mRecyclerView, layoutManager, layoutManager.getReverseLayout() ?
-currentPosition - offsetPosition : currentPosition + offsetPosition);
return true;
}else if (layoutManager.mOrientation == ViewPagerLayoutManager.HORIZONTAL
&& Math.abs(velocityX) > minFlingVelocity) {
final int currentPosition = layoutManager.getCurrentPositionOffset();
final int offsetPosition = (int) (mGravityScroller.getFinalX() /
layoutManager.mInterval / layoutManager.getDistanceRatio());
ScrollHelper.smoothScrollToPosition(mRecyclerView, layoutManager, layoutManager.getReverseLayout() ?
-currentPosition - offsetPosition : currentPosition + offsetPosition);
return true;
}
return true;
}
源码:可以看我的上一篇,模仿抖音的效果!!!!!
public class ScrollHelper {
/* package */ static void smoothScrollToPosition(RecyclerView recyclerView, ViewPagerLayoutManager viewPagerLayoutManager, int targetPosition) {
final int delta = viewPagerLayoutManager.getOffsetToPosition(targetPosition);
if (viewPagerLayoutManager.getOrientation() == ViewPagerLayoutManager.VERTICAL) {
recyclerView.smoothScrollBy(0, delta);
}else {
recyclerView.smoothScrollBy(delta, 0);
}
}
public static void smoothScrollToTargetView(RecyclerView recyclerView, View targetView) {
final RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
if (!(layoutManagerinstanceof ViewPagerLayoutManager))return;
final int targetPosition = ((ViewPagerLayoutManager) layoutManager).getLayoutPositionOfView(targetView);
smoothScrollToPosition(recyclerView, (ViewPagerLayoutManager) layoutManager, targetPosition);
}
}
/**
* 可缩放的弧形菜单
*/
publicclassCircleScaleLayoutManagerextendsViewPagerLayoutManager{
publicstaticfinalintLEFT=10;
publicstaticfinalintRIGHT=11;
publicstaticfinalintTOP=12;
publicstaticfinalintBOTTOM=13;
publicstaticfinalintLEFT_ON_TOP=4;
publicstaticfinalintRIGHT_ON_TOP=5;
publicstaticfinalintCENTER_ON_TOP=6;
privateintradius;// 半径
privateintangleInterval;// 间隔角度
privatefloatmoveSpeed;
privatefloatcenterScale;// 中心缩放比
privatefloatmaxRemoveAngle;
privatefloatminRemoveAngle;
privateintgravity;
privatebooleanflipRotate;
privateintzAlignment;
publicCircleScaleLayoutManager(Contextcontext) {
this(newBuilder(context));
}
publicCircleScaleLayoutManager(Contextcontext,intgravity,booleanreverseLayout) {
this(newBuilder(context).setGravity(gravity).setReverseLayout(reverseLayout));
}
publicCircleScaleLayoutManager(Contextcontext,booleanreverseLayout) {
this(newBuilder(context).setReverseLayout(reverseLayout));
}
publicCircleScaleLayoutManager(Builderbuilder) {
this(builder.context,builder.radius,builder.angleInterval,builder.centerScale,builder.moveSpeed,
builder.maxRemoveAngle,builder.minRemoveAngle,builder.gravity,builder.zAlignment,
builder.flipRotate,builder.maxVisibleItemCount,builder.distanceToBottom,builder.reverseLayout);
}
privateCircleScaleLayoutManager(Contextcontext,intradius,intangleInterval,floatcenterScale,
floatmoveSpeed,floatmax,floatmin,intgravity,intzAlignment,
booleanflipRotate,intmaxVisibleItemCount,intdistanceToBottom,booleanreverseLayout) {
super(context,HORIZONTAL,reverseLayout);
setEnableBringCenterToFront(true);
setMaxVisibleItemCount(maxVisibleItemCount);
setDistanceToBottom(distanceToBottom);
this.radius=radius;
this.angleInterval=angleInterval;
this.centerScale=centerScale;
this.moveSpeed=moveSpeed;
this.maxRemoveAngle=max;
this.minRemoveAngle=min;
this.gravity=gravity;
this.flipRotate=flipRotate;
this.zAlignment=zAlignment;
}
publicintgetRadius() {
returnradius;
}
publicintgetAngleInterval() {
returnangleInterval;
}
publicfloatgetCenterScale() {
returncenterScale;
}
publicfloatgetMoveSpeed() {
returnmoveSpeed;
}
publicfloatgetMaxRemoveAngle() {
returnmaxRemoveAngle;
}
publicfloatgetMinRemoveAngle() {
returnminRemoveAngle;
}
publicintgetGravity() {
returngravity;
}
publicbooleangetFlipRotate() {
returnflipRotate;
}
publicintgetZAlignment() {
returnzAlignment;
}
publicvoidsetRadius(intradius) {
assertNotInLayoutOrScroll(null);
if(this.radius==radius)return;
this.radius=radius;
removeAllViews();
}
publicvoidsetAngleInterval(intangleInterval) {
assertNotInLayoutOrScroll(null);
if(this.angleInterval==angleInterval)return;
this.angleInterval=angleInterval;
removeAllViews();
}
publicvoidsetCenterScale(floatcenterScale) {
assertNotInLayoutOrScroll(null);
if(this.centerScale==centerScale)return;
this.centerScale=centerScale;
requestLayout();
}
publicvoidsetMoveSpeed(floatmoveSpeed) {
assertNotInLayoutOrScroll(null);
if(this.moveSpeed==moveSpeed)return;
this.moveSpeed=moveSpeed;
}
publicvoidsetMaxRemoveAngle(floatmaxRemoveAngle) {
assertNotInLayoutOrScroll(null);
if(this.maxRemoveAngle==maxRemoveAngle)return;
this.maxRemoveAngle=maxRemoveAngle;
requestLayout();
}
publicvoidsetMinRemoveAngle(floatminRemoveAngle) {
assertNotInLayoutOrScroll(null);
if(this.minRemoveAngle==minRemoveAngle)return;
this.minRemoveAngle=minRemoveAngle;
requestLayout();
}
@SuppressLint("WrongConstant")
publicvoidsetGravity(intgravity) {
assertNotInLayoutOrScroll(null);
assertGravity(gravity);
if(this.gravity==gravity)return;
this.gravity=gravity;
if(gravity==LEFT||gravity==RIGHT) {
setOrientation(VERTICAL);
}else{
setOrientation(HORIZONTAL);
}
requestLayout();
}
publicvoidsetFlipRotate(booleanflipRotate) {
assertNotInLayoutOrScroll(null);
if(this.flipRotate==flipRotate)return;
this.flipRotate=flipRotate;
requestLayout();
}
publicvoidsetZAlignment(intzAlignment) {
assertNotInLayoutOrScroll(null);
assertZAlignmentState(zAlignment);
if(this.zAlignment==zAlignment)return;
this.zAlignment=zAlignment;
requestLayout();
}
@Override
protectedfloatsetInterval() {
returnangleInterval;
}
@Override
protectedvoidsetUp() {
radius=radius==Builder.INVALID_VALUE?mDecoratedMeasurementInOther:radius;
}
@Override
protectedfloatmaxRemoveOffset() {
returnmaxRemoveAngle;
}
@Override
protectedfloatminRemoveOffset() {
returnminRemoveAngle;
}
@Override
protectedintcalItemLeft(ViewitemView,floattargetOffset) {
doublesin=Math.sin(Math.toRadians(90-targetOffset));
doublecos=Math.cos(Math.toRadians(90-targetOffset));
switch(gravity) {
caseLEFT:
return(int) (radius*sin-radius);
caseRIGHT:
return(int) (radius-radius*sin);
caseTOP:
caseBOTTOM:
default:
return(int) (radius*cos);
}
}
@Override
protectedintcalItemTop(ViewitemView,floattargetOffset) {
doublesin=Math.sin(Math.toRadians(90-targetOffset));
doublecos=Math.cos(Math.toRadians(90-targetOffset));
switch(gravity) {
caseLEFT:
caseRIGHT:
return(int) (radius*cos);
caseTOP:
return(int) (radius*sin-radius);
caseBOTTOM:
default:
return(int) (radius-radius*sin);
}
}
@Override
protectedvoidsetItemViewProperty(ViewitemView,floattargetOffset) {
floatscale=1f;
switch(gravity) {
caseRIGHT:
caseTOP:
// 图片平行 渐变缩放
if(targetOffset<angleInterval&&targetOffset>-angleInterval) {
floatdiff=Math.abs(targetOffset);
scale=diff*(centerScale-1f)/-angleInterval+centerScale;
// 增加itemView回调
onPageChangeListener.onViewCentered(diff<5,itemView);
}
break;
caseLEFT:
caseBOTTOM:
default:
if(flipRotate) {
itemView.setRotation(360-targetOffset);
if(targetOffset<angleInterval&&targetOffset>-angleInterval) {
floatdiff=Math.abs(Math.abs(360-itemView.getRotation()-angleInterval)-angleInterval);
scale=(centerScale-1f)/-angleInterval*diff+centerScale;
}
}else{
itemView.setRotation(targetOffset);
if(targetOffset<angleInterval&&targetOffset>-angleInterval) {
floatdiff=Math.abs(Math.abs(itemView.getRotation()-angleInterval)-angleInterval);
scale=(centerScale-1f)/-angleInterval*diff+centerScale;
}
}
break;
}
itemView.setScaleX(scale);
itemView.setScaleY(scale);
}
@Override
protectedfloatsetViewElevation(ViewitemView,floattargetOffset) {
if(zAlignment==LEFT_ON_TOP)
return(540-targetOffset)/72;
elseif(zAlignment==RIGHT_ON_TOP)
return(targetOffset-540)/72;
else
return(360-Math.abs(targetOffset))/72;
}
@Override
protectedfloatgetDistanceRatio() {
if(moveSpeed==0)returnFloat.MAX_VALUE;
return1/moveSpeed;
}
privatestaticvoidassertGravity(intgravity) {
if(gravity!=LEFT&&gravity!=RIGHT&&gravity!=TOP&&gravity!=BOTTOM) {
thrownewIllegalArgumentException("gravity must be one of LEFT RIGHT TOP and BOTTOM");
}
}
privatestaticvoidassertZAlignmentState(intzAlignment) {
if(zAlignment!=LEFT_ON_TOP&&zAlignment!=RIGHT_ON_TOP&&zAlignment!=CENTER_ON_TOP) {
thrownewIllegalArgumentException("zAlignment must be one of LEFT_ON_TOP RIGHT_ON_TOP and CENTER_ON_TOP");
}
}
publicstaticclassBuilder{
privatestaticintINTERVAL_ANGLE=30;// The default mInterval angle between each items
privatestaticfloatDISTANCE_RATIO=10f;// Finger swipe distance divide item rotate angle
privatestaticfinalfloatSCALE_RATE=1.2f;
privatestaticintINVALID_VALUE=Integer.MIN_VALUE;
privateintradius;
privateintangleInterval;
privatefloatcenterScale;
privatefloatmoveSpeed;
privatefloatmaxRemoveAngle;
privatefloatminRemoveAngle;
privatebooleanreverseLayout;
privateContextcontext;
privateintgravity;
privatebooleanflipRotate;
privateintzAlignment;
privateintmaxVisibleItemCount;
privateintdistanceToBottom;
publicBuilder(Contextcontext) {
this.context=context;
radius=INVALID_VALUE;
angleInterval=INTERVAL_ANGLE;
centerScale=SCALE_RATE;
moveSpeed=1/DISTANCE_RATIO;
maxRemoveAngle=90;
minRemoveAngle=-90;
reverseLayout=false;
flipRotate=false;
gravity=BOTTOM;
zAlignment=CENTER_ON_TOP;
distanceToBottom=ViewPagerLayoutManager.INVALID_SIZE;
maxVisibleItemCount=ViewPagerLayoutManager.DETERMINE_BY_MAX_AND_MIN;
}
publicBuildersetRadius(intradius) {
this.radius=radius;
returnthis;
}
publicBuildersetAngleInterval(intangleInterval) {
this.angleInterval=angleInterval;
returnthis;
}
publicBuildersetCenterScale(floatcenterScale) {
this.centerScale=centerScale;
returnthis;
}
publicBuildersetMoveSpeed(intmoveSpeed) {
this.moveSpeed=moveSpeed;
returnthis;
}
publicBuildersetMaxRemoveAngle(floatmaxRemoveAngle) {
this.maxRemoveAngle=maxRemoveAngle;
returnthis;
}
publicBuildersetMinRemoveAngle(floatminRemoveAngle) {
this.minRemoveAngle=minRemoveAngle;
returnthis;
}
publicBuildersetReverseLayout(booleanreverseLayout) {
this.reverseLayout=reverseLayout;
returnthis;
}
publicBuildersetGravity(intgravity) {
assertGravity(gravity);
this.gravity=gravity;
returnthis;
}
publicBuildersetFlipRotate(booleanflipRotate) {
this.flipRotate=flipRotate;
returnthis;
}
publicBuildersetZAlignment(intzAlignment) {
assertZAlignmentState(zAlignment);
this.zAlignment=zAlignment;
returnthis;
}
publicBuildersetMaxVisibleItemCount(intmaxVisibleItemCount) {
this.maxVisibleItemCount=maxVisibleItemCount;
returnthis;
}
publicBuildersetDistanceToBottom(intdistanceToBottom) {
this.distanceToBottom=distanceToBottom;
returnthis;
}
publicCircleScaleLayoutManagerbuild() {
returnnewCircleScaleLayoutManager(this);
}
}
}
public static final int DETERMINE_BY_MAX_AND_MIN = -1;
public static final int HORIZONTAL = OrientationHelper.HORIZONTAL;
public static final int VERTICAL = OrientationHelper.VERTICAL;
private static final int DIRECTION_NO_WHERE = -1;
private static final int DIRECTION_FORWARD =0;
private static final int DIRECTION_BACKWARD =1;
protected static final int INVALID_SIZE = Integer.MAX_VALUE;
private SparseArraypositionCache =new SparseArray<>();
protected int mDecoratedMeasurement;
protected int mDecoratedMeasurementInOther;