نظرة عامة على "رسومات Google"

تجربة طريقة "الكتابة"
‫Jetpack Compose هي مجموعة أدوات واجهة المستخدم المقترَحة لنظام التشغيل Android. تعرَّف على كيفية عرض الرسومات في Compose.

عندما تحتاج إلى عرض صور ثابتة في تطبيقك، يمكنك استخدام فئة 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 أو تفاعل المستخدم، فإنّ تحديد 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 drawables

NinePatchDrawable الرسم هو صورة نقطية قابلة للتمديد يمكنك استخدامها كخلفية لعرض. يعمل نظام التشغيل Android على تغيير حجم الرسم تلقائيًا لاستيعاب محتوى العرض. من الأمثلة على استخدام صورة NinePatch الخلفية التي تستخدمها أزرار Android العادية، إذ يجب أن تتمدد الأزرار لاستيعاب سلاسل بأطوال مختلفة. رسومات NinePatch هي صور PNG عادية تتضمّن حدًا إضافيًا بعرض بكسل واحد. يجب حفظه باستخدام الإضافة 9.png في الدليل res/drawable/ الخاص بمشروعك.

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

يمكنك أيضًا تحديد قسم اختياري قابل للرسم من الصورة (بشكل فعّال، أسطر المساحة المتروكة) من خلال رسم خط على اليسار وخط في الأسفل. إذا كان عنصر View يضبط صورة NinePatch كخلفية له ثم يحدّد نص العرض، سيتم تمديده بحيث يشغل كل النص المساحة المحدّدة فقط بواسطة الخطوط اليمنى والسفلية (إذا كانت مضمّنة). إذا لم يتم تضمين خطوط المساحة المتروكة، يستخدم نظام التشغيل Android الخطوط اليمنى والعلوية لتحديد مساحة الرسم هذه.

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

يعرض الشكل 1 مثالاً على رسم NinePatch مستخدَم لتحديد زر:

صورة للمساحة القابلة للتمديد ومربّع المساحة المتروكة

الشكل 1: مثال على رسم NinePatch يحدد زرًا

يحدّد رسم NinePatch البياني مساحة واحدة قابلة للتمديد باستخدام الخطوط اليسرى والعلوية، ومساحة قابلة للرسم باستخدام الخطوط السفلية واليمنى. في الصورة العلوية، تحدّد الخطوط الرمادية المنقّطة المناطق التي تم تكرارها في الصورة لتوسيعها. يحدّد المستطيل الوردي في الصورة السفلية المنطقة التي يُسمح فيها بعرض محتوى النافذة. إذا لم يكن المحتوى مناسبًا لهذه المنطقة، يتم تمديد الصورة لتلائمها.

توفّر أداة Draw 9-patch طريقة مفيدة للغاية لإنشاء صور 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 الموضّحة أعلاه. لاحظ كيف يختلف عرض الزر وارتفاعه باختلاف النص، وكيف تمتد صورة الخلفية لتلائم النص.

صورة لأزرار صغيرة وأخرى عادية الحجم

الشكل 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" />

إضافة لون إلى العناصر القابلة للرسم

في الإصدار 5.0 من نظام التشغيل Android (مستوى واجهة برمجة التطبيقات 21) والإصدارات الأحدث، يمكنك تلوين الصور النقطية وصور Nine-patch المحدّدة كأقنعة ألفا. يمكنك تلوينها باستخدام موارد الألوان أو سمات المظهر التي يتم تحويلها إلى موارد ألوان (مثل ?android:attr/colorPrimary). وعادةً، يتم إنشاء مواد العرض هذه مرة واحدة فقط وتلوينها تلقائيًا لتتطابق مع المظهر.

يمكنك تطبيق لون على الكائنات BitmapDrawable أو NinePatchDrawable أو VectorDrawable باستخدام الطريقة setTint(). يمكنك أيضًا ضبط لون ووضع التظليل في تنسيقاتك باستخدام السمتَين android:tint وandroid:tintMode.

استخراج الألوان البارزة من صورة

تتضمّن "مكتبة دعم Android" الفئة Palette التي تتيح لك استخراج الألوان البارزة من صورة. يمكنك تحميل عناصر قابلة للرسم كـ Bitmap وتمريرها إلى Palette للوصول إلى ألوانها. لمزيد من المعلومات، يُرجى الاطّلاع على اختيار الألوان باستخدام Palette API.