blog

クールなAndroidの時計のUIコントロールは、隣の製品は泣いて貪欲である!

時計のディスクを切り替えて時計を調整し、時間の経過とともに、ディスクは自動的に元の位置に戻すことができ、相互作用の論理は自然で滑らかです。 異なるテーマカラーを設定すると、「静か」、「夜」、「月明かり...

Jul 19, 2020 · 5 min. read
シェア

それでは、結果をご覧ください。

クールなエフェクト、リッチなアニメーション、爆発的なエフェクト

ドーン

I. デザイン思考

時計コントロールの醜いデザインはもう見飽きました!現実の振り子時計の丸い文字盤デザインと、電子時計のデジタル表示の文字盤デザインを抽象化し、「丸」「デジタル表示」「時間の経過感」などの言葉を抽出し、最終的なUIデザインに融合させました。丸」「デジタル表示」「時間の経過感」などの言葉を抽出し、その言葉の特徴を融合させ、最終的なUIデザインに落とし込むことで、かっこいいUIコントロールが誕生しました!

時計ディスクをトグルして時計を調整し、時間の経過とともに、トグルディスクも自動的に元の位置に戻ることができ、相互作用の論理は自然でスムーズです。

静寂」、「夜」、「月光」、「純粋」など、より多くの意味合いを反映させるために、さまざまなテーマカラーを設定できます。「など。コントロールのデザイン自体は非常にスケーラブルです。

II.実施方法

デザインのアイデアが明確になって、それを実現する方法を考えなければなりません。誰か、スローガンに乗ってください。

誰もいません。

私よりも。

よく分かっています。

実現

クラスデザイン

あなたがUI図から観察することができるように、クロックコントロールは、4つの大きなダイヤル、すなわち午前と午後のダイヤル、時間ダイヤル、分ダイヤル、および秒ダイヤルで構成されています。アイデアの実装では、まずディスクコントロールの親クラスDiskViewの抽象化を検討し、ダイヤルの残りの部分は、DiskViewから継承することができます。ダイヤルの様々な、そして最後にそれらのすべてを組み立てるためにViewGroupを使用することができます。

DiskView は基本クラスとして、アニメーション、ドラッグ、クリック、その他のインタラクションのロジックを引き受ける必要があり、文字盤の半径、文字盤の回転角度などのパブリックプロパティも持っています。

public class DiskView extends View {
 private static final String TAG = "DiskView";
 Context mContext;
 /**
 * ディスク半径
 */
 int mRadius = 0;
 /**
 * 最初の指のプレスの座標
 */
 float startX, startY;
 /**
 * 現在の指のプレスポイントの座標
 */
 float curX, curY;
 /**
 * 指が初めて押される位置と、最初の位置との間の角度。
 */
 int startDegree;
 /**
 * 指が押される位置と初期位置との角度。
 */
 int curDegree;
 /**
 * 初期位置に対するディスクの現在位置の角度、初期位置の角度は0度である。
 */
 int degree = 0;
 /**
 * 指を離した後、元の状態に戻す必要があるか?
 */
 boolean isNeedReturn = true;
 ValueAnimator animator;
}

UI

座標と角度が決まったら、次は描画を考えます。canvas graphics drawing apiを使って描画し、各図形の位置を計算し、対応する色を与えます。円の中心を中心にrotateメソッドを呼び出し、ある角度でテキストを描画します。

@Override
protected void onDraw(Canvas canvas) {
 super.onDraw(canvas);
 // 
 mPaint.setColor(diskColor);
 canvas.drawCircle(mRadius, mRadius, mRadius, mPaint);
 // 
 mPaint.setColor(numColor);
 Rect bounds = new Rect();
 for (int i = 0; i < 60; i++) {
 if (i == minute) {
 mPaint.setColor(selectNumColor);
 } else {
 mPaint.setColor(numColor);
 }
 if (i % 10 != 0) {
 if (i % 5 == 0) {
 canvas.drawCircle(mRadius, 2 * mRadius - textHeight * 3 / 2, DisplayUtils.sp2px(mContext, 20) / 4, mPaint);
 } else {
 canvas.drawCircle(mRadius, 2 * mRadius - textHeight * 3 / 2, DisplayUtils.sp2px(mContext, 20) / 6, mPaint);
 }
 } else {
 mPaint.getTextBounds(i + "", 0, (i + "").length(), bounds);
 textHeight = bounds.height();
 canvas.drawText(i + "", mRadius - bounds.width() / 2, mRadius * 2 - bounds.height(), mPaint);
 }
 canvas.rotate(-6, mRadius, mRadius);
 }
}

