Skip to content

Commit

Permalink
自定义LayoutManager
Browse files Browse the repository at this point in the history
  • Loading branch information
wosojadfjgo committed Sep 10, 2019
1 parent e48dff4 commit 24d1e7d
Show file tree
Hide file tree
Showing 13 changed files with 3,433 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package com.heyongrui.base.widget.layoutmanager;

import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;

import androidx.annotation.Nullable;
import androidx.recyclerview.widget.RecyclerView;

/**
* An implement of {@link RecyclerView} which support auto play.
*/

public class AutoPlayRecyclerView extends RecyclerView {

private AutoPlaySnapHelper autoPlaySnapHelper;

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

public AutoPlayRecyclerView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}

public AutoPlayRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
autoPlaySnapHelper = new AutoPlaySnapHelper(AutoPlaySnapHelper.TIME_INTERVAL, AutoPlaySnapHelper.RIGHT);
}

@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
boolean result = super.dispatchTouchEvent(ev);
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
if (autoPlaySnapHelper != null) {
autoPlaySnapHelper.pause();
}
break;
case MotionEvent.ACTION_UP:
if (autoPlaySnapHelper != null) {
autoPlaySnapHelper.start();
}
}
return result;
}

public void setTimeInterval(int timeInterval) {
if (null != autoPlaySnapHelper) {
autoPlaySnapHelper.setTimeInterval(timeInterval);
}
}

public void setDirection(int direction) {
if (null != autoPlaySnapHelper) {
autoPlaySnapHelper.setDirection(direction);
}
}

public void start() {
autoPlaySnapHelper.start();
}

public void pause() {
autoPlaySnapHelper.pause();
}

