AndroidカスタムView時計振子効果進捗バーPendulumView
10434 ワード
インターネットで見たIOSユニットのPendulumViewは、振り子のアニメーション効果を実現しました。オリジナルのプログレスバーは確かに綺麗ではないので、Viewをカスタマイズしてこのような効果を実現したいです。今後はページのプログレスバーをロードするのにも使えます。
余計なことを言わないで、先に効果図を書きます。
下の黒い端は録音時に不注意で録音しました。無視できます。
カスタムViewなら標準的な流れで、第一歩、カスタム属性
カスタム属性
属性ファイルを作成
Androidプロジェクトのres->valuesディレクトリの下にatrs.xmlファイルを作成します。ファイルの内容は以下の通りです。
styleを使って、システムは私達のために多くの定数(int[配列],標準量)などの編纂を完成することができます。私達の開発作業を簡略化します。例えば、下記のコードで使うR.style.PendulumView。golbeNumなどはシステムが私たちのために自動的に生成します。
globeNum属性は小さいボールの数を表し、globeColorは小さいボールの色を表し、globeRadiusは小さいボールの半径を表し、swingRadiusはスイング半径を表します。
属性値を読み出す
自定viewの構造方法でTypedArayで属性値を読み出す
AttributeSetでも属性値を取得できますが、属性値が参照タイプであれば、IDだけを取得しても、解析IDで真の属性値を取得し続ける必要があります。TypedArayは直接に上記の作業を完了しました。
int height=mGlobeRadius+mSwingRadius;
<pre name=“code”class=“java”int width=mSwingRadius+mGlobeRadius*2*(mGlobeNum-1)+mSwingRadius;
測定モードを処理するためのAT_MOSTの場合は、カスタムViewの幅高設定が一般的です。wrap_contentは、この時点でボールの数、半径、スイングの半径などを通じて、Viewの幅の高さを計算します。
小さいボールの個数5を例にとって、Viewの大きさは下の図の赤い長方形の領域です。
書き換えonDraw()方法
その中でmLeftPointは、mRightPointともAndroid.graphics.Pointオブジェクトであり、それらを使って左右の2つのボールのx,y座標情報を保存します。
属性アニメーションを使う
mSwingRadius-mGlobeRadiusは図中の緑色の直線の長さを表しています。スイングのコースは、小さいボールの円心のコースは半径の弧であり、変化のX値は(mSwingRadius-mGloberadius)*sin(angggggladius)であり、変化の値はRadiusです。
対応するボールの実際の中心座標は(mSwingRadius-x,mGlobeRadius+y)です。
右の小さいボールの運動コースは左と似ていますが、方向が違っています。右のボールの実際の中心座標(mSwingRadius+(mGlobeNum-1)*mGlobeRadius*2+x,mGlobeRadius+y)
左右の小球の縦座標は同じで、横座標だけが違っています。
この時点をどう捉えますか?
小さいボールを投げる時はfractionの値がどんどん増えて、小さなボールが落ちた時はfractionの値がどんどん減っています。ボールがスローされる瞬間は、fractionが減少してから増大していく瞬間です。コードに前回のfractionが減少しているかどうかを記録して、今度のfractionが増加しているかどうかを比較して、二つの条件が成立すれば運動のボールを切り換えます。
アニメーションの補間器を設置して、小さいボールが投げ始めるのは1つの次第に減速する過程なため、落ちるのは1つの次第に加速する過程で、だからDecell erate Interpolatorを使って減速の効果を実現して、倒順が放送する時加速する効果です。
動画を起動すると、時計の振り子効果をカスタマイズしたViewの進捗バーが実現します。早く運行して効果を見てください。
以上が本文の全部です。皆さんの勉強に役に立つように、私たちを応援してください。
余計なことを言わないで、先に効果図を書きます。
下の黒い端は録音時に不注意で録音しました。無視できます。
カスタムViewなら標準的な流れで、第一歩、カスタム属性
カスタム属性
属性ファイルを作成
Androidプロジェクトのres->valuesディレクトリの下にatrs.xmlファイルを作成します。ファイルの内容は以下の通りです。
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="PendulumView">
<attr name="globeNum" format="integer"/>
<attr name="globeColor" format="color"/>
<attr name="globeRadius" format="dimension"/>
<attr name="swingRadius" format="dimension"/>
</declare-styleable>
</resources>
ここで、declare-styleのname属性は、コード内でこの属性ファイルを参照するために使用されます。name属性は、一般的に私たちがカスタマイズしたViewの類名で、より直感的です。styleを使って、システムは私達のために多くの定数(int[配列],標準量)などの編纂を完成することができます。私達の開発作業を簡略化します。例えば、下記のコードで使うR.style.PendulumView。golbeNumなどはシステムが私たちのために自動的に生成します。
globeNum属性は小さいボールの数を表し、globeColorは小さいボールの色を表し、globeRadiusは小さいボールの半径を表し、swingRadiusはスイング半径を表します。
属性値を読み出す
自定viewの構造方法でTypedArayで属性値を読み出す
AttributeSetでも属性値を取得できますが、属性値が参照タイプであれば、IDだけを取得しても、解析IDで真の属性値を取得し続ける必要があります。TypedArayは直接に上記の作業を完了しました。
public PendulumView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
// TypedArray
TypedArray ta = context.getResources().obtainAttributes(attrs, R.styleable.PendulumView);
int count = ta.getIndexCount();
for (int i = 0; i < count; i++) {
int attr = ta.getIndex(i);
switch (attr) {
case R.styleable.PendulumView_globeNum:
mGlobeNum = ta.getInt(attr, 5);
break;
case R.styleable.PendulumView_globeRadius:
mGlobeRadius = ta.getDimensionPixelSize(attr, (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, 16, getResources().getDisplayMetrics()));
break;
case R.styleable.PendulumView_globeColor:
mGlobeColor = ta.getColor(attr, Color.BLUE);
break;
case R.styleable.PendulumView_swingRadius:
mSwingRadius = ta.getDimensionPixelSize(attr, (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, 16, getResources().getDisplayMetrics()));
break;
}
}
ta.recycle(); //
mPaint = new Paint();
mPaint.setColor(mGlobeColor);
}
書き換え方法
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
// +
int height = mGlobeRadius + mSwingRadius;
// 2* +( -1)*
int width = mSwingRadius + mGlobeRadius * 2 * (mGlobeNum - 1) + mSwingRadius;
// EXACTLY, , EXACTLY( wrap_content ),
setMeasuredDimension((widthMode == MeasureSpec.EXACTLY) ? widthSize : width, (heightMode == MeasureSpec.EXACTLY) ? heightSize : height);
}
そのうちint height=mGlobeRadius+mSwingRadius;
<pre name=“code”class=“java”int width=mSwingRadius+mGlobeRadius*2*(mGlobeNum-1)+mSwingRadius;
測定モードを処理するためのAT_MOSTの場合は、カスタムViewの幅高設定が一般的です。wrap_contentは、この時点でボールの数、半径、スイングの半径などを通じて、Viewの幅の高さを計算します。
小さいボールの個数5を例にとって、Viewの大きさは下の図の赤い長方形の領域です。
書き換えonDraw()方法
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//
for (int i = 0; i < mGlobeNum - 2; i++) {
canvas.drawCircle(mSwingRadius + (i + 1) * 2 * mGlobeRadius, mSwingRadius, mGlobeRadius, mPaint);
}
if (mLeftPoint == null || mRightPoint == null) {
//
mLeftPoint = new Point(mSwingRadius, mSwingRadius);
mRightPoint = new Point(mSwingRadius + mGlobeRadius * 2 * (mGlobeNum - 1), mSwingRadius);
//
startPendulumAnimation();
}
//
canvas.drawCircle(mLeftPoint.x, mLeftPoint.y, mGlobeRadius, mPaint);
canvas.drawCircle(mRightPoint.x, mRightPoint.y, mGlobeRadius, mPaint);
}
onDraw()メソッドはViewをカスタマイズするための鍵となり、この方法の中にViewの表示効果を描画します。コードはまず一番左の一番右の小さいボール以外の小さいボールを描いて、左右の二つの小さいボールの座標値を判断します。初めて描いた場合、座標値が全部空だったら、二つの小さいボールの座標を初期化して、アニメーションを開始します。最後にmLeftPoint、mRightPointのx、y値を通して、左右の二つの小さいボールを描きます。 その中でmLeftPointは、mRightPointともAndroid.graphics.Pointオブジェクトであり、それらを使って左右の2つのボールのx,y座標情報を保存します。
属性アニメーションを使う
public void startPendulumAnimation() {
//
final ValueAnimator anim = ValueAnimator.ofObject(new TypeEvaluator() {
@Override
public Object evaluate(float fraction, Object startValue, Object endValue) {
// fraction ,
double angle = Math.toRadians(90 * fraction);
int x = (int) ((mSwingRadius - mGlobeRadius) * Math.sin(angle));
int y = (int) ((mSwingRadius - mGlobeRadius) * Math.cos(angle));
Point point = new Point(x, y);
return point;
}
}, new Point(), new Point());
anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
Point point = (Point) animation.getAnimatedValue();
// fraction
float fraction = anim.getAnimatedFraction();
// fraction ,
//
if (lastSlope && fraction > mLastFraction) {
isNext = !isNext;
}
// x,y
// isNext ,
if (isNext) {
// ,
mRightPoint.x = mSwingRadius + mGlobeRadius * 2 * (mGlobeNum - 1);
mRightPoint.y = mSwingRadius;
mLeftPoint.x = mSwingRadius - point.x;
mLeftPoint.y = mGlobeRadius + point.y;
} else {
// ,
mLeftPoint.x = mSwingRadius;
mRightPoint.y = mSwingRadius;
mRightPoint.x = mSwingRadius + (mGlobeNum - 1) * mGlobeRadius * 2 + point.x;
mRightPoint.y = mGlobeRadius + point.y;
}
invalidate();
lastSlope = fraction < mLastFraction;
mLastFraction = fraction;
}
});
//
anim.setRepeatCount(ValueAnimator.INFINITE);
//
anim.setRepeatMode(ValueAnimator.REVERSE);
anim.setDuration(200);
// ,
anim.setInterpolator(new DecelerateInterpolator());
anim.start();
}
この中でValue Animator.objectを使う方法はPointオブジェクトを操作できるように、より具体的にイメージするためです。また、ObjectによってカスタムType Everalatorオブジェクトを使用したことによりfract値が得られ、この値は0-1から変化する小数点である。したがって、この方法の後の二つのパラメータstartValue(new Point)は、endValue(new Point)は、実際的な意味がなく、直接書かなくてもいいです。ここには、理解しやすいように書いてあります。同様に、Value Animator.ofloat(0 f,1 f)方法を使って、0-1から変化する小数点を直接取得することもできる。
final ValueAnimator anim = ValueAnimator.ofObject(new TypeEvaluator() {
@Override
public Object evaluate(float fraction, Object startValue, Object endValue) {
// fraction ,
double angle = Math.toRadians(90 * fraction);
int x = (int) ((mSwingRadius - mGlobeRadius) * Math.sin(angle));
int y = (int) ((mSwingRadius - mGlobeRadius) * Math.cos(angle));
Point point = new Point(x, y);
return point;
}
}, new Point(), new Point());
fractionによって、小球が揺動する時の角度変化の値を計算します。0-90度です。mSwingRadius-mGlobeRadiusは図中の緑色の直線の長さを表しています。スイングのコースは、小さいボールの円心のコースは半径の弧であり、変化のX値は(mSwingRadius-mGloberadius)*sin(angggggladius)であり、変化の値はRadiusです。
対応するボールの実際の中心座標は(mSwingRadius-x,mGlobeRadius+y)です。
右の小さいボールの運動コースは左と似ていますが、方向が違っています。右のボールの実際の中心座標(mSwingRadius+(mGlobeNum-1)*mGlobeRadius*2+x,mGlobeRadius+y)
左右の小球の縦座標は同じで、横座標だけが違っています。
float fraction = anim.getAnimatedFraction();
// fraction ,
//
if (lastSlope && fraction > mLastFraction) {
isNext = !isNext;
}
// fraction
lastSlope = fraction < mLastFraction;
// fraction
mLastFraction = fraction;
この二つのコードはいつ運動のボールが切り替わるかを計算するために使われています。この動画は循環再生が設定されています。循環モードは逆再生です。だからアニメーションの周期はボールが投げられてボールが落ちる過程です。この過程でfractionの値は0から1になり、1から0になります。アニメの新しいサイクルの始まりはいつですか?小さいボールが間もなく投げ始める時、この時に運動の小さいボールを変えて、左の小さいボールが落ちた後に右の小さいボールが投げ始めることを実現して、右の小さいボールが落ちた後に左の小さいボールの投げるアニメーションの効果。 この時点をどう捉えますか?
小さいボールを投げる時はfractionの値がどんどん増えて、小さなボールが落ちた時はfractionの値がどんどん減っています。ボールがスローされる瞬間は、fractionが減少してから増大していく瞬間です。コードに前回のfractionが減少しているかどうかを記録して、今度のfractionが増加しているかどうかを比較して、二つの条件が成立すれば運動のボールを切り換えます。
anim.setDuration(200);
// ,
anim.setInterpolator(new DecelerateInterpolator());
anim.start();
アニメーションの持続時間を200ミリ秒に設定します。読者はこの値を変更することで、小球のスイング速度を変更することができます。アニメーションの補間器を設置して、小さいボールが投げ始めるのは1つの次第に減速する過程なため、落ちるのは1つの次第に加速する過程で、だからDecell erate Interpolatorを使って減速の効果を実現して、倒順が放送する時加速する効果です。
動画を起動すると、時計の振り子効果をカスタマイズしたViewの進捗バーが実現します。早く運行して効果を見てください。
以上が本文の全部です。皆さんの勉強に役に立つように、私たちを応援してください。