Skip to content

Commit

Permalink
Improvements on Carousel (#32)
Browse files Browse the repository at this point in the history
* Improvements on Carousel

Simplify support, add invisible/gone choice for empty behavior
Fix a bug in MotionLayout not applying a ConstraintSet in some situations.

* Update Carousel.java
  • Loading branch information
camaelon committed Nov 20, 2020
1 parent 8848939 commit a549d36
Show file tree
Hide file tree
Showing 11 changed files with 649 additions and 616 deletions.
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@

projects/CarouselExperiments/.idea/vcs.xml
projects/CarouselExperiments/.idea/misc.xml
projects/CarouselExperiments/.idea/jarRepositories.xml
projects/CarouselExperiments/.idea/gradle.xml
projects/CarouselExperiments/.idea/compiler.xml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import android.content.res.TypedArray;
import android.os.Build;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;

import androidx.annotation.RequiresApi;
Expand All @@ -33,26 +34,21 @@

public class Carousel extends MotionHelper {
private static final boolean DEBUG = false;
private static final String TAG = "Carousel";
private Adapter mAdapter = null;
private ArrayList<View> mList = new ArrayList<>();
private int mPreviousIndex = 0;
private int mIndex = 0;
private MotionLayout mMotionLayout;
private int firstViewReference = -1;
private int lastViewReference = -1;

private int backwardTransition = -1;
private int forwardTransition = -1;
private int forwardStartTransition = -1; // optional
private int forwardEndTransition = -1; // optional
private int forwardMinEndTransition = -1; // optional
private int defaultTransition = -1; // optional
private int minEndThreshold = -1;
private int previousState = -1;
private int nextState = -1;
private float dampening = 0.9f;
private int startIndex = 0;
private int endIndex = 0;
private int emptyViewBehavior = INVISIBLE;

public static final int TOUCH_UP_IMMEDIATE_STOP = 1;
public static final int TOUCH_UP_CARRY_ON = 2;
Expand All @@ -63,8 +59,7 @@ public class Carousel extends MotionHelper {
public interface Adapter {
int count();
void populate(View view, int index);

void newItem(int mIndex);
void onNewItem(int mIndex);
}

public Carousel(Context context) {
Expand All @@ -89,22 +84,12 @@ private void init(Context context, AttributeSet attrs) {
int attr = a.getIndex(i);
if (attr == R.styleable.Carousel_carousel_firstView) {
firstViewReference = a.getResourceId(attr, firstViewReference);
} else if (attr == R.styleable.Carousel_carousel_lastView) {
lastViewReference = a.getResourceId(attr, lastViewReference);
} else if (attr == R.styleable.Carousel_carousel_backwardTransition) {
backwardTransition = a.getResourceId(attr, backwardTransition);
} else if (attr == R.styleable.Carousel_carousel_forwardTransition) {
forwardTransition = a.getResourceId(attr, forwardTransition);
} else if (attr == R.styleable.Carousel_carousel_forwardStartTransition) {
forwardStartTransition = a.getResourceId(attr, forwardStartTransition);
} else if (attr == R.styleable.Carousel_carousel_forwardEndTransition) {
forwardEndTransition = a.getResourceId(attr, forwardEndTransition);
} else if (attr == R.styleable.Carousel_carousel_forwardMinEndTransition) {
forwardMinEndTransition = a.getResourceId(attr, forwardMinEndTransition);
} else if (attr == R.styleable.Carousel_carousel_defaultTransition) {
defaultTransition = a.getResourceId(attr, defaultTransition);
} else if (attr == R.styleable.Carousel_carousel_minEndThreshold) {
minEndThreshold = a.getInteger(attr, minEndThreshold);
} else if (attr == R.styleable.Carousel_carousel_emptyViewsBehavior) {
emptyViewBehavior = a.getInt(attr, emptyViewBehavior);
} else if (attr == R.styleable.Carousel_carousel_previousState) {
previousState = a.getResourceId(attr, previousState);
} else if (attr == R.styleable.Carousel_carousel_nextState) {
Expand All @@ -128,7 +113,7 @@ public void refresh() {
for (int i = 0; i < count; i++) {
View view = mList.get(i);
if (mAdapter.count() == 0) {
updateViewVisibility(view, INVISIBLE);
updateViewVisibility(view, emptyViewBehavior);
} else {
updateViewVisibility(view, VISIBLE);
}
Expand All @@ -142,28 +127,43 @@ public void onTransitionChange(MotionLayout motionLayout, int startId, int endId
if (DEBUG) {
System.out.println("onTransitionChange from " + startId + " to " + endId + " progress " + progress);
}
mLastStartId = startId;
}

int mLastStartId = -1;

@Override
public void onTransitionCompleted(MotionLayout motionLayout, int currentId) {
System.out.println("on transition completed");
mPreviousIndex = mIndex;
if (currentId == nextState) {
mIndex++;
System.out.println("increment index...");
} else if (currentId == previousState) {
mIndex--;
System.out.println("decrement index...");
}
if (mIndex >= mAdapter.count()) {
mIndex = mAdapter.count() - 1;
System.out.println("index capped... " + mIndex);
}
if (mIndex < 0) {
mIndex = 0;
System.out.println("index zeroed... ");
}

if (mPreviousIndex != mIndex) {
mMotionLayout.post(mUpdateRunnable);
}
}

private void enableAllTransitions(boolean enable) {
ArrayList<MotionScene.Transition> transitions = mMotionLayout.getDefinedTransitions();
for (MotionScene.Transition transition : transitions) {
transition.setEnable(enable);
}
}

private boolean enableTransition(int transitionID, boolean enable) {
if (transitionID == -1) {
return false;
Expand All @@ -187,7 +187,7 @@ private boolean enableTransition(int transitionID, boolean enable) {
public void run() {
mMotionLayout.setProgress(0);
updateItems();
mAdapter.newItem(mIndex);
mAdapter.onNewItem(mIndex);
float velocity = mMotionLayout.getVelocity();
if (touchUpMode == TOUCH_UP_CARRY_ON && velocity > velocityThreshold && mIndex < mAdapter.count() - 1) {
final float v = velocity * dampening;
Expand Down Expand Up @@ -225,9 +225,6 @@ protected void onAttachedToWindow() {
if (firstViewReference == id) {
startIndex = i;
}
if (lastViewReference == id) {
endIndex = i;
}
mList.add(view);
}
mMotionLayout = container;
Expand Down Expand Up @@ -272,36 +269,15 @@ private boolean updateViewVisibility(int constraintSetId, View view, int visibil
if (constraint == null) {
return false;
}
if (constraint.propertySet.visibility == visibility) {
return false;
}
constraint.propertySet.visibility = visibility;
constraint.propertySet.mVisibilityMode = ConstraintSet.VISIBILITY_MODE_IGNORE;
// if (constraint.propertySet.visibility == visibility) {
// return false;
// }
// constraint.propertySet.visibility = visibility;
view.setVisibility(visibility);
return true;
}

private void updateItemsVisibility() {
if (mAdapter == null) {
return;
}
if (mMotionLayout == null) {
return;
}
final int count = mList.size();
for (int i = 0; i < count; i++) {
// mIndex should map to i == startIndex
View view = mList.get(i);
int index = mIndex + i - startIndex;
if (index < 0) {
updateViewVisibility(view, INVISIBLE);
} else if (index >= mAdapter.count()) {
updateViewVisibility(view, INVISIBLE);
} else {
updateViewVisibility(view, VISIBLE);
}
}
}

private void updateItems() {
if (mAdapter == null) {
return;
Expand All @@ -312,96 +288,38 @@ private void updateItems() {
if (DEBUG) {
System.out.println("Update items, index: " + mIndex);
}
final int count = mList.size();
boolean needsToRebuild = false;
for (int i = 0; i < count; i++) {
final int viewCount = mList.size();
for (int i = 0; i < viewCount; i++) {
// mIndex should map to i == startIndex
View view = mList.get(i);
int index = mIndex + i - startIndex;
if (index < 0) {
if (forwardEndTransition == -1) {
// no custom start transition, so let's make those views invisible
needsToRebuild |= updateViewVisibility(view, INVISIBLE);
}
updateViewVisibility(view, emptyViewBehavior);
} else if (index >= mAdapter.count()) {
if (forwardEndTransition == -1) {
// no custom end transition, so let's make those views invisible
needsToRebuild |= updateViewVisibility(view, INVISIBLE);
}
updateViewVisibility(view, emptyViewBehavior);
} else {
if (forwardStartTransition == -1 || forwardEndTransition == -1) {
// if we don't have a start/end transitions, we might have modified the
// visibility of the views, so let's make sure they are visible
needsToRebuild |= updateViewVisibility(view, VISIBLE);
}
updateViewVisibility(view, VISIBLE);
mAdapter.populate(view, index);
}
}

if (backwardTransition != -1 && forwardTransition != -1) {
if (mAdapter.count() > 1) {
if (mIndex == 0) {
needsToRebuild |= enableTransition(backwardTransition, false);
if (forwardStartTransition != -1) {
needsToRebuild |= enableTransition(forwardTransition, false);
needsToRebuild |= enableTransition(forwardStartTransition, true);
mMotionLayout.setTransition(forwardStartTransition);
} else {
mMotionLayout.setTransition(forwardTransition);
}
} else {
needsToRebuild |= enableTransition(backwardTransition, true);
}
}

if (mAdapter.count() == 0) {
for (int i = 0; i < count; i++) {
// mIndex should map to i == startIndex
View view = mList.get(i);
// needsToRebuild |= updateViewVisibility(view, INVISIBLE);
}
} else if (mAdapter.count() == 1 && defaultTransition != -1) {
enableTransition(defaultTransition, true);
mMotionLayout.setTransition(defaultTransition);
mMotionLayout.setProgress(0);
} else if (mAdapter.count() > 1) {
if (mIndex == mAdapter.count() - 1) {
needsToRebuild |= enableTransition(forwardTransition, false);
if (forwardEndTransition != -1) {
needsToRebuild |= enableTransition(forwardEndTransition, false);
}
mMotionLayout.setTransition(backwardTransition);
} else {
int lastVisibleIndex = Math.max(0, mAdapter.count() - (mList.size() - endIndex) - 1);
if (DEBUG) {
System.out.println("### index " + mIndex + " endIndex: " + endIndex + " last index is " + lastVisibleIndex + " count " + mAdapter.count());
}
if (mIndex == lastVisibleIndex && forwardEndTransition != -1) {
if (mIndex == 0) {
if (forwardMinEndTransition != -1 && mAdapter.count() < minEndThreshold) {
needsToRebuild |= enableTransition(forwardTransition, false);
needsToRebuild |= enableTransition(forwardStartTransition, false);
needsToRebuild |= enableTransition(forwardMinEndTransition, true);
mMotionLayout.setTransition(forwardMinEndTransition);
} else {
needsToRebuild |= enableTransition(forwardTransition, false);
needsToRebuild |= enableTransition(forwardStartTransition, true);
mMotionLayout.setTransition(forwardStartTransition);
}
} else {
needsToRebuild |= enableTransition(forwardTransition, false);
needsToRebuild |= enableTransition(forwardEndTransition, true);
mMotionLayout.setTransition(forwardEndTransition);
}
} else {
needsToRebuild |= enableTransition(forwardTransition, true);
}
}
}
if (backwardTransition == -1 || forwardTransition == -1) {
Log.w(TAG, "No backward or forward transitions defined for Carousel!");
return;
}

if (needsToRebuild) {
mMotionLayout.rebuildScene();
final int count = mAdapter.count();
if (mIndex == 0) {
enableTransition(backwardTransition, false);
} else {
enableTransition(backwardTransition, true);
mMotionLayout.setTransition(backwardTransition);
}
if (mIndex == count - 1) {
enableTransition(forwardTransition, false);
} else {
enableTransition(forwardTransition, true);
mMotionLayout.setTransition(forwardTransition);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1327,6 +1327,16 @@ public void setTransition(int transitionId) {
mModel.initFrom(mLayoutWidget, mScene.getConstraintSet(mBeginState), mScene.getConstraintSet(mEndState));
rebuildScene();

if (mTransitionLastPosition != pos) {
// If the last drawn position isn't the same, we might have to make sure we apply the
// corresponding constraintset.
if (pos == 0) {
mScene.getConstraintSet(mBeginState).applyTo(this);
} else if (pos == 1) {
mScene.getConstraintSet(mEndState).applyTo(this);
}
}

mTransitionLastPosition = Float.isNaN(pos) ? 0 : pos;

if (Float.isNaN(pos)) {
Expand Down Expand Up @@ -1605,7 +1615,6 @@ public void setProgress(float pos) {
} else {
mCurrentState = UNSET;
setState(TransitionState.MOVING);

}

if (mScene == null) {
Expand Down
10 changes: 4 additions & 6 deletions constraintlayout/constraintlayout/src/main/res/values/attrs.xml
Original file line number Diff line number Diff line change
Expand Up @@ -234,22 +234,20 @@

<declare-styleable name="Carousel">
<attr name="carousel_firstView" format="reference" />
<attr name="carousel_lastView" format="reference" />
<attr name="carousel_previousState" format="reference" />
<attr name="carousel_nextState" format="reference" />
<attr name="carousel_defaultTransition" format="reference" />
<attr name="carousel_forwardTransition" format="reference" />
<attr name="carousel_backwardTransition" format="reference" />
<attr name="carousel_forwardStartTransition" format="reference" />
<attr name="carousel_forwardEndTransition" format="reference" />
<attr name="carousel_forwardMinEndTransition" format="reference" />
<attr name="carousel_minEndThreshold" format="integer" />
<attr name="carousel_touchUp_dampeningFactor" format="float" />
<attr name="carousel_touchUpMode" format="enum">
<enum name="immediateStop" value="1"/>
<enum name="carryVelocity" value="2"/>
</attr>
<attr name="carousel_touchUp_velocityThreshold" format="float"/>
<attr name="carousel_emptyViewsBehavior" format="enum">
<enum name="invisible" value="4"/>
<enum name="gone" value="8" />
</attr>
</declare-styleable>

<!-- =============================== End Carousel =============================== -->
Expand Down

0 comments on commit a549d36

Please sign in to comment.