وقتی لازم است تصاویر ثابت را در برنامهتان نمایش دهید، میتوانید از کلاس Drawable و زیرکلاسهای آن برای رسم شکلها و تصاویر استفاده کنید. Drawable یک انتزاع کلی برای
چیزی است که میتواند کشیده شود. زیرکلاسهای مختلف به سناریوهای تصویر خاص کمک میکنند و میتوانید آنها را گسترش دهید تا اشیای طراحیشدنی خودتان را که به روشهای منحصربهفرد عمل میکنند تعریف کنید.
علاوهبر استفاده از سازندههای کلاس، دو روش برای تعریف و نمونهسازی Drawable وجود دارد:
- منبع تصویر (فایل بیتمپ) ذخیرهشده در پروژه را ازهم باز میکند.
- منبع زبان نشانهگذاری توسعهپذیر را که ویژگیهای طراحیکردنی را تعریف میکند ازهم باز کنید.
توجه: شاید ترجیح دهید بهجای آن از دارایی برداری استفاده کنید که تصویر را با مجموعهای از نقطهها، خطها، و منحنیها بههمراه اطلاعات رنگ مرتبط تعریف میکند. این کار به ترسیمیهای بُرداری امکان میدهد بدون افت کیفیت برای اندازههای مختلف مقیاسبندی شوند. برای اطلاعات بیشتر، نمای کلی عناصر برداری قابلکشیدن را ببینید.
ساختن عناصر طراحی از تصاویر منبع
میتوانید با ارجاع دادن به فایل تصویری از منابع پروژه، به برنامهتان گرافیک اضافه کنید. انواع فایلهای پشتیبانیشده عبارتاند از PNG (ترجیح داده میشود)، JPG (قابلقبول)، و GIF (توصیه نمیشود). نشانکهای برنامه، نشانوارهها، و دیگر گرافیکها، مثل مواردی که در بازیها استفاده میشوند، برای این تکنیک بسیار مناسب هستند.
برای استفاده از منبع تصویر، فایل خود را به res/drawable/
دایرکتوری پروژه خود اضافه کنید. پساز ورود به پروژه، میتوانید به منبع تصویر از کد یا چیدمان XML خود ارجاع دهید. در هرصورت، بااستفاده از
شناسه منبع، که نام فایل بدون پسوند نوع فایل است، به آن ارجاع داده میشود. برای مثال، به my_image.png بهعنوان my_image اشاره کن.
توجه: منابع تصویری قرارگرفته در
دایرکتوری res/drawable/ ممکن است درطول فرایند ساخت با
ابزار aapt بهطور خودکار با
فشردهسازی تصویر بدون افت بهینهسازی شوند. برای مثال، یک PNG رنگ واقعی که به بیش از ۲۵۶ رنگ نیاز ندارد
ممکن است به یک PNG ۸ بیتی با پالت رنگ تبدیل شود. این کار منجر به تصویری با کیفیت یکسان میشود اما به حافظه کمتری نیاز دارد. درنتیجه، فایلهای اجرایی تصویر
قرارگرفته در این دایرکتوری میتواند در زمان ساخت تغییر کند. اگر قصد دارید تصویری را بهعنوان یک جاریسازی بیت بخوانید تا آن را به یک بیتمپ تبدیل کنید، تصاویرتان را بهجای آن در پوشه
res/raw/ قرار دهید، جایی که ابزار aapt آنها را
اصلاح نمیکند.
تکه کد زیر نشان میدهد که چگونه یک ImageView بسازید که از
تصویری که از منبع طراحیکردنی ایجاد شده است استفاده میکند و آن را به چیدمان اضافه میکند:
کاتلین
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) }
جاوا
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 مدیریت کنید، همانطور که در مثال زیر نشان داده شده است:
کاتلین
val myImage: Drawable = ResourcesCompat.getDrawable(context.resources, R.drawable.my_image, null)
جاوا
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 خاصی استفاده میکند که به تعریف کردن ویژگیهای شیء کمک میکند. کد زیر
TransitionDrawable
را نمونهسازی میکند و آن را بهعنوان محتوای
ImageView شیء تنظیم میکند:
کاتلین
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.
جاوا
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 است. به همین دلیل، میتوانید هرجا Drawable موردانتظار است از
ShapeDrawable استفاده کنید. برای
مثال، میتوانید از شیء ShapeDrawable برای تنظیم پسزمینه
نما با ارسال آن به روش setBackgroundDrawable() نما استفاده کنید. همچنین میتوانید شکلتان را بهعنوان نمای سفارشی خودتان بکشید و آن را به چیدمانی در برنامهتان اضافه کنید.
چون ShapeDrawable روش draw() خود را دارد، میتوانید زیرکلاسی از View ایجاد کنید که شیء ShapeDrawable را درطول رویداد onDraw() رسم میکند، همانطور که در مثال کد زیر نشان داده شده است:
کاتلین
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) } }
جاوا
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 در نمونه کد
بالا همانگونه که از هر نمای سفارشی دیگری استفاده میکنید استفاده کنید. برای مثال، میتوانید آن را بهصورت برنامهریزیشده به فعالیتی در برنامهتان اضافه کنید، همانطور که در مثال زیر نشان داده شده است:
کاتلین
private lateinit var customDrawableView: CustomDrawableView override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) customDrawableView = CustomDrawableView(this) setContentView(customDrawableView) }
جاوا
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/ پروژه شما ذخیره شود.
از حاشیه برای تعریف کردن نواحی کشآمدنی و ثابت تصویر استفاده کنید. بخش کششپذیر را با کشیدن یک (یا چند) خط سیاه ۱ پیکسلی در بخش چپ و بالای حاشیه نشان میدهید (پیکسلهای دیگر حاشیه باید کاملاً شفاف یا سفید باشند). میتوانید هر تعداد بخش کششی که میخواهید داشته باشید. اندازه نسبی بخشهای کشآمدنی یکسان میماند، بنابراین بزرگترین بخش همیشه بزرگترین باقی میماند.
همچنین میتوانید بخش اختیاری قابلرسم تصویر (درواقع،
خطوط حاشیه) را با رسم خطی در سمت راست و خطی در پایین تعریف کنید. اگر شیء View گرافیک NinePatch را بهعنوان پسزمینه خود تنظیم کند و سپس نوشتار نمای را مشخص کند، خود را طوری میکشد که همه نوشتار فقط ناحیه تعیینشده توسط خطوط راست و پایین (درصورت وجود) را اشغال کند.
اگر خطوط حاشیه اضافه درنظر گرفته نشده باشد، Android از خطوط چپ و بالا برای تعریف این ناحیه طراحیشدنی استفاده میکند.
برای روشن کردن تفاوت بین خطوط، خطوط چپ و بالا مشخص میکنند کدام پیکسلهای تصویر مجاز به تکثیر شدن برای کشیدن تصویر هستند. خطوط پایین و راست ناحیه نسبی درون تصویر را تعریف میکنند که محتوای نما مجاز است در آن قرار بگیرد.
شکل ۱ نمونهای از گرافیک NinePatch را نشان میدهد که برای تعریف دکمه استفاده میشود:
شکل ۱: نمونهای از گرافیک NinePatch که دکمهای را تعریف میکند
این گرافیک NinePatch یک ناحیه کشیدنی را با خطوط چپ و بالا، و ناحیه رسمشدنی را با خطوط پایین و راست تعریف میکند. در تصویر بالا، خطوط نقطهچین خاکستری مناطقی از تصویر را که برای کشیدن تصویر تکثیر شدهاند مشخص میکنند. مستطیل صورتی در تصویر پایین منطقهای را که محتوای نما در آن مجاز است مشخص میکند. اگر محتوا در این ناحیه جا نشود، تصویر کشیده میشود تا محتوا در آن جا بگیرد.
ابزار کشیدن ۹-تکه روشی بسیار کاربردی برای ایجاد تصاویر 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 تنظیم شدهاند تا دکمه بهخوبی
در اطراف نوشتار قرار بگیرد.
شکل ۲ دو دکمه را نشان میدهد که از تصویر XML و NinePatch ارائهشده در بالا پرداز شدهاند. توجه کنید که عرض و ارتفاع دکمه با نوشتار تغییر میکند، و تصویر پسزمینه برای جا دادن آن کشیده میشود.
شکل ۲: دکمههایی که بااستفاده از منبع XML و گرافیک NinePatch پرداز شدهاند
داراییهای طراحی سفارشی
وقتی میخواهید طراحیهای سفارشی ایجاد کنید، میتوانید این کار را با گسترش کلاس Drawable (یا هریک از زیرکلاسهای آن) انجام دهید.
مهمترین روش برای پیادهسازی draw(Canvas) است
زیرا این روش شیء Canvas را که باید برای ارائه
دستورالعملهای طراحیتان استفاده کنید ارائه میدهد.
کد زیر زیرکلاس سادهای از Drawable را نشان میدهد
که دایرهای را رسم میکند:
کاتلین
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 }
جاوا
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 همانطور که در اینجا نشان داده شده است:
کاتلین
val myDrawing = MyDrawable() val image: ImageView = findViewById(R.id.imageView) image.setImageDrawable(myDrawing) image.contentDescription = resources.getString(R.string.my_image_desc)
جاوا
MyDrawable mydrawing = new MyDrawable(); ImageView image = findViewById(R.id.imageView); image.setImageDrawable(mydrawing); image.setContentDescription(getResources().getString(R.string.my_image_desc));
در Android نسخه ۷.۰ (سطح میانای برنامه کاربردی ۲۴) و بالاتر، میتوانید نمونههای طراحی قابلرسم سفارشیتان را با 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 (سطح میانای برنامهسازی کاربردی ۲۱) و بالاتر، میتوانید نقشه بیتی و نه وصله تعریفشده بهعنوان
نقابهای آلفا را رنگی کنید. میتوانید آنها را با منابع رنگ یا مشخصههای زمینهای که به منابع رنگ تبدیل میشوند (برای نمونه، ?android:attr/colorPrimary) رنگآمیزی کنید. معمولاً این داراییها را فقط یکبار ایجاد میکنید و آنها را بهطور خودکار رنگآمیزی میکنید تا با زمینهتان مطابقت پیدا کنند.
با روش setTint() میتوانید رنگی به BitmapDrawable، NinePatchDrawable، یا VectorDrawable
اشیا اضافه کنید. همچنین میتوانید رنگ و حالت سایهرنگ را در چیدمانهایتان با ویژگیهای android:tint و android:tintMode تنظیم کنید.
استخراج رنگهای برجسته از تصویر
«کتابخانه پشتیبانی Android» شامل کلاس Palette است که به شما امکان میدهد رنگهای برجسته را از تصویر استخراج کنید.
میتوانید عناصر طراحیشدنی را بهعنوان Bitmap بار کنید و آن را به Palette منتقل کنید تا به رنگهای آن دسترسی پیدا کنید.
برای کسب اطلاعات بیشتر، انتخاب رنگها با
میانای برنامهسازی کاربردی پالت را بخوانید.