@Override
public void setLayoutManager(LayoutManager layout) {
super.setLayoutManager(layout);
autoPlaySnapHelper.attachToRecyclerView(this);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
package com.heyongrui.base.widget.layoutmanager;

import android.os.Handler;
import android.os.Looper;
import android.view.animation.DecelerateInterpolator;
import android.widget.Scroller;

import androidx.annotation.Nullable;
import androidx.recyclerview.widget.RecyclerView;


/**
* Used by {@link AutoPlayRecyclerView} to implement auto play effect
*/

class AutoPlaySnapHelper extends CenterSnapHelper {
final static int TIME_INTERVAL = 2000;

final static int LEFT = 1;
final static int RIGHT = 2;

private Handler handler;
private int timeInterval;
private Runnable autoPlayRunnable;
private boolean runnableAdded;
private int direction;

AutoPlaySnapHelper(int timeInterval, int direction) {
checkTimeInterval(timeInterval);
checkDirection(direction);
handler = new Handler(Looper.getMainLooper());
this.timeInterval = timeInterval;
this.direction = direction;
}

@Override
public void attachToRecyclerView(@Nullable RecyclerView recyclerView) throws IllegalStateException {
if (mRecyclerView == recyclerView) {
return; // nothing to do
}
if (mRecyclerView != null) {
destroyCallbacks();
}
mRecyclerView = recyclerView;
if (mRecyclerView != null) {
final RecyclerView.LayoutManager layoutManager = mRecyclerView.getLayoutManager();
if (!(layoutManager instanceof ViewPagerLayoutManager)) return;

setupCallbacks();
mGravityScroller = new Scroller(mRecyclerView.getContext(),
new DecelerateInterpolator());

snapToCenterView((ViewPagerLayoutManager) layoutManager,
((ViewPagerLayoutManager) layoutManager).onPageChangeListener);

((ViewPagerLayoutManager) layoutManager).setInfinite(true);

autoPlayRunnable = new Runnable() {
@Override
public void run() {
final int currentPosition =
((ViewPagerLayoutManager) layoutManager).getCurrentPositionOffset() *
(((ViewPagerLayoutManager) layoutManager).getReverseLayout() ? -1 : 1);
ScrollHelper.smoothScrollToPosition(mRecyclerView,
(ViewPagerLayoutManager) layoutManager, direction == RIGHT ? currentPosition + 1 : currentPosition - 1);
handler.postDelayed(autoPlayRunnable, timeInterval);
}
};
handler.postDelayed(autoPlayRunnable, timeInterval);
runnableAdded = true;
}
}

@Override
void destroyCallbacks() {
super.destroyCallbacks();
if (runnableAdded) {
handler.removeCallbacks(autoPlayRunnable);
runnableAdded = false;
}
}

void pause() {
if (runnableAdded) {
handler.removeCallbacks(autoPlayRunnable);
runnableAdded = false;
}
}

void start() {
if (!runnableAdded) {
handler.postDelayed(autoPlayRunnable, timeInterval);
runnableAdded = true;
}
}

void setTimeInterval(int timeInterval) {
checkTimeInterval(timeInterval);
this.timeInterval = timeInterval;
}

void setDirection(int direction) {
checkDirection(direction);
this.direction = direction;
}

private void checkDirection(int direction) {
if (direction != LEFT && direction != RIGHT)
throw new IllegalArgumentException("direction should be one of left or right");
}

private void checkTimeInterval(int timeInterval) {
if (timeInterval <= 0)
throw new IllegalArgumentException("time interval should greater than 0");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
package com.heyongrui.base.widget.layoutmanager;

import android.content.Context;
import android.view.View;

/**
* An implementation of {@link ViewPagerLayoutManager}
* which layouts items like carousel
*/

public class CarouselLayoutManager extends ViewPagerLayoutManager {

private int itemSpace;
private float minScale;
private float moveSpeed;

public CarouselLayoutManager(Context context, int itemSpace) {
this(new Builder(context, itemSpace));
}

public CarouselLayoutManager(Context context, int itemSpace, int orientation) {
this(new Builder(context, itemSpace).setOrientation(orientation));
}

public CarouselLayoutManager(Context context, int itemSpace, int orientation, boolean reverseLayout) {
this(new Builder(context, itemSpace).setOrientation(orientation).setReverseLayout(reverseLayout));
}

public CarouselLayoutManager(Builder builder) {
this(builder.context, builder.itemSpace, builder.minScale, builder.orientation,
builder.maxVisibleItemCount, builder.moveSpeed, builder.distanceToBottom,
builder.reverseLayout);
}

private CarouselLayoutManager(Context context, int itemSpace, float minScale, int orientation,
int maxVisibleItemCount, float moveSpeed, int distanceToBottom,
boolean reverseLayout) {
super(context, orientation, reverseLayout);
setEnableBringCenterToFront(true);
setDistanceToBottom(distanceToBottom);
setMaxVisibleItemCount(maxVisibleItemCount);
this.itemSpace = itemSpace;
this.minScale = minScale;
this.moveSpeed = moveSpeed;
}

public int getItemSpace() {
return itemSpace;
}

public float getMinScale() {
return minScale;
}

public float getMoveSpeed() {
return moveSpeed;
}

public void setItemSpace(int itemSpace) {
assertNotInLayoutOrScroll(null);
if (this.itemSpace == itemSpace) return;
this.itemSpace = itemSpace;
removeAllViews();
}

public void setMinScale(float minScale) {
assertNotInLayoutOrScroll(null);
if (minScale > 1f) minScale = 1f;
if (this.minScale == minScale) return;
this.minScale = minScale;
requestLayout();
}

public void setMoveSpeed(float moveSpeed) {
assertNotInLayoutOrScroll(null);
if (this.moveSpeed == moveSpeed) return;
this.moveSpeed = moveSpeed;
}

@Override
protected float setInterval() {
return (mDecoratedMeasurement - itemSpace);
}

@Override
protected void setItemViewProperty(View itemView, float targetOffset) {
float scale = calculateScale(targetOffset + mSpaceMain);
itemView.setScaleX(scale);
itemView.setScaleY(scale);
}

@Override
protected float getDistanceRatio() {
if (moveSpeed == 0) return Float.MAX_VALUE;
return 1 / moveSpeed;
}

@Override
protected float setViewElevation(View itemView, float targetOffset) {
return itemView.getScaleX() * 5;
}

private float calculateScale(float x) {
float deltaX = Math.abs(x - (mOrientationHelper.getTotalSpace() - mDecoratedMeasurement) / 2f);
return (minScale - 1) * deltaX / (mOrientationHelper.getTotalSpace() / 2f) + 1f;
}

public static class Builder {
private static final float DEFAULT_SPEED = 1f;
private static final float MIN_SCALE = 0.5f;

private Context context;
private int itemSpace;
private int orientation;
private float minScale;
private float moveSpeed;
private int maxVisibleItemCount;
private boolean reverseLayout;
private int distanceToBottom;

public Builder(Context context, int itemSpace) {
this.itemSpace = itemSpace;
this.context = context;
orientation = HORIZONTAL;
minScale = MIN_SCALE;
this.moveSpeed = DEFAULT_SPEED;
reverseLayout = false;
maxVisibleItemCount = ViewPagerLayoutManager.DETERMINE_BY_MAX_AND_MIN;
distanceToBottom = ViewPagerLayoutManager.INVALID_SIZE;
}

public Builder setOrientation(int orientation) {
this.orientation = orientation;
return this;
}

public Builder setMinScale(float minScale) {
this.minScale = minScale;
return this;
}

public Builder setReverseLayout(boolean reverseLayout) {
this.reverseLayout = reverseLayout;
return this;
}

public Builder setMoveSpeed(float moveSpeed) {
this.moveSpeed = moveSpeed;
return this;
}

public Builder setMaxVisibleItemCount(int maxVisibleItemCount) {
this.maxVisibleItemCount = maxVisibleItemCount;
return this;
}

public Builder setDistanceToBottom(int distanceToBottom) {
this.distanceToBottom = distanceToBottom;
return this;
}

public CarouselLayoutManager build() {
return new CarouselLayoutManager(this);
}
}
}
Loading

0 comments on commit 24d1e7d

Please sign in to comment.