Compose-এ গ্রাফিক্স

স্ক্রিনে ঠিক কী আঁকা হয়েছে তা অনেক অ্যাপকে সঠিকভাবে নিয়ন্ত্রণ করতে পারতে হবে। এটি স্ক্রিনের ঠিক জায়গায় একটি বক্স বা বৃত্ত রাখার মতো ছোট হতে পারে অথবা অনেকগুলি বিভিন্ন স্টাইলে গ্রাফিক এলিমেন্টের একটি বিস্তারিত ব্যবস্থা হতে পারে।

মডিফায়ার ও DrawScope সহ বেসিক ড্রয়িং

Compose-এ কাস্টম কিছু আঁকার মূল উপায় হল মডিফায়ার ব্যবহার করা, যেমন Modifier.drawWithContent, Modifier.drawBehind এবং Modifier.drawWithCache।

যেমন, আপনার কম্পোজেবলের পিছনে কিছু আঁকতে, আপনি ড্রয়িং কমান্ড কার্যকর করা শুরু করতে drawBehind মডিফায়ার ব্যবহার করতে পারেন:

Spacer(
    modifier = Modifier
        .fillMaxSize()
        .drawBehind {
            // this = DrawScope
        }
)

আপনার যদি শুধু ড্র করে এমন কম্পোজেবল প্রয়োজন হয়, তাহলে আপনি Canvas কম্পোজেবল ব্যবহার করতে পারেন। Canvas কম্পোজেবল হল Modifier.drawBehind-এর চারপাশে একটি সুবিধাজনক র‍্যাপার। অন্য যেকোনও Compose UI এলিমেন্টের মতোই আপনি নিজের লেআউটে Canvas যোগ করেন। Canvas-এর মধ্যে, আপনি স্টাইল ও লোকেশনের উপর সুনির্দিষ্ট কন্ট্রোল সহ এলিমেন্ট আঁকতে পারবেন।

সব ড্রয়িং মডিফায়ার একটি DrawScope, একটি স্কোপড ড্রয়িং এনভায়রনমেন্ট এক্সপোজ করে যা নিজের স্ট্যাটাস বজায় রাখে। এটি আপনাকে গ্রাফিক্যাল এলিমেন্টের গ্রুপের জন্য প্যারামিটার সেট করতে দেয়। DrawScope-এ বেশ কিছু প্রয়োজনীয় ফিল্ড থাকে, যেমন size, Size অবজেক্ট যা DrawScope-এর বর্তমান ডাইমেনশন নির্দিষ্ট করে।

কিছু আঁকার জন্য, আপনি DrawScope-এ অনেক ড্র ফাংশনের মধ্যে একটি ব্যবহার করতে পারেন। যেমন, নিম্নলিখিত কোডটি স্ক্রিনের উপরের বাঁদিকের কোণে একটি আয়তক্ষেত্র আঁকে:

Canvas(modifier = Modifier.fillMaxSize()) {
    val canvasQuadrantSize = size / 2F
    drawRect(
        color = Color.Magenta,
        size = canvasQuadrantSize
    )
}

সাদা ব্যাকগ্রাউন্ডে আঁকা গোলাপি রঙের আয়তক্ষেত্র যা স্ক্রিনের এক চতুর্থাংশ জায়গা জুড়ে রয়েছে
ছবি ১. Compose-এ Canvas ব্যবহার করে আঁকা আয়তক্ষেত্র।

বিভিন্ন ড্রয়িং মডিফায়ার সম্পর্কে আরও জানতে, গ্রাফিক্স মডিফায়ার ডকুমেন্টেশন দেখুন।

স্থানাঙ্ক সিস্টেম

স্ক্রিনে কিছু আঁকতে হলে, আপনাকে অফসেট (x এবং y) এবং আপনার আইটেমের সাইজ জানতে হবে। DrawScope-এ ড্র করার অনেক পদ্ধতির সাথে, অবস্থান এবং সাইজ ডিফল্ট প্যারামিটার ভ্যালু দ্বারা প্রদান করা হয়। ডিফল্ট প্যারামিটার সাধারণত ক্যানভাসের [0, 0] পয়েন্টে আইটেমটি পজিশন করে এবং একটি ডিফল্ট size প্রদান করে যা সম্পূর্ণ ড্রয়িং এরিয়া পূরণ করে, যেমন উপরের উদাহরণে - আপনি দেখতে পাবেন যে আয়তক্ষেত্রটি উপরে বাঁদিকে পজিশন করা হয়েছে। আপনার আইটেমের সাইজ ও পজিশন অ্যাডজাস্ট করতে, আপনাকে Compose-এ কোঅর্ডিনেট সিস্টেম বুঝতে হবে।

