package carbon.widget;
import android.animation.ValueAnimator;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
import android.os.Build;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.view.ViewCompat;
import android.util.AttributeSet;
import android.view.View;
import android.view.animation.DecelerateInterpolator;
import carbon.Carbon;
import carbon.R;
import carbon.animation.AnimatedColorStateList;
import carbon.drawable.DefaultPrimaryColorStateList;
public class ViewPagerIndicator extends View implements TintedView, VisibleView {
ViewPager viewPager;
private Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
private float indicatorPos = 0;
private int selectedPage = 0;
DecelerateInterpolator decelerateInterpolator = new DecelerateInterpolator();
private ValueAnimator animator;
private ViewPager.OnPageChangeListener pageChangeListener = new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
position = Math.round(position + positionOffset);
if (position != selectedPage) {
if (animator != null)
animator.cancel();
animator = ValueAnimator.ofFloat(indicatorPos, position);
animator.setDuration(200);
if (position > selectedPage)
animator.setStartDelay(100);
animator.setInterpolator(decelerateInterpolator);
animator.addUpdateListener(animation -> {
indicatorPos = (float) animation.getAnimatedValue();
postInvalidate();
});
animator.start();
selectedPage = position;
}
}
@Override
public void onPageSelected(int position) {
}
@Override
public void onPageScrollStateChanged(int state) {
}
};
public ViewPagerIndicator(Context context) {
super(context, null, R.attr.carbon_viewPagerIndicatorStyle);
initViewPagerIndicator(null, R.attr.carbon_viewPagerIndicatorStyle);
}
public ViewPagerIndicator(Context context, AttributeSet attrs) {
super(Carbon.getThemedContext(context, attrs, R.styleable.ViewPagerIndicator, R.attr.carbon_viewPagerIndicatorStyle, R.styleable.ViewPagerIndicator_carbon_theme), attrs, R.attr.carbon_viewPagerIndicatorStyle);
initViewPagerIndicator(attrs, R.attr.carbon_viewPagerIndicatorStyle);
}
public ViewPagerIndicator(Context context, AttributeSet attrs, int defStyleAttr) {
super(Carbon.getThemedContext(context, attrs, R.styleable.ViewPagerIndicator, defStyleAttr, R.styleable.ViewPagerIndicator_carbon_theme), attrs, defStyleAttr);
initViewPagerIndicator(attrs, defStyleAttr);
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public ViewPagerIndicator(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(Carbon.getThemedContext(context, attrs, R.styleable.ViewPagerIndicator, defStyleAttr, R.styleable.ViewPagerIndicator_carbon_theme), attrs, defStyleAttr, defStyleRes);
initViewPagerIndicator(attrs, defStyleAttr);
}
private static int[] tintIds = new int[]{
R.styleable.ViewPagerIndicator_carbon_tint,
R.styleable.ViewPagerIndicator_carbon_tintMode,
R.styleable.ViewPagerIndicator_carbon_backgroundTint,
R.styleable.ViewPagerIndicator_carbon_backgroundTintMode,
R.styleable.ViewPagerIndicator_carbon_animateColorChanges
};
private void initViewPagerIndicator(AttributeSet attrs, int defStyleAttr) {
TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.ViewPagerIndicator, defStyleAttr, R.style.carbon_ViewPagerIndicator);
Carbon.initTint(this, a, tintIds);
a.recycle();
}
public void setViewPager(final ViewPager viewPager) {
if (viewPager != null)
viewPager.removeOnPageChangeListener(pageChangeListener);
this.viewPager = viewPager;
if (viewPager != null)
viewPager.addOnPageChangeListener(pageChangeListener);
}
@Override
public void draw(@NonNull Canvas canvas) {
super.draw(canvas);
int pages;
if (viewPager != null && viewPager.getAdapter() != null) {
pages = viewPager.getAdapter().getCount();
} else {
pages = 5;
}
paint.setStyle(Paint.Style.STROKE);
int color = getTint().getColorForState(getDrawableState(), getTint().getDefaultColor());
paint.setColor(color);
float radius = getHeight() / 2.0f - 1;
if (pages > 1) {
for (int i = 0; i < pages; i++)
canvas.drawCircle(getHeight() / 2.0f + (getWidth() - getHeight()) * i / (pages - 1), getHeight() / 2.0f, radius, paint);
paint.setStyle(Paint.Style.FILL);
float frac = (float) (indicatorPos - Math.floor(indicatorPos));
paint.setAlpha((int) ((1 - frac) * Color.alpha(color)));
canvas.drawCircle((float) (getHeight() / 2.0f + (getWidth() - getHeight()) * Math.floor(indicatorPos) / (pages - 1)), getHeight() / 2.0f, radius, paint);
paint.setAlpha((int) (frac * Color.alpha(color)));
canvas.drawCircle((float) (getHeight() / 2.0f + (getWidth() - getHeight()) * Math.ceil(indicatorPos) / (pages - 1)), getHeight() / 2.0f, radius, paint);
} else {
paint.setStyle(Paint.Style.FILL);
canvas.drawCircle(getWidth() / 2.0f, getHeight() / 2.0f, radius, paint);
}
}
public ViewPager getViewPager() {
return viewPager;
}
// -------------------------------
// tint
// -------------------------------
ColorStateList tint;
PorterDuff.Mode tintMode;
ColorStateList backgroundTint;
PorterDuff.Mode backgroundTintMode;
boolean animateColorChanges;
ValueAnimator.AnimatorUpdateListener tintAnimatorListener = animation -> {
updateTint();
ViewCompat.postInvalidateOnAnimation(ViewPagerIndicator.this);
};
ValueAnimator.AnimatorUpdateListener backgroundTintAnimatorListener = animation -> {
updateBackgroundTint();
ViewCompat.postInvalidateOnAnimation(ViewPagerIndicator.this);
};
@Override
public void setTint(ColorStateList list) {
this.tint = animateColorChanges && !(list instanceof AnimatedColorStateList) ? AnimatedColorStateList.fromList(list, tintAnimatorListener) : list;
updateTint();
}
@Override
public void setTint(int color) {
if (color == 0) {
setTint(new DefaultPrimaryColorStateList(getContext()));
} else {
setTint(ColorStateList.valueOf(color));
}
}
@Override
public ColorStateList getTint() {
return tint;
}
private void updateTint() {
if (tint == null)
return;
tint.getColorForState(getDrawableState(), tint.getDefaultColor());
}
@Override
public void setTintMode(@NonNull PorterDuff.Mode mode) {
this.tintMode = mode;
updateTint();
}
@Override
public PorterDuff.Mode getTintMode() {
return tintMode;
}
@Override
public void setBackgroundTint(ColorStateList list) {
this.backgroundTint = animateColorChanges && !(list instanceof AnimatedColorStateList) ? AnimatedColorStateList.fromList(list, backgroundTintAnimatorListener) : list;
updateBackgroundTint();
}
@Override
public void setBackgroundTint(int color) {
if (color == 0) {
setBackgroundTint(new DefaultPrimaryColorStateList(getContext()));
} else {
setBackgroundTint(ColorStateList.valueOf(color));
}
}
@Override
public ColorStateList getBackgroundTint() {
return backgroundTint;
}
private void updateBackgroundTint() {
if (getBackground() == null)
return;
if (backgroundTint != null && backgroundTintMode != null) {
int color = backgroundTint.getColorForState(getDrawableState(), backgroundTint.getDefaultColor());
getBackground().setColorFilter(new PorterDuffColorFilter(color, tintMode));
} else {
getBackground().setColorFilter(null);
}
}
@Override
public void setBackgroundTintMode(@Nullable PorterDuff.Mode mode) {
this.backgroundTintMode = mode;
updateBackgroundTint();
}
@Override
public PorterDuff.Mode getBackgroundTintMode() {
return backgroundTintMode;
}
public boolean isAnimateColorChangesEnabled() {
return animateColorChanges;
}
public void setAnimateColorChangesEnabled(boolean animateColorChanges) {
this.animateColorChanges = animateColorChanges;
if (tint != null && !(tint instanceof AnimatedColorStateList))
setTint(AnimatedColorStateList.fromList(tint, tintAnimatorListener));
if (backgroundTint != null && !(backgroundTint instanceof AnimatedColorStateList))
setBackgroundTint(AnimatedColorStateList.fromList(backgroundTint, backgroundTintAnimatorListener));
}
}