نظرة عامة على "رسومات 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 في 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 المستخدم لتعريف أحد الأزرار:

صورة للمنطقة القابلة للتوسيع
ومربع المساحة المتروكة

الشكل 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 الموضحة أعلاه. لاحظ كيف يختلف عرض الزر وارتفاعه مع النص، وتمتدّ صورة الخلفية لتلائم النص.

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

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