インタラクション・ロジック

コントロールのインタラクションロジックのほとんどは、それぞれonTouchEventのコールバックで処理され、ユーザーのクリック、移動、リフトアクションは、ターゲットを絞った処理を行うには、キーのコアは、各ケースのディスクの角度を計算することであり、その後、アニメーターを介して良好な対応する値を計算するために、リアルタイムのインターフェイスをリフレッシュすることができます。注意しなければならないのは、ユーザーの始点が円盤の限界を超えないことです。

@Override
public boolean onTouchEvent(MotionEvent event) {
 curX = event.getX();
 curY = event.getY();
 switch (event.getAction()) {
 case MotionEvent.ACTION_DOWN:
 startX = event.getX();
 startY = event.getY();
 startDegree = computeCurrentAngle(curX, curY);
 //出発点はディスクの境界を越えてはならない
 if (Math.sqrt(
 (startX - mRadius) * (startX - mRadius) + (startY - mRadius) * (startY - mRadius)
 ) > mRadius) {
 startDegree = 0;
 }
 break;
 case MotionEvent.ACTION_MOVE:
 //出発点はディスクの境界を越えてはならない
 if (Math.sqrt(
 (startX - mRadius) * (startX - mRadius) + (startY - mRadius) * (startY - mRadius)
 ) > mRadius) {
 return false;
 }
 curDegree = computeCurrentAngle(curX, curY);
 postInvalidate();
 break;
 case MotionEvent.ACTION_UP:
 if (Math.sqrt(
 (startX - mRadius) * (startX - mRadius) + (startY - mRadius) * (startY - mRadius)
 ) > mRadius) {
 return false;
 }
 int tmpDegree = degree;//指で押す前のディスクの角度
 degree = degree + curDegree - startDegree;
 if (Math.abs(degree) > 360) {
 degree %= 360;
 }
 startDegree = 0;
 curDegree = 0;
 startX = 0;
 startY = 0;
 //ポジションに戻る必要があるかどうか
 if (isNeedReturn) {
 animator = ValueAnimator.ofInt(degree, tmpDegree);
 animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
 @Override
 public void onAnimationUpdate(ValueAnimator animation) {
 degree = (int) animation.getAnimatedValue();
 postInvalidate();
 }
 });
 animator.setDuration(200);
 animator.setInterpolator(new DecelerateInterpolator());
 animator.start();
 }
 break;
 }
 return true;
}

III.あとがき

Androidの開発において重要なカスタムコントロールの開発は、各APIをいかにうまく活用して機能を実現するかという側面もあれば、トップダウンでいかに設計するかという側面もあります。開発において最も重要なことは、どのように実現するか、どのように設計するかを構想することではなく、ユーザーのニーズを発見し、逆にどのような機能を持たせるべきかというニーズから、機能的な観点から、どのようにクリエイティブな設計を行うかを検討し、最終的にユーザーに提示することです。そうすることで、より良い製品を作ることができるのです。

ギティのコントロール

Read next

JVMの詳細:クラスのロード、リンク、初期化

Javaのクラスファイルを持った後、クラスファイルをJVMが実際に実行できる構造に変換するためには、ロード、リンク、初期化のプロセスを経る必要があります。 この3つのプロセスはどのように行われるのでしょうか?この記事では、その答えを見つけます。 JVMは、3つの主要な部分、5つのスペースと3つのエンジンに分けることができ、それが特に複雑ではないと言うために、まず、次の全体的なJVMアーキテクチャを見てください。 から...

Jul 19, 2020 · 4 min read