কোঅর্ডিনেট সিস্টেমের ([0,0]) উৎস হল ড্রয়িং এরিয়ার একেবারে উপরে বাঁদিকের পিক্সেল। x ডানদিকে সরানোর সাথে সাথে বাড়ে এবং y নিচের দিকে সরানোর সাথে সাথে বাড়ে।

একটি গ্রিড কোঅর্ডিনেট সিস্টেম দেখাচ্ছে, যেখানে উপরে বাঁদিকে [0, 0] এবং নিচে ডানদিকে [width, height] দেখা যাচ্ছে
ছবি ২. ড্রয়িং কোঅর্ডিনেট সিস্টেম / ড্রয়িং গ্রিড।

যেমন, আপনি যদি ক্যানভাস এরিয়ার উপরের ডানদিকের কোণ থেকে নিচের বাঁদিকের কোণ পর্যন্ত একটি কর্ণ আঁকতে চান, তাহলে আপনি DrawScope.drawLine() ফাংশন ব্যবহার করতে পারেন এবং সংশ্লিষ্ট x ও y পজিশন সহ শুরু ও শেষের অফসেট নির্দিষ্ট করতে পারেন:

Canvas(modifier = Modifier.fillMaxSize()) {
    val canvasWidth = size.width
    val canvasHeight = size.height
    drawLine(
        start = Offset(x = canvasWidth, y = 0f),
        end = Offset(x = 0f, y = canvasHeight),
        color = Color.Blue
    )
}

বেসিক ট্রান্সফর্মেশন

DrawScope ড্রয়িং কমান্ড কোথায় বা কীভাবে এক্সিকিউট করা হবে তা পরিবর্তন করার জন্য ট্রান্সফর্মেশন অফার করে।

স্কেল

আপনার ড্রয়িং অপারেশনের সাইজ ফ্যাক্টর অনুযায়ী বাড়াতে DrawScope.scale() ব্যবহার করুন। scale()-এর মতো অপারেশন সংশ্লিষ্ট ল্যাম্বডার মধ্যে সব ড্রয়িং অপারেশনে প্রয়োগ করা হয়। যেমন, নিম্নলিখিত কোড scaleX ১০ গুণ এবং scaleY ১৫ গুণ বাড়ায়:

Canvas(modifier = Modifier.fillMaxSize()) {
    scale(scaleX = 10f, scaleY = 15f) {
        drawCircle(Color.Blue, radius = 20.dp.toPx())
    }
}

একটি বৃত্ত যা অসমভাবে স্কেল করা হয়েছে
ছবি ৩। Canvas-এ বৃত্তের উপর স্কেল অপারেশন প্রয়োগ করা।

অনুবাদ করুন

আপনার ড্রয়িং অপারেশন উপরে, নিচে, বাঁদিকে বা ডানদিকে সরাতে DrawScope.translate() ব্যবহার করুন। যেমন, নিম্নলিখিত কোডটি ড্রয়িংকে ডানদিকে ১০০ পিক্সেল এবং উপরে ৩০০ পিক্সেল সরায়:

Canvas(modifier = Modifier.fillMaxSize()) {
    translate(left = 100f, top = -300f) {
        drawCircle(Color.Blue, radius = 200.dp.toPx())
    }
}

একটি বৃত্ত যা কেন্দ্র থেকে সরে গেছে
ছবি ৪. Canvas-এ বৃত্তের উপর ট্রান্সলেট অপারেশন প্রয়োগ করা হচ্ছে।

ঘোরান

DrawScope.rotate() ব্যবহার করে পিভট পয়েন্টের চারপাশে আপনার ড্রয়িং অপারেশন ঘোরান। যেমন, নিম্নলিখিত কোডটি একটি আয়তক্ষেত্রকে ৪৫ ডিগ্রি ঘোরায়:

Canvas(modifier = Modifier.fillMaxSize()) {
    rotate(degrees = 45F) {
        drawRect(
            color = Color.Gray,
            topLeft = Offset(x = size.width / 3F, y = size.height / 3F),
            size = size / 3F
        )
    }
}

একটি ফোন যার স্ক্রিনের মাঝখানে ৪৫ ডিগ্রি ঘোরানো একটি আয়তক্ষেত্র রয়েছে
ছবি ৫. বর্তমান ড্রয়িং স্কোপে রোটেশন প্রয়োগ করতে আমরা rotate() ব্যবহার করি, যা আয়তক্ষেত্রটিকে ৪৫ ডিগ্রি ঘোরায়।

ইনসেট

বর্তমান DrawScope-এর ডিফল্ট প্যারামিটার অ্যাডজাস্ট করতে DrawScope.inset() ব্যবহার করুন, ড্রয়িংয়ের সীমানা পরিবর্তন করুন এবং সেই অনুযায়ী ড্রয়িং অনুবাদ করুন:

Canvas(modifier = Modifier.fillMaxSize()) {
    val canvasQuadrantSize = size / 2F
    inset(horizontal = 50f, vertical = 30f) {
        drawRect(color = Color.Green, size = canvasQuadrantSize)
    }
}

এই কোডটি ড্রয়িং কমান্ডে কার্যকরভাবে প্যাডিং যোগ করে:

একটি আয়তক্ষেত্র যার চারপাশে প্যাডিং করা হয়েছে
ছবি ৬. ড্রয়িং কমান্ডে ইনসেট প্রয়োগ করা হচ্ছে।

একাধিক ট্রান্সফর্মেশন

আপনার ড্রয়িংয়ে একাধিক ট্রান্সফর্মেশন প্রয়োগ করতে, DrawScope.withTransform() ফাংশন ব্যবহার করুন, যা আপনার সব কাঙ্ক্ষিত পরিবর্তনকে একত্রিত করে একটি একক ট্রান্সফর্মেশন তৈরি করে এবং প্রয়োগ করে। ব্যক্তিগত ট্রান্সফর্মেশনে নেস্টেড কল করার চেয়ে withTransform() ব্যবহার করা অনেক বেশি কার্যকর, কারণ Compose-কে প্রতিটি নেস্টেড ট্রান্সফর্মেশন হিসেব করে সেভ করার পরিবর্তে, একটি অপারেশনে সব ট্রান্সফর্মেশন একসাথে করা হয়।

যেমন, নিম্নলিখিত কোডটি আয়তক্ষেত্রে অনুবাদ ও রোটেশন, দুটিই প্রয়োগ করে:

Canvas(modifier = Modifier.fillMaxSize()) {
    withTransform({
        translate(left = size.width / 5F)
        rotate(degrees = 45F)
    }) {
        drawRect(
            color = Color.Gray,
            topLeft = Offset(x = size.width / 3F, y = size.height / 3F),
            size = size / 3F
        )
    }
}

একটি ফোন যার স্ক্রিনের একপাশে একটি ঘোরানো আয়তক্ষেত্র রয়েছে
ছবি ৭. withTransform ব্যবহার করে রোটেশন ও ট্রান্সলেশন, দুটিই প্রয়োগ করুন, আয়তক্ষেত্রটি ঘুরিয়ে বাঁদিকে সরিয়ে দিন।

সাধারণ ড্রয়িং অপারেশন

টেক্সট আঁকুন

Compose-এ টেক্সট ড্র করতে, আপনি সাধারণত Text কম্পোজেবল ব্যবহার করতে পারেন। তবে, আপনি যদি DrawScope-এ থাকেন অথবা কাস্টমাইজেশন সহ ম্যানুয়ালি টেক্সট আঁকতে চান, তাহলে DrawScope.drawText() পদ্ধতি ব্যবহার করতে পারেন।

টেক্সট আঁকতে, rememberTextMeasurer ব্যবহার করে TextMeasurer তৈরি করুন এবং পরিমাপকের সাথে drawText কল করুন:

val textMeasurer = rememberTextMeasurer()

Canvas(modifier = Modifier.fillMaxSize()) {
    drawText(textMeasurer, "Hello")
}

Canvas-এ আঁকা Hello দেখানো হচ্ছে
ছবি ৮. Canvas-এ টেক্সট আঁকা হচ্ছে।

টেক্সট পরিমাপ করুন

