English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

تحقيق تأثير دائرة الأداء في QQ ومؤثرات الرسم المتحرك باستخدام Android

في تطبيق حساب الخطوات الدقيقة جدًا في أندرويد - Dylan، تم استخدام وحدة تحكم مخصصة في الصفحة الرئيسية، يشبه واجهة QQ للرياضة، بالإضافة إلى تأثيرات حركة، وسأشرح فيما يلي كيفية رسم هذه View.

1. أولاً، ننظر إلى الصور

تحليل الصور

شرح الوظيفة: الأصفر يمثل عدد خطوات التمرين المخطط له من قبل المستخدم، والأحمر يمثل عدد الخطوات التي قام بها المستخدم حاليًا.

تحليل أولي: إعادة كتابة View كاملًا من خلال كتابة طريقة onDraw()، رسم الدائرة.

3. المعرفة اللازمة لرسم الدائرة

في Canvas هناك طريقة للرسم بالدائرة

drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter, Paint paint)// رسم دائرة

المفارقات الأولى هي object RectF، منطقة مستطيلة محددة بشكل مركزي مستطيلة مستديرة للحد من الشكل، الحجم، الحلقة

المفارقات الثانية هي الزاوية البداية (درجة) في بداية الحلقة، زاوية البداية للحلقة، وحدة القياس درجة

المفارقات الثالثة هي زاوية الحلقة المائلة، باتجاه الساعة، وحدة القياس درجة، تبدأ من الصفر درجة في المنتصف الأيمن

المعلمة الرابعة هي إذا كان الحدث الحقيقي (صحيح) عند رسم الدائرة فإن المركز يتم تضمينه، عادة ما يتم استخدامه لرسم المروحة؛ إذا كان الحدث الزائف (خطأ) فإنه سيكون خطًا منحني.

المفارقات الخامسة هي Paint object;

بالنسبة لهذه الطريقة، يمكنكم النظر في الرسم اليدوي الذي قمت به، وهو سيء جدًا، لتفسير معنى هذه المعلمات والعملية الرسومية، الرسم سيء، وأرجو منكم التسامح!

4. التحضير للرسم

(1). الحصول على إحداثيات المركز

/**中心点的x坐标*/
float centerX = (getWidth()) / 2;

(2). إنشاء مستطيل مرجعي خارج الدائرة

/**指定圆弧的外轮廓矩形区域*/
RectF rectF = new RectF(0 + borderWidth, borderWidth, 2 * centerX - borderWidth, 2 * centerX - borderWidth);

5. الخطوات الرئيسية للرسم

(1).【الخطوة الأولى】 رسم الدائرة الصفراء الكاملة

/**
* 1.绘制总步数的黄色圆弧
*
* @param canvas 画笔
* @param rectF 参考的矩形
*/
private void drawArcYellow(Canvas canvas, RectF rectF) {

/** 默认画笔颜色,黄色 */
paint.setColor(getResources().getColor(R.color.yellow));
/** 结合处为圆弧*/
paint.setStrokeJoin(Paint.Join.ROUND);
/** 设置画笔的样式 Paint.Cap.Round ,Cap.SQUARE等分别为圆形、方形*/
paint.setStrokeCap(Paint.Cap.ROUND);
/** 设置画笔的填充样式 Paint.Style.FILL :填充内部;Paint.Style.FILL_AND_STROKE :填充内部和描边; Paint.Style.STROKE :仅描边*/
paint.setStyle(Paint.Style.STROKE);
/**抗锯齿功能*/
paint.setAntiAlias(true);
/**设置画笔宽度*/
paint.setStrokeWidth(borderWidth);
/** طريقة رسم الحلقة
* drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter, Paint paint)// رسم الحلقة،
المفارقات الأولى هي object RectF، منطقة مستطيلة محددة بشكل مركزي مستطيلة مستديرة للحد من الشكل، الحجم، الحلقة
المفارقات الثانية هي الزاوية البداية (درجة) في بداية الحلقة، زاوية البداية للحلقة، وحدة القياس درجة
المفارقات الثالثة هي زاوية الحلقة المائلة، باتجاه الساعة، وحدة القياس درجة، تبدأ من الصفر درجة في المنتصف الأيمن
المفارقات الرابعة هي إذا كان هذا true (صحيح) عند رسم الحلقة سيتم تضمين مركز الحلقة، عادة ما يتم استخدامه لرسم الدوائر الشعبية؛ إذا كان هذا false (خطأ) سيكون هذا خطًا دائريًا
المفارقات الخامسة هي Paint object;
*/
canvas.drawArc(rectF, startAngle, angleLength, false, paint);
}

(2).【الخطوة الثانية】 رسم الدائرة الحمراء للتقدم الحالي

/**
* 2. رسم الحلقة الحمراء الحالية
*/
private void drawArcRed(Canvas canvas, RectF rectF) {
Paint paintCurrent = new Paint();
paintCurrent.setStrokeJoin(Paint.Join.ROUND);
paintCurrent.setStrokeCap(Paint.Cap.ROUND);//زوايا الحدادة المستديرة
paintCurrent.setStyle(Paint.Style.STROKE);//تعيين نمط التحديد
paintCurrent.setAntiAlias(true);//وظيفة الحدادة المضادة
paintCurrent.setStrokeWidth(borderWidth);//تعيين عرض القلم
paintCurrent.setColor(getResources().getColor(R.color.red));//تعيين لون القلم
canvas.drawArc(rectF, startAngle, currentAngleLength, false, paintCurrent);
}

(3).【الخطوة الثالثة】 رسم الأرقام الحمراء للتقدم الحالي

/**
* 3. الحلقة المركزية لعدد الخطوات
*/
private void drawTextNumber(Canvas canvas, float centerX) {
Paint vTextPaint = new Paint();
vTextPaint.setTextAlign(Paint.Align.CENTER);
vTextPaint.setAntiAlias(true); // ميزة التمويه
vTextPaint.setTextSize(numberTextSize);
Typeface font = Typeface.create(Typeface.SANS_SERIF, Typeface.NORMAL);
vTextPaint.setTypeface(font); // نمط الخط
vTextPaint.setColor(getResources().getColor(R.color.red));
paint.setTextSize(fontSize);
vTextPaint.getTextBounds(stepNumber, 0, stepNumber.length(), bounds_Number);
canvas.drawText(stepNumber, centerX, getHeight() / 2 + bounds_Number.height() / 2, vTextPaint);
}

(4).【الخطوة الرابعة】 رسم الأرقام الحمراء لـ 'الخطوات'

/**
* النص الموجود في وسط الدائرة [عدد الخطوات]
*/
private void drawTextStepString(Canvas canvas, float centerX) {
Paint vTextPaint = new Paint();
vTextPaint.setTextSize(dipToPx(16));
vTextPaint.setTextAlign(Paint.Align.CENTER);
vTextPaint.setAntiAlias(true); // ميزة التمويه
vTextPaint.setColor(getResources().getColor(R.color.grey));
String stepString = "عدد الخطوات";
Rect bounds = new Rect();
vTextPaint.getTextBounds(stepString, 0, stepString.length(), bounds);
canvas.drawText(stepString, centerX, getHeight() / 2 + bounds.height() + getFontHeight(numberTextSize), vTextPaint);
}

6. كيف يتم تنفيذ التحريك -> ValueAnimator

ValueAnimator هو أكثر الفئات الأساسية في ميكانيكية تحريك الخصائص، ويتم تنفيذ ميكانيكية تحريك الخصائص من خلال تعديل القيم باستمرار، حيث أن انتقال التحريك بين القيمة الأولية والنهاية يتم من خلال حساب ValueAnimator هذا الفئة. يستخدم الداخل باستمرار ميكانيكية دائرة زمنية لحساب انتقال القيم، حيث يكفي تقديم القيمة الأولية والنهاية لـ ValueAnimator وتخبره بالمدة التي يجب تشغيل التحريك فيها، فإن ValueAnimator سيساعدنا تلقائيًا في تحقيق تأثير انتقال سلس من القيمة الأولية إلى القيمة النهائية.

