/*
* Copyright (C) 2015 RECRUIT LIFESTYLE CO., LTD.
*
* 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.
*/
package jp.co.recruit_lifestyle.android.widget;
import jp.co.recruit_lifestyle.android.widget.character.AkPath;
import jp.co.recruit_lifestyle.android.widget.character.ButterflyPath;
import jp.co.recruit_lifestyle.android.widget.character.CatPath;
import jp.co.recruit_lifestyle.android.widget.character.CucumberPath;
import jp.co.recruit_lifestyle.android.widget.character.DogezaPath;
import jp.co.recruit_lifestyle.android.widget.character.HairStylePath;
import jp.co.recruit_lifestyle.android.widget.character.NinjaPath;
import jp.co.recruit_lifestyle.android.widget.character.NinjaStarPath;
import jp.co.recruit_lifestyle.android.widget.character.StormPath;
import jp.co.recruit_lifestyle.android.widget.character.ToothPath;
import jp.co.recruit_lifestyle.android.widget.character.ViolinPath;
import android.content.Context;
import android.graphics.BlurMaskFilter;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.CornerPathEffect;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PathMeasure;
import android.graphics.RectF;
import android.graphics.Region;
import android.os.Handler;
import android.support.v4.view.ViewCompat;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
/**
* @author amyu
*/
public class ColoringLoadingView extends View {
private PencilPath mPencilPath;
private BackgroundPath mBackgroundPath;
private Paint mCharacterPaint;
private Paint mPencilPaint;
private Paint mColoringPaint;
private Paint mLoadingPaint;
private Paint mBackgroundPaint;
private Paint mShadowPaint;
private Path mCharacterPath;
private Path mLoadingPath;
private PathMeasure mCharacterPathMeasure;
private PathMeasure mLoadingPathMeasure;
private int mViewWidth;
private float[] mCenterPoint = new float[2];
private float[] mCurrentPoint = new float[2];
private boolean isHandWriting = false;
private Handler mCharacterHandler;
private Handler mLoadingHandler;
private int mCharacterRunnableCount = 0;
private int mLoadingRunnableCount = 0;
private RectF mRectF;
private Region mRegion;
private Path mColoringPath;
private float[] mCurrentTouchPoint = new float[2];
private boolean isPreDraw = false;
private Character mCharacter;
public ColoringLoadingView(Context context) {
this(context, null, 0);
}
public ColoringLoadingView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public ColoringLoadingView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView();
mLoadingHandler = new Handler();
mCharacterHandler = new Handler();
mRectF = new RectF();
mRegion = new Region();
setLayerType(View.LAYER_TYPE_SOFTWARE, null);
}
private void initView() {
initPaint();
}
private void initPath() {
mColoringPath = new Path();
mLoadingPath = new Path();
mCharacterPath = new Path();
mPencilPath = new PencilPath();
mPencilPath.setWidth(mViewWidth / 6);
Path loadingPath = LoadingPath.getPath(mViewWidth);
mLoadingPathMeasure = new PathMeasure(loadingPath, false);
mBackgroundPath = new BackgroundPath();
mBackgroundPath.setWidth(mViewWidth);
}
private void initPaint() {
mCharacterPaint = new Paint();
mCharacterPaint.setColor(Color.BLACK);
mCharacterPaint.setStyle(Paint.Style.STROKE);
mCharacterPaint.setStrokeWidth(20);
mCharacterPaint.setStrokeCap(Paint.Cap.ROUND);
mCharacterPaint.setPathEffect(new CornerPathEffect(10));
mCharacterPaint.setAntiAlias(true);
mLoadingPaint = new Paint();
mLoadingPaint.setColor(Color.BLACK);
mLoadingPaint.setStrokeWidth(10);
mLoadingPaint.setStyle(Paint.Style.STROKE);
mLoadingPaint.setStrokeCap(Paint.Cap.ROUND);
mLoadingPaint.setPathEffect(new CornerPathEffect(10));
mLoadingPaint.setAntiAlias(true);
mPencilPaint = new Paint();
mPencilPaint.setStyle(Paint.Style.FILL);
mPencilPaint.setColor(Color.BLACK);
mPencilPaint.setAntiAlias(true);
mColoringPaint = new Paint();
mColoringPaint.setStyle(Paint.Style.STROKE);
mColoringPaint.setStrokeWidth(30);
mColoringPaint.setStrokeCap(Paint.Cap.ROUND);
mColoringPaint.setAntiAlias(true);
mColoringPaint.setColor(0xffF8C92C);
mBackgroundPaint = new Paint();
mBackgroundPaint.setStyle(Paint.Style.FILL);
mBackgroundPaint.setAntiAlias(true);
mBackgroundPaint.setColor(Color.WHITE);
mShadowPaint = new Paint();
mShadowPaint.setStyle(Paint.Style.FILL);
mShadowPaint.setColor(0x70000000);
mShadowPaint.setMaskFilter(new BlurMaskFilter(16, BlurMaskFilter.Blur.NORMAL));
}
@Override protected void onSizeChanged(int w, int h, int oldw, int oldh) {
mViewWidth = w;
mCenterPoint[0] = w / 2.f;
mCenterPoint[1] = h / 2.f;
initPath();
isPreDraw = true;
setCharacter(mCharacter);
if (isStartAnimationFlag) {
startDrawAnimation();
isStartAnimationFlag = false;
}
super.onSizeChanged(w, h, oldw, oldh);
}
public void startDrawAnimation() {
if (!isPreDraw) {
isStartAnimationFlag = true;
return;
}
stopHandler();
mPencilPaint.setColor(Color.BLACK);
isHandWriting = true;
mLoadingPath.reset();
mCharacterPath.reset();
mColoringPath.reset();
mCharacterHandler.post(mCharacterRunnable);
mCharacterRunnableCount = 0;
}
private Runnable mCharacterRunnable = new Runnable() {
@Override public void run() {
if ((mCharacterRunnableCount / 200.f) == 1.f) {
mCharacterHandler.removeCallbacks(this);
mLoadingHandler.post(mLoadingRunnable);
mLoadingRunnableCount = 0;
return;
}
float[] point = new float[2];
mCharacterPathMeasure.getPosTan(
mCharacterPathMeasure.getLength() * (mCharacterRunnableCount / 200.f), point, null);
if (mCharacterRunnableCount == 0) {
mCharacterPath.moveTo(point[0], point[1]);
} else {
mCharacterPath.lineTo(point[0], point[1]);
}
mCurrentPoint = point;
ViewCompat.postInvalidateOnAnimation(ColoringLoadingView.this);
mCharacterHandler.postDelayed(this, 10);
mCharacterRunnableCount++;
}
};
private Runnable mLoadingRunnable = new Runnable() {
@Override public void run() {
if ((mLoadingRunnableCount / 100.f) == 1.f) {
mLoadingHandler.removeCallbacks(this);
mLoadingPath.addPath(LoadingPath.getCirclePath(mViewWidth));
ViewCompat.postInvalidateOnAnimation(ColoringLoadingView.this);
isHandWriting = false;
mPencilPaint.setColor(mColoringPaint.getColor());
return;
}
float[] point = new float[2];
mLoadingPathMeasure.getPosTan(
mLoadingPathMeasure.getLength() * (mLoadingRunnableCount / 100.f), point, null);
if (mLoadingRunnableCount == 0) {
mLoadingPath.moveTo(point[0], point[1]);
} else {
mLoadingPath.lineTo(point[0], point[1]);
}
mCurrentPoint = point;
ViewCompat.postInvalidateOnAnimation(ColoringLoadingView.this);
mLoadingHandler.postDelayed(this, 10);
mLoadingRunnableCount++;
}
};
@Override protected void onDraw(Canvas canvas) {
canvas.drawPath(mBackgroundPath.getShadowPath(), mShadowPaint);
canvas.drawPath(mBackgroundPath.getBackgroundPath(), mBackgroundPaint);
if (isHandWriting) {
//Auto
canvas.drawPath(mCharacterPath, mCharacterPaint);
canvas.drawPath(mLoadingPath, mLoadingPaint);
canvas.drawPath(mPencilPath.getPencilPath(mCurrentPoint), mPencilPaint);
} else {
//Touch
canvas.drawPath(mColoringPath, mColoringPaint);
canvas.drawPath(mCharacterPath, mCharacterPaint);
canvas.drawPath(mPencilPath.getPencilPath(mCurrentTouchPoint), mPencilPaint);
canvas.drawPath(mLoadingPath, mLoadingPaint);
}
}
@Override protected void onDetachedFromWindow() {
stopHandler();
super.onDetachedFromWindow();
}
private void stopHandler() {
mCharacterHandler.removeCallbacks(mCharacterRunnable);
mLoadingHandler.removeCallbacks(mLoadingRunnable);
}
@Override public boolean onTouchEvent( MotionEvent event) {
if (isHandWriting) {
return true;
}
mCurrentTouchPoint[0] = event.getX();
mCurrentTouchPoint[1] = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mColoringPath.moveTo(mCurrentTouchPoint[0], mCurrentTouchPoint[1]);
return true;
case MotionEvent.ACTION_MOVE:
if (mRegion.contains((int) mCurrentTouchPoint[0], (int) mCurrentTouchPoint[1])) {
mColoringPath.lineTo(mCurrentTouchPoint[0], mCurrentTouchPoint[1]);
} else {
mColoringPath.moveTo(mCurrentTouchPoint[0], mCurrentTouchPoint[1]);
}
ViewCompat.postInvalidateOnAnimation(this);
break;
}
return true;
}
public void setColoringColor(int color) {
mColoringPaint.setColor(color);
invalidate();
}
public void setCnavasColor(int color) {
mBackgroundPaint.setColor(color);
invalidate();
}
public void setLineColor(int color) {
mLoadingPaint.setColor(color);
mCharacterPaint.setColor(color);
}
private boolean isStartAnimationFlag = false;
public void setCharacter(Character character) {
if (!isPreDraw) {
mCharacter = character;
return;
}
Path characterPath;
switch (character) {
case NINJA:
characterPath = NinjaPath.getNinjaPath(mViewWidth / 1.5f, mCenterPoint);
break;
case BUTTERFLY:
characterPath = ButterflyPath.getButterflyPath(mViewWidth / 1.5f, mCenterPoint);
break;
case AK:
characterPath = AkPath.getAkPath(mViewWidth / 1.5f, mCenterPoint);
break;
case HAIR_STYLE:
characterPath = HairStylePath.getHairStylePath(mViewWidth / 1.5f, mCenterPoint);
break;
case TOOTH:
characterPath = ToothPath.getToothPath(mViewWidth / 1.5f, mCenterPoint);
break;
case STORM:
characterPath = StormPath.getStormPath(mViewWidth / 1.5f, mCenterPoint);
break;
case DOGEZA:
characterPath = DogezaPath.getDogezaPath(mViewWidth / 1.5f, mCenterPoint);
break;
case CAT:
characterPath = CatPath.getCatPath(mViewWidth / 1.5f, mCenterPoint);
break;
case VIOLIN:
characterPath = ViolinPath.getViolinPath(mViewWidth / 1.5f, mCenterPoint);
break;
case CUCUMBER:
characterPath = CucumberPath.getCucumberPath(mViewWidth / 1.5f, mCenterPoint);
break;
case NINJA_STAR:
characterPath = NinjaStarPath.getNinjaStarPath(mViewWidth / 1.5f, mCenterPoint);
break;
default:
characterPath = NinjaPath.getNinjaPath(mViewWidth / 1.5f, mCenterPoint);
break;
}
mCharacterPathMeasure = new PathMeasure(characterPath, false);
mRectF.setEmpty();
characterPath.computeBounds(mRectF, true);
mRegion.setEmpty();
mRegion.set((int) mRectF.left, (int) mRectF.top, (int) mRectF.right, (int) mRectF.bottom);
mRegion.setPath(characterPath, mRegion);
}
public enum Character {
NINJA(1),
BUTTERFLY(2),
AK(4),
HAIR_STYLE(5),
TOOTH(6),
STORM(8),
DOGEZA(9),
CAT(10),
VIOLIN(11),
CUCUMBER(12),
NINJA_STAR(13);
Character(int i) {
nativeInt = i;
}
final int nativeInt;
}
}