টেক্সট আঁকার কমান্ড অন্যান্য আঁকার কমান্ডের থেকে একটু আলাদাভাবে কাজ করে। সাধারণত, আপনি ড্রয়িং কমান্ডকে আকার/ছবি আঁকার জন্য সাইজ (প্রস্থ এবং উচ্চতা) দেন। টেক্সটের ক্ষেত্রে, কিছু প্যারামিটার আছে যা রেন্ডার করা টেক্সটের সাইজ কন্ট্রোল করে, যেমন ফন্ট সাইজ, ফন্ট, লিগেচার এবং লেটার স্পেসিং।

Compose-এর সাহায্যে, আপনি উপরে উল্লেখ করা ফ্যাক্টরের উপর নির্ভর করে, টেক্সটের পরিমাপ করা সাইজে অ্যাক্সেস পেতে, TextMeasurer ব্যবহার করতে পারবেন। আপনি যদি টেক্সটের পিছনে ব্যাকগ্রাউন্ড আঁকতে চান, তাহলে টেক্সটের জন্য প্রয়োজনীয় জায়গার সাইজ জানতে, পরিমাপ করা তথ্য ব্যবহার করতে পারেন:

val textMeasurer = rememberTextMeasurer()

Spacer(
    modifier = Modifier
        .drawWithCache {
            val measuredText =
                textMeasurer.measure(
                    AnnotatedString(longTextSample),
                    constraints = Constraints.fixedWidth((size.width * 2f / 3f).toInt()),
                    style = TextStyle(fontSize = 18.sp)
                )

            onDrawBehind {
                drawRect(pinkColor, size = measuredText.size.toSize())
                drawText(measuredText)
            }
        }
        .fillMaxSize()
)

এই কোড স্নিপেট টেক্সটে গোলাপি ব্যাকগ্রাউন্ড তৈরি করে:

সম্পূর্ণ এলাকার ⅔ সাইজ জুড়ে থাকা মাল্টি-লাইন টেক্সট, সাথে একটি ব্যাকগ্রাউন্ড আয়তক্ষেত্র
ছবি ৯. ব্যাকগ্রাউন্ডে আয়তাকার বক্স সহ মাল্টি-লাইন টেক্সট যা সম্পূর্ণ এলাকার ⅔ সাইজ জুড়ে রয়েছে।

কনস্ট্রেন্ট, ফন্ট সাইজ বা পরিমাপ করা সাইজকে প্রভাবিত করে এমন কোনও প্রপার্টি অ্যাডজাস্ট করলে নতুন সাইজ দেখানো হয়। আপনি width এবং height, দুটির জন্যই একটি নির্দিষ্ট সাইজ সেট করতে পারেন এবং তারপর টেক্সট সেট করা TextOverflow অনুসরণ করে। যেমন, নিম্নলিখিত কোড কম্পোজেবল এরিয়ার উচ্চতার ⅓ এবং প্রস্থের ⅓ অংশে টেক্সট রেন্ডার করে এবং TextOverflow-কে TextOverflow.Ellipsis-এ সেট করে:

val textMeasurer = rememberTextMeasurer()

Spacer(
    modifier = Modifier
        .drawWithCache {
            val measuredText =
                textMeasurer.measure(
                    AnnotatedString(longTextSample),
                    constraints = Constraints.fixed(
                        width = (size.width / 3f).toInt(),
                        height = (size.height / 3f).toInt()
                    ),
                    overflow = TextOverflow.Ellipsis,
                    style = TextStyle(fontSize = 18.sp)
                )

            onDrawBehind {
                drawRect(pinkColor, size = measuredText.size.toSize())
                drawText(measuredText)
            }
        }
        .fillMaxSize()
)

টেক্সট এখন শেষে উপবৃত্ত সহ সীমাবদ্ধতায় আঁকা হয়েছে:

গোলাপি ব্যাকগ্রাউন্ডে টেক্সট লেখা আছে, টেক্সটটি উপবৃত্তাকার আকারে কাটা।
ছবি ১০. TextOverflow.Ellipsis-এ টেক্সট পরিমাপ করার ক্ষেত্রে নির্দিষ্ট সীমাবদ্ধতা আছে।

ছবি আঁকুন

