عندما تحتاج إلى عرض صور ثابتة في تطبيقك، يمكنك استخدام الفئة Drawable
وفئاتها الفرعية لرسم أشكال
وصور. تمثّل السمة Drawable
تجريدًا عامًا لشيء يمكن رسمه. تساعد الفئات الفرعية المختلفة في سيناريوهات
صور معينة، ويمكنك توسيعها لتحديد الكائنات القابلة للرسم الخاصة بك
التي تعمل بطرق فريدة.
هناك طريقتان لتحديد Drawable
وإنشاء مثيل له إلى جانب استخدام الدالة الإنشائية للفئة:
- تضخيم مورد صورة (ملف صورة نقطية) محفوظ في مشروعك.
- تضخيم مورد XML يحدد الخصائص القابلة للرسم.
ملاحظة: قد تفضّل بدلاً من ذلك استخدام متّجه قابل للرسم، يحدّد الصورة باستخدام مجموعة من النقاط والخطوط والمنحنيات، إلى جانب معلومات اللون المرتبطة بها. يسمح هذا بقياس الرسومات المتجهة لأحجام مختلفة دون فقدان الجودة. لمزيد من المعلومات، يُرجى الاطّلاع على نظرة عامة على الرسومات المتّجهة القابلة للرسم.
إنشاء عناصر قابلة للرسم من صور الموارد
يمكنك إضافة رسومات إلى تطبيقك من خلال الرجوع إلى ملف صورة من موارد مشروعك. أنواع الملفات المعتمدة هي PNG (مفضّل) وJPG (مقبولة) وGIF (غير يُنصح بها). تُعد رموز التطبيقات والشعارات والرسومات الأخرى، مثل تلك المستخدمة في الألعاب، مناسبة تمامًا لهذه التقنية.
لاستخدام مورد صورة، أضِف ملفك إلى دليل res/drawable/
بمشروعك. بمجرد دخول مشروعك، يمكنك الرجوع إلى مورد الصورة
من التعليمة البرمجية أو تخطيط XML الخاص بك. وفي كلتا الحالتين، يُشار إليه باستخدام رقم تعريف المورد،
وهو اسم الملف بدون امتداد نوع الملف. على سبيل المثال، يمكنك الإشارة إلى my_image.png
باسم my_image
.
ملاحظة: يمكن تلقائيًا تحسين موارد الصور الموضوعة في دليل
res/drawable/
باستخدام ميزة "ضغط الصور بدون فقدان البيانات" باستخدام أداة aapt
أثناء عملية التصميم. على سبيل المثال، يمكن تحويل ملف PNG بألوان حقيقية لا يتطلب أكثر من 256 لونًا إلى تنسيق PNG 8 بت باستخدام لوحة ألوان. ينتج عن ذلك صورة ذات جودة متساوية
ولكنها تتطلب ذاكرة أقل. ونتيجة لذلك، يمكن أن تتغير الصور الثنائية
الموجودة في هذا الدليل في وقت الإنشاء. إذا كنت تنوي قراءة صورة على أنها صورة نقطية لتحويلها إلى صورة نقطية، عليك وضع صورك في المجلد res/raw/
بدلاً من ذلك، حيث لا تعدّلها أداة aapt
.
يوضّح مقتطف الرمز التالي كيفية إنشاء ImageView
يستخدم صورة يتم إنشاؤها من مورد قابل للرسم ويضيفها إلى التنسيق:
Kotlin
private lateinit var constraintLayout: ConstraintLayout override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // Instantiate an ImageView and define its properties val i = ImageView(this).apply { setImageResource(R.drawable.my_image) contentDescription = resources.getString(R.string.my_image_desc) // set the ImageView bounds to match the Drawable's dimensions adjustViewBounds = true layoutParams = ViewGroup.LayoutParams( ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT) } // Create a ConstraintLayout in which to add the ImageView constraintLayout = ConstraintLayout(this).apply { // Add the ImageView to the layout. addView(i) } // Set the layout as the content view. setContentView(constraintLayout) }
Java
ConstraintLayout constraintLayout; protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Create a ConstraintLayout in which to add the ImageView constraintLayout = new ConstraintLayout(this); // Instantiate an ImageView and define its properties ImageView i = new ImageView(this); i.setImageResource(R.drawable.my_image); i.setContentDescription(getResources().getString(R.string.my_image_desc)); // set the ImageView bounds to match the Drawable's dimensions i.setAdjustViewBounds(true); i.setLayoutParams(new ViewGroup.LayoutParams( ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)); // Add the ImageView to the layout and set the layout as the content view. constraintLayout.addView(i); setContentView(constraintLayout); }
وفي حالات أخرى، يمكنك التعامل مع مورد الصورة ككائن Drawable
، على النحو الموضّح في المثال التالي:
Kotlin
val myImage: Drawable = ResourcesCompat.getDrawable(context.resources, R.drawable.my_image, null)
Java
Resources res = context.getResources(); Drawable myImage = ResourcesCompat.getDrawable(res, R.drawable.my_image, null);
تحذير: يمكن لكل مورد فريد في مشروعك الحفاظ على حالة واحدة فقط، بغض النظر عن عدد الكائنات المختلفة التي تشير إليها. على سبيل المثال، إذا أنشأت مثيلاً لكائنَين Drawable
من مورد الصورة نفسه
وغيّرت سمة (مثل ألفا) لعنصر واحد، سيؤثّر ذلك أيضًا
في الآخر. عند التعامل مع مثيلات متعددة لمورد صورة، يجب تنفيذ صورة متحركة بدلاً من تحويل الكائن Drawable
مباشرةً.
يوضِّح مقتطف XML أدناه كيفية إضافة مورد قابل للرسم إلى ImageView
بتنسيق XML:
<ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/my_image" android:contentDescription="@string/my_image_desc" />
لمزيد من المعلومات حول استخدام موارد المشاريع، يُرجى الاطّلاع على الموارد والأصول.
ملاحظة: عند استخدام موارد الصور كمصدر للصور القابلة للرسم، تأكّد من أن الصور بالحجم المناسب لكثافات وحدات البكسل المختلفة. إذا لم تكن الصور صحيحة، فسيتم تكبيرها لتلائم، مما قد يتسبب في إضافة أدوات في الرسومات القابلة للرسم. لمزيد من المعلومات، يُرجى الاطّلاع على المقالة دعم كثافات وحدات البكسل المختلفة.
إنشاء عناصر قابلة للرسم من موارد XML
إذا أردت إنشاء عنصر Drawable
ولا يعتمد في البداية على متغيّرات يتم تحديدها من خلال الرمز أو تفاعل المستخدم، ننصحك بتعريف Drawable
في XML. حتى
إذا كنت تتوقع أن يغيّر Drawable
سماته أثناء تفاعل المستخدم
مع تطبيقك، يجب تحديد الكائن في XML، إذ يمكنك تعديل السمات بعد
إنشاء مثيل للكائن.
بعد تحديد Drawable
بتنسيق XML، احفظ الملف في دليل res/drawable/
الخاص بمشروعك. يوضّح المثال التالي ملف XML الذي يحدّد مورد TransitionDrawable
الذي يتم اكتسابه من Drawable
:
<!-- res/drawable/expand_collapse.xml --> <transition xmlns:android="http://schemas.android.com/apk/res/android"> <item android:drawable="@drawable/image_expand"/> <item android:drawable="@drawable/image_collapse"/> </transition>
بعد ذلك، يمكنك استرداد الكائن وإنشاء مثيل له من خلال طلب
Resources#getDrawable()
وتمرير رقم تعريف المورد لملف XML. يمكن تحديد أي فئة فرعية من النوع Drawable
تتيح الطريقة inflate()
في XML وإنشاء مثيل لها من خلال تطبيقك.
تستخدم كل فئة قابلة للرسم تتيح تضخيم XML سمات XML معيّنة تساعد في تحديد خصائص الكائنات. ينشئ الرمز التالي مثيلاً TransitionDrawable
ويضبطه كمحتوى لكائن ImageView
:
Kotlin
val transition= ResourcesCompat.getDrawable( context.resources, R.drawable.expand_collapse, null ) as TransitionDrawable val image: ImageView = findViewById(R.id.toggle_image) image.setImageDrawable(transition) // Description of the initial state that the drawable represents. image.contentDescription = resources.getString(R.string.collapsed) // Then you can call the TransitionDrawable object's methods. transition.startTransition(1000) // After the transition is complete, change the image's content description // to reflect the new state.
Java
Resources res = context.getResources(); TransitionDrawable transition = (TransitionDrawable) ResourcesCompat.getDrawable(res, R.drawable.expand_collapse, null); ImageView image = (ImageView) findViewById(R.id.toggle_image); image.setImageDrawable(transition); // Description of the initial state that the drawable represents. image.setContentDescription(getResources().getString(R.string.collapsed)); // Then you can call the TransitionDrawable object's methods. transition.startTransition(1000); // After the transition is complete, change the image's content description // to reflect the new state.
لمزيد من المعلومات حول سمات XML المتوافقة، يمكنك الرجوع إلى الفئات المذكورة أعلاه.
أشكال قابلة للرسم
قد يكون كائن ShapeDrawable
خيارًا جيدًا
عندما تريد رسم رسم ثنائي الأبعاد ديناميكيًا. يمكنك رسم أشكال أولية آليًا على كائن ShapeDrawable
وتطبيق الأنماط التي يحتاجها تطبيقك.
ShapeDrawable
هي فئة فرعية من Drawable
. لهذا السبب، يمكنك استخدام
ShapeDrawable
في أي مكان يُتوقَّع فيه وجود Drawable
. على سبيل المثال، يمكنك استخدام كائن ShapeDrawable
لضبط خلفية العرض من خلال تمريره إلى طريقة العرض setBackgroundDrawable()
. يمكنك أيضًا رسم الشكل كعرض مخصص
خاص به وإضافته إلى تخطيط في تطبيقك.
بما أنّ السمة ShapeDrawable
تتضمّن طريقة draw()
الخاصة بها، يمكنك إنشاء فئة فرعية من View
ترسم الكائن ShapeDrawable
أثناء حدث onDraw()
، كما هو موضّح في مثال الرمز التالي:
Kotlin
class CustomDrawableView(context: Context) : View(context) { private val drawable: ShapeDrawable = run { val x = 10 val y = 10 val width = 300 val height = 50 contentDescription = context.resources.getString(R.string.my_view_desc) ShapeDrawable(OvalShape()).apply { // If the color isn't set, the shape uses black as the default. paint.color = 0xff74AC23.toInt() // If the bounds aren't set, the shape can't be drawn. setBounds(x, y, x + width, y + height) } } override fun onDraw(canvas: Canvas) { drawable.draw(canvas) } }
Java
public class CustomDrawableView extends View { private ShapeDrawable drawable; public CustomDrawableView(Context context) { super(context); int x = 10; int y = 10; int width = 300; int height = 50; setContentDescription(context.getResources().getString( R.string.my_view_desc)); drawable = new ShapeDrawable(new OvalShape()); // If the color isn't set, the shape uses black as the default. drawable.getPaint().setColor(0xff74AC23); // If the bounds aren't set, the shape can't be drawn. drawable.setBounds(x, y, x + width, y + height); } protected void onDraw(Canvas canvas) { drawable.draw(canvas); } }
يمكنك استخدام الفئة CustomDrawableView
في نموذج الرمز البرمجي أعلاه كما تستخدم أي طريقة عرض مخصّصة أخرى. على سبيل المثال، يمكنك إضافته آليًا إلى نشاط في تطبيقك، كما هو موضّح في المثال التالي:
Kotlin
private lateinit var customDrawableView: CustomDrawableView override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) customDrawableView = CustomDrawableView(this) setContentView(customDrawableView) }
Java
CustomDrawableView customDrawableView; protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); customDrawableView = new CustomDrawableView(this); setContentView(customDrawableView); }
وإذا أردت استخدام طريقة العرض المخصّصة في تنسيق XML بدلاً من ذلك، يجب أن تلغي
الفئة CustomDrawableView
الدالة الإنشائية View(Context, AttributeSet)
التي يتم استدعاؤها عند تضخيم الفئة
من XML. يوضّح المثال التالي كيفية الإشارة إلى
CustomDrawableView
في تنسيق XML:
<com.example.shapedrawable.CustomDrawableView android:layout_width="fill_parent" android:layout_height="wrap_content" />
تتيح لك الفئة ShapeDrawable
، مثل العديد من الأنواع الأخرى القابلة للرسم في حزمة android.graphics.drawable
، تحديد خصائص مختلفة للكائن باستخدام الطرق العامة. تتضمن بعض أمثلة الخصائص التي قد ترغب في تعديلها شفافية ألفا وفلتر الألوان
والتخفيف والتعتيم واللون.
يمكنك أيضًا تحديد أشكال أولية قابلة للرسم باستخدام موارد XML. لمزيد من المعلومات، راجع شكل قابل للرسم في أنواع الموارد القابلة للرسم.
عناصر NinePatch القابلة للرسم
صورة NinePatchDrawable
هي صورة نقطية قابلة للتمديد يمكنك استخدامها كخلفية لطريقة العرض. يقوم Android
بتغيير حجم الرسم تلقائيًا ليلائم محتويات العرض. ومن الأمثلة على استخدام صورة NinePatch الخلفية التي تستخدمها أزرار Android القياسية، ويجب أن تمتد الأزرار لتلائم سلاسل ذات أطوال مختلفة. رسم NinePatch هو صورة عادية بتنسيق PNG وتتضمّن حدًا إضافيًا يبلغ بكسل واحد.
ويجب حفظها بالإضافة 9.png
في دليل res/drawable/
الخاص بمشروعك.
ويمكنك استخدام الحدود لتحديد المناطق القابلة للتمديد والثابتة في الصورة. تشير إلى قسم قابل للتمديد من خلال رسم خط (خطوط) أسود عريض بعرض 1 بكسل في الجزء الأيسر والعلوي من الحد (يجب أن تكون وحدات بكسل الحدود الأخرى شفافة بالكامل أو بيضاء). يمكن أن يكون لديك العديد من الأقسام القابلة للتمديد. يظل الحجم النسبي للأقسام القابلة للتمديد كما هو، لذلك يظل الجزء الأكبر دائمًا هو الأكبر.
يمكنك أيضًا تحديد قسم اختياري قابل للرسم من الصورة (في الواقع، خطوط المساحة المتروكة) من خلال رسم خط على اليمين وخط في الأسفل. إذا ضبط كائن View
رسم NinePatch كخلفية له ثم حدّد نص طريقة العرض، سيمتد نفسه بحيث لا يشغل النص سوى المساحة المحددة في السطرين الأيمن والسفلي (إذا كانا مضمَّنين).
إذا لم يتم تضمين سطور المساحة المتروكة، يستخدم Android السطرين الأيسر والعلوي لتحديد هذه المنطقة القابلة للرسم.
لتوضيح الفرق بين الخطوط، يحدد الخطين الأيسر والعلوي أي وحدات بكسل من الصورة يُسمح بنسخها من أجل توسيع الصورة. يحدد الخطين السفليين واليمنىين المنطقة النسبية داخل الصورة التي يُسمح لمحتويات العرض بشغلها.
يوضح الشكل 1 مثالاً لرسم NinePatch المستخدم لتعريف أحد الأزرار:
![صورة للمنطقة القابلة للتوسيع
ومربع المساحة المتروكة](https://developer.android.com/static/images/ninepatch_raw.png?authuser=5&hl=ar)
الشكل 1: مثال لرسم NinePatch يحدّد زرّ
يحدد رسم NinePatch هذا منطقة واحدة قابلة للتوسيع مع الخطوط اليسرى والعلوية، والمنطقة القابلة للرسم بالخطوط السفلية واليمنى. في الصورة العلوية، تحدد الخطوط الرمادية المنقطة مناطق الصورة التي تم تكرارها من أجل تمديد الصورة. يحدد المستطيل الوردي في الصورة السفلية المنطقة التي يُسمح فيها بمحتوى العرض. إذا كان المحتوى لا يتناسب مع هذه المنطقة، فسيتم تمديد الصورة لجعلها مناسبة.
توفر أداة رسم 9-تصحيحات طريقة سهلة للغاية لإنشاء صور NinePatch باستخدام محرر رسومات WYSIWYG. كما أنها تثير تحذيرات إذا كانت المنطقة التي حددتها للمساحة القابلة للتوسيع معرّضة لخطر إنتاج عناصر رسم نتيجة لنسخ البكسل.
يوضح نموذج XML التالي للتخطيط كيفية إضافة رسم NinePatch إلى بضعة أزرار. تم حفظ صورة NinePatch في
res/drawable/my_button_background.9.png
.
<Button android:id="@+id/tiny" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentTop="true" android:layout_centerInParent="true" android:text="Tiny" android:textSize="8sp" android:background="@drawable/my_button_background"/> <Button android:id="@+id/big" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_centerInParent="true" android:text="Biiiiiiig text!" android:textSize="30sp" android:background="@drawable/my_button_background"/>
يُرجى العلم أنّه تم ضبط السمتَين layout_width
وlayout_height
على wrap_content
لجعل الزر ملائمًا
مع النص بدقة.
يُظهر الشكل 2 الزرين المعروضين من صورة XML وNinePatch الموضحة أعلاه. لاحظ كيف يختلف عرض الزر وارتفاعه مع النص، وتمتدّ صورة الخلفية لتلائم النص.
![صورة للأزرار الصغيرة
والحجم العادي](https://developer.android.com/static/images/ninepatch_examples.png?authuser=5&hl=ar)
الشكل 2: الأزرار المعروضة باستخدام مورد XML ورسم NinePatch
رسومات مخصّصة
عندما تريد إنشاء بعض الرسومات المخصّصة، يمكنك إجراء ذلك من خلال توسيع فئة Drawable
(أو أي من فئاتها الفرعية).
أهمّ طريقة لتنفيذها هي draw(Canvas)
لأنّ هذه الطريقة توفّر الكائن Canvas
الذي يجب استخدامه لتقديم تعليمات الرسم.
يُظهر الرمز البرمجي التالي فئة فرعية بسيطة من
Drawable
ترسم دائرة:
Kotlin
class MyDrawable : Drawable() { private val redPaint: Paint = Paint().apply { setARGB(255, 255, 0, 0) } override fun draw(canvas: Canvas) { // Get the drawable's bounds val width: Int = bounds.width() val height: Int = bounds.height() val radius: Float = Math.min(width, height).toFloat() / 2f // Draw a red circle in the center canvas.drawCircle((width / 2).toFloat(), (height / 2).toFloat(), radius, redPaint) } override fun setAlpha(alpha: Int) { // This method is required } override fun setColorFilter(colorFilter: ColorFilter?) { // This method is required } override fun getOpacity(): Int = // Must be PixelFormat.UNKNOWN, TRANSLUCENT, TRANSPARENT, or OPAQUE PixelFormat.OPAQUE }
Java
public class MyDrawable extends Drawable { private final Paint redPaint; public MyDrawable() { // Set up color and text size redPaint = new Paint(); redPaint.setARGB(255, 255, 0, 0); } @Override public void draw(Canvas canvas) { // Get the drawable's bounds int width = getBounds().width(); int height = getBounds().height(); float radius = Math.min(width, height) / 2; // Draw a red circle in the center canvas.drawCircle(width/2, height/2, radius, redPaint); } @Override public void setAlpha(int alpha) { // This method is required } @Override public void setColorFilter(ColorFilter colorFilter) { // This method is required } @Override public int getOpacity() { // Must be PixelFormat.UNKNOWN, TRANSLUCENT, TRANSPARENT, or OPAQUE return PixelFormat.OPAQUE; } }
بعد ذلك، يمكنك إضافة الرسم القابل للرسم أينما أردت، مثلاً إلى
ImageView
كما هو موضّح هنا:
Kotlin
val myDrawing = MyDrawable() val image: ImageView = findViewById(R.id.imageView) image.setImageDrawable(myDrawing) image.contentDescription = resources.getString(R.string.my_image_desc)
Java
MyDrawable mydrawing = new MyDrawable(); ImageView image = findViewById(R.id.imageView); image.setImageDrawable(mydrawing); image.setContentDescription(getResources().getString(R.string.my_image_desc));
على نظام التشغيل Android 7.0 (المستوى 24 لواجهة برمجة التطبيقات) والإصدارات الأحدث، يمكنك أيضًا تحديد مثيلات مخصّصة قابلة للرسم باستخدام XML بالطرق التالية:
- استخدام اسم الفئة المؤهّل بالكامل كاسم عنصر XML لتنفيذ هذا النهج، يجب أن تكون الفئة المخصّصة القابلة للرسم فئة عامة عالية المستوى:
<com.myapp.MyDrawable xmlns:android="http://schemas.android.com/apk/res/android" android:color="#ffff0000" />
- استخدام
drawable
كاسم علامة XML وتحديد اسم الفئة المؤهّل بالكامل من سمة الفئة يمكن استخدام هذا النهج لكل من الصفوف ذات المستوى الأعلى العامة والصفوف الداخلية الثابتة العامة:<drawable xmlns:android="http://schemas.android.com/apk/res/android" class="com.myapp.MyTopLevelClass$MyDrawable" android:color="#ffff0000" />
إضافة درجات لونية إلى العناصر القابلة للرسم
باستخدام نظام التشغيل Android 5.0 (المستوى 21 من واجهة برمجة التطبيقات) والإصدارات الأحدث، يمكنك تلوين الصور النقطية وتسع رموز تصحيح
كأقنعة ألفا. يمكنك تلوين مواد العرض هذه باستخدام موارد لونية أو سمات مظاهر يتم تحويلها إلى موارد
ألوان (على سبيل المثال، ?android:attr/colorPrimary
). يمكنك عادةً إنشاء مواد العرض هذه
مرة واحدة فقط وتلوينها تلقائيًا لتتناسب مع مظهرك.
يمكنك تطبيق درجة اللون على العناصر BitmapDrawable
أو NinePatchDrawable
أو VectorDrawable
باستخدام طريقة setTint()
. يمكنك أيضًا ضبط لون درجة اللون ووضعه في التنسيقات باستخدام السمتَين android:tint
وandroid:tintMode
.
استخراج الألوان البارزة من صورة
تتضمن مكتبة دعم Android الفئة Palette
التي تتيح لك استخراج ألوان بارزة من صورة.
يمكنك تحميل رسوماتك القابلة للرسم بتنسيق Bitmap
وتمريرها إلى Palette
للوصول إلى ألوانها.
لمزيد من المعلومات، اقرأ تحديد الألوان باستخدام
Palette API.