مثال عملي لإنشاء مكون العرض الخاص بك

مثال عملي لإنشاء مكون العرض الخاص بك


أنا أحب Dribbble . هناك العديد من مشاريع التصميم الرائعة والملهمة. ولكن إذا كنت مطورًا ، فغالبًا ما يتم استبدال الشعور بالجمال سريعًا باليأس عندما تبدأ التفكير في كيفية تنفيذ هذا التصميم الرائع.


في هذه المقالة سأريك مثالاً على هذا التصميم وتنفيذه ، لكن قبل ذلك ، دعونا نتحدث عن حل المشكلة ككل.


أسهل طريقة هي استخدام نوع من المكتبات التي تغطي احتياجاتنا. الآن لا تفهموني خطأ ، أنا مؤيد كبير لنهج "عدم اختراع العجلة". هناك مكتبات كبيرة مفتوحة المصدر ، وعندما أحتاج إلى تحميل الصور أو تطبيق REST API ، سوف يساعدني Glide / Picasso و Retrofit كثيرًا.


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


لذلك ، أقول إنه غالبًا ما يكون من الأسهل والأفضل إنشاء مكون View الخاص بك. عندما أقول "مكون View الأصلي" ، أقصد امتداد فئة View ، onDraw() طريقة onDraw() واستخدام Paint Canvas لرسم مكون View . قد يبدو هذا مخيفًا إذا لم تكن قد فعلت ذلك من قبل ، لأن هذه الفئات بها العديد من الأساليب والخصائص ، لكن يمكنك التركيز على الخصائص الرئيسية:


  • canvas.drawRect() - تحديد إحداثيات الزوايا ورسم مستطيل ؛


  • canvas.drawRoundRect() - حدد نصف القطر اختياريًا ، وسيتم تقريب زوايا المستطيل ؛


  • canvas.drawPath() هي طريقة أكثر تعقيدًا ، لكنها أيضًا أكثر قوة لإنشاء الشكل الخاص بك باستخدام الخطوط والمنحنيات ؛


  • canvas.drawText() - لرسم النص على قماش (باستخدام Paint يمكنك التحكم في الحجم واللون والخصائص الأخرى) ؛


  • canvas.drawCircle() - حدد نقطة الوسط ونصف القطر واحصل على دائرة ؛


  • canvas.drawArc() - حدد المستطيل المحيط ، وكذلك canvas.drawArc() لرسم القوس ؛


  • paint.style - يشير إلى ما إذا كان الشكل المرسوم سوف يتم ملؤه ، دائري ، أو كلاهما ؛


  • paint.color - يشير إلى اللون (بما في ذلك الشفافية) ؛


  • paint.strokeWidth - يتحكم في عرض أشكال الحد ؛


  • paint.pathEffect - يسمح لك بالتأثير على هندسة الشكل المرسوم ؛


  • paint.shader - يسمح لك برسم التدرجات.



تذكر ، في بعض الأحيان قد تحتاج إلى استخدام واجهات برمجة التطبيقات الأخرى ، ولكن حتى بعد إتقان هذه الأساليب ، يمكنك رسم أشكال معقدة للغاية.


مثال عملي


هنا تصميم الفلفل يقدم لنا:


تصميم


هناك العديد من الأشياء المثيرة للاهتمام هنا ، ولكن دعونا نأخذ كل شيء إلى قطع صغيرة.


الخطوة 1. حساب المواقف علامة


 private fun calcPositions(markers: List<Marker>) { val max = markers.maxBy { it.value } val min = markers.minBy { it.value } pxPerUnit = chartHeight / (max - min) zeroY = max * pxPerUnit + paddingTop val step = (width - 2 * padding - scalesWidth) / (markers.size - 1) for ((i, marker) in markers.withIndex()) { val x = step * i + paddingLeft val y = zeroY - entry.value * pxPerUnit marker.currentPos.x = x marker.currentPos.y = y } } 

نجد الحد الأدنى والحد الأقصى للقيم ، ونحسب نسبة البكسل لكل وحدة ، وحجم الخطوة الأفقية بين العلامات والمواقع X و Y.


الخطوة 2. رسم التدرج


التدرج


 // prepare the gradient paint val colors = intArrayOf(colorStart, colorEnd)) val gradient = LinearGradient( 0f, paddingTop, 0f, zeroY, colors, null, CLAMP ) gradientPaint.style = FILL gradientPaint.shader = gradient private fun drawGradient(canvas: Canvas) { path.reset() path.moveTo(paddingLeft, zeroY) for (marker in markers) { path.lineTo(marker.targetPos.x, entry.targetPos.y) } // close the path path.lineTo(markers.last().targetPos.x, zeroY) path.lineTo(paddingLeft, zeroY) canvas.drawPath(path, gradientPaint) } 

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


الخطوة 3. ارسم شبكة


مش


 // prepare the guideline paint dottedPaint.style = STROKE dottedPaint.strokeWidth = DOTTED_STROKE_WIDTH_DP dottedPaint.pathEffect = DashPathEffect(floatArrayOf(INTERVAL, INTERVAL), 0f) private fun drawGuidelines(canvas: Canvas) { val first = findFirstDayOfWeekInMonth(markers) for (i in first..markers.lastIndex step 7) { val marker = markers[i] guidelinePath.reset() guidelinePath.moveTo(entry.currentPos.x, paddingTop) guidelinePath.lineTo(entry.currentPos.x, zeroY) canvas.drawPath(guidelinePath, dottedPaint) } } 

نحن تخصيص الطلاء بحيث شرطات. ثم نستخدم دورة لغة Kotlin الخاصة ، والتي تسمح لنا بالتكرار على العلامات في الخطوات من 7 (عدد الأيام في الأسبوع). لكل علامة ، نأخذ إحداثي X zeroY خطًا منقط رأسياً من أعلى الرسم البياني إلى zeroY .


الخطوة 4. رسم الرسم البياني وعلامات


الرسم البياني وعلامات


 private fun drawLineAndMarkers(canvas: Canvas) { var previousMarker: Marker? = null for (marker in markers) { if (previousMarker != null) { // draw the line val p1 = previousMarker.currentPos val p2 = marker.currentPos canvas.drawLine(p1.x, p1.y, p2.x, p2.y, strokePaint) } previousMarker = marker // draw the marker canvas.drawCircle( marker.currentPos.x, marker.currentPos.y, pointRadius, pointPaint ) } } 

نذهب إلى العلامات ونرسم دائرة مليئة بكل منها وخط بسيط من العلامة السابقة إلى الحالية.


الخطوة 5. رسم أزرار الأسابيع


أزرار أسابيع


 private fun drawWeeks(canvas: Canvas) { for ((i, week) in weeks.withIndex()) { textPaint.getTextBounds(week, 0, week.length, rect) val x = middle(i) val y = zeroY + rect.height() val halfWidth = rect.width() / 2f val halfHeight = rect.height() / 2f val left = x - halfWidth - padding val top = y - halfHeight - padding val right = x + halfWidth + padding val bottom = y + halfHeight + padding rect.set(left, top, right, bottom) paint.color = bgColor paint.style = FILL canvas.drawRoundRect(rect, radius, radius, paint) paint.color = strokeColor paint.style = STROKE canvas.drawRoundRect(rect, radius, radius, paint) canvas.drawText(week, x, keyY, textPaint) } } 

نذهب فوق علامات الأسبوع ونجد إحداثي X في منتصف الأسبوع ونبدأ في رسم الزر في طبقات: أولاً نرسم خلفية بزوايا مستديرة ، ثم نضع حدًا وأخيراً نصًا. نقوم بضبط الطلاء قبل رسم كل طبقة.


الخطوة 6. ارسم العلامات الرقمية على اليمين


علامات رقمية


 private fun drawGraduations(canvas: Canvas) { val x = markers.last().currentPos.x + padding for (value in graduations) { val y = zeroY - scale * pxPerUnit val formatted = NumberFormat.getIntegerInstance().format(value) canvas.drawText(formatted, x, y, textPaint) } } 

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


هذا كل شيء ، الآن سيبدو onDraw() كما يلي:


 override fun onDraw(canvas: Canvas) { drawGradient(canvas) drawGuidelines(canvas) drawLineAndMarkers(canvas) drawWeeks(canvas) drawGraduations(canvas) } 

والجمع بين الطبقات يعطينا النتيجة المرجوة:


النتيجة


ملخص


  1. لا تخف من إنشاء مكونات View خاصة بك (إذا لزم الأمر).
  2. تعلم واجهات برمجة التطبيقات Canvas and Paint الأساسية.
  3. قسم التصميم الخاص بك إلى طبقات صغيرة وارسم كل منها بشكل مستقل.

بالنسبة للنقطة الأخيرة ، بالنسبة لي ، يعد هذا أحد أفضل دروس البرمجة بشكل عام: عند مواجهة مهمة كبيرة ومعقدة ، قسّمها إلى مهام أصغر وأكثر بساطة.

Source: https://habr.com/ru/post/ar433782/


All Articles