/* إعداد تحريك التقدم */
* @param start القيمة الأولية
* @param current القيمة النهائية
* @param length فترة التحريك
*/
private void setAnimation(float start, float current, int length) {
ValueAnimator progressAnimator = ValueAnimator.ofFloat(start, current);
progressAnimator.setDuration(length);
progressAnimator.setTarget(currentAngleLength);
progressAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
/** قيمة سلسة منسقة تنتج بين القيمة الأولية والنهاية في كل مرة */
currentAngleLength = (float) animation.getAnimatedValue();
invalidate();
}
});
progressAnimator.start();
}

7. كود مصدر CustomStepArcView كله

إدخال android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Typeface;
import android.util.AttributeSet;
import android.view.View;
import cn.bluemobi.dylan.step.R;
/**
* Created by DylanAndroid on 2016/5/26.
* 显示步数的圆弧
*/
public class StepArcView extends View {
/**
* 圆弧的宽度
*/
private float borderWidth = 38f;
/**
* 画步数的数值的字体大小
*/
private float numberTextSize = 0;
/**
* 步数
*/
private String stepNumber = "0";
/**
* 开始绘制圆弧的角度
*/
private float startAngle = 135;
/**
* 终点对应的角度和起始点对应的角度的夹角
*/
private float angleLength = 270;
/**
* 所要绘制的当前步数的红色圆弧终点到起点的夹角
*/
private float currentAngleLength = 0;
/**
* 动画时长
*/
private int animationLength = 3000;
public StepArcView(Context context) {
super(context);
}
public StepArcView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public StepArcView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
/**中心点的x坐标*/
float centerX = (getWidth()) / 2;
/**指定圆弧的外轮廓矩形区域*/
RectF rectF = new RectF(0 + borderWidth, borderWidth, 2 * centerX - borderWidth, 2 * centerX - borderWidth);
/**【第一步】绘制整体的黄色圆弧*/
drawArcYellow(canvas, rectF);
/**【第二步】绘制当前进度的红色圆弧*/
drawArcRed(canvas, rectF);
/**【第三步】绘制当前进度的红色数字*/
drawTextNumber(canvas, centerX);
/**【第四步】绘制"步数"的红色数字*/
drawTextStepString(canvas, centerX);
}
/**
* 1.绘制总步数的黄色圆弧
*
* @param canvas 画笔
* @param rectF 参考的矩形
*/
private void drawArcYellow(Canvas canvas, RectF rectF) {

/** 默认画笔颜色,黄色 */
paint.setColor(getResources().getColor(R.color.yellow));
/** 结合处为圆弧*/
paint.setStrokeJoin(Paint.Join.ROUND);
/** 设置画笔的样式 Paint.Cap.Round ,Cap.SQUARE等分别为圆形、方形*/
paint.setStrokeCap(Paint.Cap.ROUND);
/** 设置画笔的填充样式 Paint.Style.FILL :填充内部;Paint.Style.FILL_AND_STROKE :填充内部和描边; Paint.Style.STROKE :仅描边*/
paint.setStyle(Paint.Style.STROKE);
/**抗锯齿功能*/
paint.setAntiAlias(true);
/**设置画笔宽度*/
paint.setStrokeWidth(borderWidth);
/** طريقة رسم الحلقة
* drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter, Paint paint)// رسم الحلقة،
المفارقات الأولى هي object RectF، منطقة مستطيلة محددة بشكل مركزي مستطيلة مستديرة للحد من الشكل، الحجم، الحلقة
المفارقات الثانية هي الزاوية البداية (درجة) في بداية الحلقة، زاوية البداية للحلقة، وحدة القياس درجة
المفارقات الثالثة هي زاوية الحلقة المائلة، باتجاه الساعة، وحدة القياس درجة، تبدأ من الصفر درجة في المنتصف الأيمن
المفارقات الرابعة هي إذا كان هذا true (صحيح) عند رسم الحلقة سيتم تضمين مركز الحلقة، عادة ما يتم استخدامه لرسم الدوائر الشعبية؛ إذا كان هذا false (خطأ) سيكون هذا خطًا دائريًا
المفارقات الخامسة هي Paint object;
*/
canvas.drawArc(rectF, startAngle, angleLength, false, paint);
}
/**
* 2. رسم الحلقة الحمراء الحالية
*/
private void drawArcRed(Canvas canvas, RectF rectF) {
Paint paintCurrent = new Paint();
paintCurrent.setStrokeJoin(Paint.Join.ROUND);
paintCurrent.setStrokeCap(Paint.Cap.ROUND);//زوايا الحدادة المستديرة
paintCurrent.setStyle(Paint.Style.STROKE);//تعيين نمط التحديد
paintCurrent.setAntiAlias(true);//وظيفة الحدادة المضادة
paintCurrent.setStrokeWidth(borderWidth);//تعيين عرض القلم
paintCurrent.setColor(getResources().getColor(R.color.red));//تعيين لون القلم
canvas.drawArc(rectF, startAngle, currentAngleLength, false, paintCurrent);
}
/**
* 3. الحلقة المركزية لعدد الخطوات
*/
private void drawTextNumber(Canvas canvas, float centerX) {
Paint vTextPaint = new Paint();
vTextPaint.setTextAlign(Paint.Align.CENTER);
vTextPaint.setAntiAlias(true); // ميزة التمويه
vTextPaint.setTextSize(numberTextSize);
Typeface font = Typeface.create(Typeface.SANS_SERIF, Typeface.NORMAL);
vTextPaint.setTypeface(font); // نمط الخط
vTextPaint.setColor(getResources().getColor(R.color.red));
paint.setTextSize(fontSize);
vTextPaint.getTextBounds(stepNumber, 0, stepNumber.length(), bounds_Number);
canvas.drawText(stepNumber, centerX, getHeight() / 2 + bounds_Number.height() / 2, vTextPaint);
}
/**
* النص الموجود في وسط الدائرة [عدد الخطوات]
*/
private void drawTextStepString(Canvas canvas, float centerX) {
Paint vTextPaint = new Paint();
vTextPaint.setTextSize(dipToPx(16));
vTextPaint.setTextAlign(Paint.Align.CENTER);
vTextPaint.setAntiAlias(true); // ميزة التمويه
vTextPaint.setColor(getResources().getColor(R.color.grey));
String stepString = "عدد الخطوات";
Rect bounds = new Rect();
vTextPaint.getTextBounds(stepString, 0, stepString.length(), bounds);
canvas.drawText(stepString, centerX, getHeight() / 2 + bounds.height() + getFontHeight(numberTextSize), vTextPaint);
}
/**
* الحصول على ارتفاع الرقم الحالي للخط
*
* @param fontSize حجم الخط
* @return الارتفاع الحقيقي للخط
*/
public int getFontHeight(float fontSize) {

Paint paint = new Paint();
paint.setTextSize(fontSize);
Rect bounds_Number = new Rect();
paint.getTextBounds(stepNumber, 0, stepNumber.length(), bounds_Number);
}
/**
return bounds_Number.height();
*
* dip تحويل إلى px
* @param dip
*/
* @return
private int dipToPx(float dip) {
float density = getContext().getResources().getDisplayMetrics().density;
}
/**
return (int) (dip * density + 0.5f * (dip >= 0 ? 1 : -1));
*
* تقدم الخطوات المحررة
* @param totalStepNum عدد الخطوات المحدد
*/
* @param currentCounts عدد الخطوات المحررة
public void setCurrentCount(int totalStepNum, int currentCounts) {
stepNumber = currentCounts + "";
setTextSize(currentCounts);
/**إذا كانت الخطوات المحررة تتجاوز عدد الخطوات الإجمالي، فإن الدائرة ستكون 270 درجة، ولا يمكن أن تكون دائرية
if (currentCounts > totalStepNum) {
}
/**نسبة الخطوات المحررة إلى إجمالي عدد الخطوات
float scale = (float) currentCounts / totalStepNum;
/**تحويل إلى طول الدائرة النهائي للزاوية--> طول الدائرة
float currentAngleLength = scale * angleLength;
/**بدء تنفيذ التحريك*/
setAnimation(0, currentAngleLength, animationLength);
}
/**
* لتحديد تحريك التقدم
* ValueAnimator هو أكثر الفئات الأساسية في ميكانيكية تحريك الخصائص، ويتم تنفيذ ميكانيكية تحريك الخصائص من خلال تحرير القيم بشكل مستمر.
* ويتولى ValueAnimator حساب انتقال القيم بين القيمة الأولية والقيمة النهائية
* يستخدم ValueAnimator ميكانيكية دورة الوقت الداخلية لتحليل انتقال القيم بين القيم
* كل ما نحتاجه هو تقديم القيمة الأولية والقيمة النهائية لـ ValueAnimator، وإخبارها عن طول الوقت الذي يجب عليه تشغيله
* سيقوم ValueAnimator تلقائيًا بمساعدتنا في تحقيق انتقال سلس من القيمة الأولية إلى القيمة النهائية
*
* @param last
* @param current
*/
private void setAnimation(float last, float current, int length) {
ValueAnimator progressAnimator = ValueAnimator.ofFloat(last, current);
progressAnimator.setDuration(length);
progressAnimator.setTarget(currentAngleLength);
progressAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
currentAngleLength = (float) animation.getAnimatedValue();
invalidate();
}
});
progressAnimator.start();
}
/**
* ضبط حجم النص، لمنع أن يكون الخط كبير جدًا بعد ذلك وعدم وجود مساحة له، يتم ضبط حجم الخط بشكل ديناميكي
*
* @param num
*/
public void setTextSize(int num) {
String s = String.valueOf(num);
int length = s.length();
إذا (length <= 4) {
numberTextSize = dipToPx(50);
إذا (length > 4 && length <= 6) {
numberTextSize = dipToPx(40);
} else if (length > 6 && length <= 8) {
numberTextSize = dipToPx(30);
} else if (length > 8) {
numberTextSize = dipToPx(25);
}
}
}