DrawScope-এর সাহায্যে ImageBitmap আঁকতে, ImageBitmap.imageResource() ব্যবহার করে ছবি লোড করুন এবং তারপর drawImage কল করুন:

val dogImage = ImageBitmap.imageResource(id = R.drawable.dog)

Canvas(modifier = Modifier.fillMaxSize(), onDraw = {
    drawImage(dogImage)
})

Canvas-এ আঁকা কুকুরের ছবি
ছবি ১১. Canvas-এ ImageBitmap আঁকা।

বেসিক আকার আঁকা

DrawScope-এ অনেক শেপ ড্রয়িং ফাংশন আছে। কোনও আকার আঁকতে, আগে থেকে নির্ধারিত আঁকার ফাংশনগুলির মধ্যে একটি ব্যবহার করুন, যেমন drawCircle:

val purpleColor = Color(0xFFBA68C8)
Canvas(
    modifier = Modifier
        .fillMaxSize()
        .padding(16.dp),
    onDraw = {
        drawCircle(purpleColor)
    }
)

API

আউটপুট

drawCircle()

বৃত্ত আঁকুন

drawRect()

রেকট্যাঙ্গেল আঁকুন

drawRoundedRect()

বৃত্তাকার কোণবিশিষ্ট আয়তক্ষেত্র আঁকো

drawLine()

লাইন আঁকুন

drawOval()

ডিম্বাকৃতি আঁকো

drawArc()

আর্ক আঁকুন

drawPoints()

পয়েন্ট আঁকুন

পাথ আঁকুন

পাথ হল গাণিতিক নির্দেশের একটি সিরিজ যা একবার এক্সিকিউট করা হলে একটি ড্রয়িং তৈরি হয়। DrawScope, DrawScope.drawPath() পদ্ধতি ব্যবহার করে একটি পাথ আঁকতে পারে।

যেমন, ধরুন আপনি একটি ত্রিভুজ আঁকতে চান। আপনি ড্রয়িং এরিয়ার সাইজ ব্যবহার করে lineTo() এবং moveTo()-এর মতো ফাংশন সহ একটি পাথ জেনারেট করতে পারবেন। তারপর, ত্রিভুজ পেতে এই নতুন তৈরি করা পাথ সহ drawPath() কল করুন।

Spacer(
    modifier = Modifier
        .drawWithCache {
            val path = Path()
            path.moveTo(0f, 0f)
            path.lineTo(size.width / 2f, size.height / 2f)
            path.lineTo(size.width, 0f)
            path.close()
            onDrawBehind {
                drawPath(path, Color.Magenta, style = Stroke(width = 10f))
            }
        }
        .fillMaxSize()
)

Compose-এ আঁকা উল্টো বেগুনি রঙের পাথ ট্রায়াঙ্গেল
ছবি ১২. Compose-এ Path তৈরি ও ড্র করা।

Canvas অবজেক্ট অ্যাক্সেস করা হচ্ছে

DrawScope-এর মাধ্যমে, আপনার কাছে Canvas অবজেক্টের সরাসরি অ্যাক্সেস নেই। আপনি DrawScope.drawIntoCanvas() ব্যবহার করে Canvas অবজেক্টে অ্যাক্সেস পেতে পারেন, যার উপর আপনি ফাংশন কল করতে পারেন।

যেমন, আপনার কাছে যদি কোনও কাস্টম Drawable থাকে যা আপনি ক্যানভাসে আঁকতে চান, তাহলে আপনি ক্যানভাস অ্যাক্সেস করতে এবং Drawable#draw() কল করতে পারেন, Canvas অবজেক্টে পাস করে:

val drawable = ShapeDrawable(OvalShape())
Spacer(
    modifier = Modifier
        .drawWithContent {
            drawIntoCanvas { canvas ->
                drawable.setBounds(0, 0, size.width.toInt(), size.height.toInt())
                drawable.draw(canvas.nativeCanvas)
            }
        }
        .fillMaxSize()
)

একটি ডিম্বাকৃতি কালো ShapeDrawable যা সম্পূর্ণ সাইজ দখল করে আছে
ছবি ১৩. Drawable আঁকার জন্য ক্যানভাস অ্যাক্সেস করা হচ্ছে।

আরও জানুন

Compose-এ ড্রয়িং সম্পর্কে আরও তথ্যের জন্য, নিম্নলিখিত রিসোর্সগুলি দেখুন: