Java tutorial
package com.gj.administrator.gjerp.view; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.PorterDuff; import android.graphics.PorterDuffXfermode; import android.graphics.drawable.ShapeDrawable; import android.graphics.drawable.shapes.OvalShape; import android.graphics.drawable.shapes.Shape; import android.support.v4.view.ViewPager; import android.util.AttributeSet; import android.util.Log; import android.view.View; import com.gj.administrator.gjerp.R; import java.util.ArrayList; import java.util.List; /** * Created by xiayong on 2015/9/29. * * Copyright (C) 2014 relex Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ public class CircleIndicator extends View { private ViewPager viewPager; private List<ShapeHolder> tabItems; private ShapeHolder movingItem; //config list private int mCurItemPosition; private float mCurItemPositionOffset; private float mIndicatorRadius; private float mIndicatorMargin; private int mIndicatorBackground; private int mIndicatorSelectedBackground; private Gravity mIndicatorLayoutGravity; private Mode mIndicatorMode; //default value private final int DEFAULT_INDICATOR_RADIUS = 10; private final int DEFAULT_INDICATOR_MARGIN = 40; private final int DEFAULT_INDICATOR_BACKGROUND = Color.BLUE; private final int DEFAULT_INDICATOR_SELECTED_BACKGROUND = Color.RED; private final int DEFAULT_INDICATOR_LAYOUT_GRAVITY = Gravity.CENTER.ordinal(); private final int DEFAULT_INDICATOR_MODE = Mode.SOLO.ordinal(); public enum Gravity { LEFT, CENTER, RIGHT } public enum Mode { INSIDE, OUTSIDE, SOLO } public CircleIndicator(Context context) { super(context); init(context, null); } public CircleIndicator(Context context, AttributeSet attrs) { super(context, attrs); init(context, attrs); } public CircleIndicator(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(context, attrs); } private void init(Context context, AttributeSet attrs) { tabItems = new ArrayList<>(); handleTypedArray(context, attrs); } private void handleTypedArray(Context context, AttributeSet attrs) { if (attrs == null) return; TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CircleIndicator); mIndicatorRadius = typedArray.getDimensionPixelSize(R.styleable.CircleIndicator_ci_radius, DEFAULT_INDICATOR_RADIUS); mIndicatorMargin = typedArray.getDimensionPixelSize(R.styleable.CircleIndicator_ci_margin, DEFAULT_INDICATOR_MARGIN); mIndicatorBackground = typedArray.getColor(R.styleable.CircleIndicator_ci_background, DEFAULT_INDICATOR_BACKGROUND); mIndicatorSelectedBackground = typedArray.getColor(R.styleable.CircleIndicator_ci_selected_background, DEFAULT_INDICATOR_SELECTED_BACKGROUND); int gravity = typedArray.getInt(R.styleable.CircleIndicator_ci_gravity, DEFAULT_INDICATOR_LAYOUT_GRAVITY); mIndicatorLayoutGravity = Gravity.values()[gravity]; int mode = typedArray.getInt(R.styleable.CircleIndicator_ci_mode, DEFAULT_INDICATOR_MODE); mIndicatorMode = Mode.values()[mode]; typedArray.recycle(); } public void setViewPager(final ViewPager viewPager) { this.viewPager = viewPager; createTabItems(); createMovingItem(); setUpListener(); } private void setUpListener() { viewPager.addOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() { @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { super.onPageScrolled(position, positionOffset, positionOffsetPixels); if (mIndicatorMode != Mode.SOLO) { trigger(position, positionOffset); } } @Override public void onPageSelected(int position) { super.onPageSelected(position); if (mIndicatorMode == Mode.SOLO) { trigger(position, 0); } } }); } /** * trigger to redraw the indicator when the ViewPager's selected item changed! * @param position * @param positionOffset */ private void trigger(int position, float positionOffset) { CircleIndicator.this.mCurItemPosition = position; CircleIndicator.this.mCurItemPositionOffset = positionOffset; Log.e("CircleIndicator", "onPageScrolled()" + position + ":" + positionOffset); requestLayout(); invalidate(); } private void createTabItems() { for (int i = 0; i < viewPager.getAdapter().getCount(); i++) { OvalShape circle = new OvalShape(); ShapeDrawable drawable = new ShapeDrawable(circle); ShapeHolder shapeHolder = new ShapeHolder(drawable); Paint paint = drawable.getPaint(); paint.setColor(mIndicatorBackground); paint.setAntiAlias(true); shapeHolder.setPaint(paint); tabItems.add(shapeHolder); } } private void createMovingItem() { OvalShape circle = new OvalShape(); ShapeDrawable drawable = new ShapeDrawable(circle); movingItem = new ShapeHolder(drawable); Paint paint = drawable.getPaint(); paint.setColor(mIndicatorSelectedBackground); paint.setAntiAlias(true); switch (mIndicatorMode) { case INSIDE: paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_ATOP)); break; case OUTSIDE: paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OVER)); break; case SOLO: paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC)); break; } movingItem.setPaint(paint); } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { Log.e("CircleIndicator", "onLayout()"); super.onLayout(changed, left, top, right, bottom); final int width = getWidth(); final int height = getHeight(); layoutTabItems(width, height); layoutMovingItem(mCurItemPosition, mCurItemPositionOffset); } private void layoutTabItems(final int containerWidth, final int containerHeight) { if (tabItems == null) { throw new IllegalStateException("forget to create tabItems?"); } final float yCoordinate = containerHeight * 0.5f; final float startPosition = startDrawPosition(containerWidth); for (int i = 0; i < tabItems.size(); i++) { ShapeHolder item = tabItems.get(i); item.resizeShape(2 * mIndicatorRadius, 2 * mIndicatorRadius); item.setY(yCoordinate - mIndicatorRadius); float x = startPosition + (mIndicatorMargin + mIndicatorRadius * 2) * i; item.setX(x); } } private float startDrawPosition(final int containerWidth) { if (mIndicatorLayoutGravity == Gravity.LEFT) return 0; float tabItemsLength = tabItems.size() * (2 * mIndicatorRadius + mIndicatorMargin) - mIndicatorMargin; if (containerWidth < tabItemsLength) { return 0; } if (mIndicatorLayoutGravity == Gravity.CENTER) { return (containerWidth - tabItemsLength) / 2; } return containerWidth - tabItemsLength; } private void layoutMovingItem(final int position, final float positionOffset) { if (movingItem == null) { throw new IllegalStateException("forget to create movingItem?"); } ShapeHolder item = tabItems.get(position); movingItem.resizeShape(item.getWidth(), item.getHeight()); float x = item.getX() + (mIndicatorMargin + mIndicatorRadius * 2) * positionOffset; movingItem.setX(x); movingItem.setY(item.getY()); } @Override protected void onDraw(Canvas canvas) { Log.e("CircleIndicator", "onDraw()"); super.onDraw(canvas); int sc = canvas.saveLayer(0, 0, getWidth(), getHeight(), null, Canvas.MATRIX_SAVE_FLAG | Canvas.CLIP_SAVE_FLAG | Canvas.HAS_ALPHA_LAYER_SAVE_FLAG | Canvas.FULL_COLOR_LAYER_SAVE_FLAG | Canvas.CLIP_TO_LAYER_SAVE_FLAG); for (ShapeHolder item : tabItems) { canvas.save(); canvas.translate(item.getX(), item.getY()); item.getShape().draw(canvas); canvas.restore(); } if (movingItem != null) { canvas.save(); canvas.translate(movingItem.getX(), movingItem.getY()); movingItem.getShape().draw(canvas); canvas.restore(); } canvas.restoreToCount(sc); } public void setIndicatorRadius(float mIndicatorRadius) { this.mIndicatorRadius = mIndicatorRadius; } public void setIndicatorMargin(float mIndicatorMargin) { this.mIndicatorMargin = mIndicatorMargin; } public void setIndicatorBackground(int mIndicatorBackground) { this.mIndicatorBackground = mIndicatorBackground; } public void setIndicatorSelectedBackground(int mIndicatorSelectedBackground) { this.mIndicatorSelectedBackground = mIndicatorSelectedBackground; } public void setIndicatorLayoutGravity(Gravity mIndicatorLayoutGravity) { this.mIndicatorLayoutGravity = mIndicatorLayoutGravity; } public void setIndicatorMode(Mode mIndicatorMode) { this.mIndicatorMode = mIndicatorMode; } public class ShapeHolder { private float x = 0, y = 0; private ShapeDrawable shape; private int color; private float alpha = 1f; private Paint paint; public void setPaint(Paint value) { paint = value; } public Paint getPaint() { return paint; } public void setX(float value) { x = value; } public float getX() { return x; } public void setY(float value) { y = value; } public float getY() { return y; } public void setShape(ShapeDrawable value) { shape = value; } public ShapeDrawable getShape() { return shape; } public int getColor() { return color; } public void setColor(int value) { shape.getPaint().setColor(value); color = value; } public void setAlpha(float alpha) { this.alpha = alpha; shape.setAlpha((int) ((alpha * 255f) + .5f)); } public float getWidth() { return shape.getShape().getWidth(); } public void setWidth(float width) { Shape s = shape.getShape(); s.resize(width, s.getHeight()); } public float getHeight() { return shape.getShape().getHeight(); } public void setHeight(float height) { Shape s = shape.getShape(); s.resize(s.getWidth(), height); } public void resizeShape(final float width, final float height) { shape.getShape().resize(width, height); } public ShapeHolder(ShapeDrawable s) { shape = s; } } }