8.شرح الاستخدام

في xml

<cn.bluemobi.dylan.step.view.StepArcView
android:id="@+id/sv "
android:layout_width="200dp"
android:layout_height="200dp"
android:layout_centerHorizontal="true"
android:layout_marginTop="50dp" />

في Activity

StepArcView sv = (StepArcView) findViewById(R.id.sv);
sv.setCurrentCount(7000, 1000);

ما ذكرته أعلاه هو ما قدمه المحرر لكم من Android مقلد QQ رياضية الخطوات الدائرية والإجراءات المتنقلة، آمل أن يكون مفيدًا لكم، إذا كان لديكم أي استفسارات، يرجى ترك تعليق، وسأقوم بالرد على رسائلكم في الوقت المناسب. وأود أيضًا أن أتعبّر عن امتناني للدعم الذي قدمته لمنصة呐喊 لتعليم البرمجة!

بيان: محتوى هذا المقال تم جمعه من الإنترنت، ملكية الحقوق مملوكة للمساهمين، ويتم جمع المحتوى من قبل المستخدمين على الإنترنت بطرق متفرقة، ويتم إدارة هذا الموقع دون امتلاك حقوق الملكية، ولا يتم تعديل المحتوى بشكل يدوي، ولا يتحمل الموقع أي مسؤولية قانونية. إذا كنت قد وجدت محتوى يشتبه في حقوق النسخ، فالرجاء إرسال بريد إلكتروني إلى: notice#oldtoolbag.com (عند إرسال البريد الإلكتروني، يرجى استبدال '#' ب '@') لإبلاغنا، وتقديم الدليل على ذلك، إذا تم التحقق من ذلك، فإن هذا الموقع سيقوم بإزالة المحتوى المزعوم بشكل فوري.

أنت قد تحب