একটি কাস্টম ভিউ এর সবচেয়ে গুরুত্বপূর্ণ অংশ হল এর চেহারা। কাস্টম অঙ্কন আপনার অ্যাপ্লিকেশনের প্রয়োজন অনুযায়ী সহজ বা জটিল হতে পারে। এই নথিটি সবচেয়ে সাধারণ কিছু ক্রিয়াকলাপ কভার করে।
আরও তথ্যের জন্য, অঙ্কনযোগ্য ওভারভিউ দেখুন।
ওভাররাইড onDraw()
একটি কাস্টম ভিউ আঁকার সবচেয়ে গুরুত্বপূর্ণ ধাপ হল onDraw()
পদ্ধতিকে ওভাররাইড করা। onDraw()
এর পরামিতি হল একটি Canvas
অবজেক্ট যা ভিউ নিজেই আঁকার জন্য ব্যবহার করতে পারে। Canvas
ক্লাস টেক্সট, লাইন, বিটম্যাপ এবং অন্যান্য অনেক গ্রাফিক্স প্রাইমিটিভ আঁকার পদ্ধতি সংজ্ঞায়িত করে। আপনি আপনার কাস্টম ইউজার ইন্টারফেস (UI) তৈরি করতে onDraw()
এ এই পদ্ধতিগুলি ব্যবহার করতে পারেন।
একটি Paint
অবজেক্ট তৈরি করে শুরু করুন। পরবর্তী বিভাগে Paint
সম্পর্কে আরও বিস্তারিত আলোচনা করা হয়েছে।
অঙ্কন বস্তু তৈরি করুন
android.graphics
ফ্রেমওয়ার্ক অঙ্কনকে দুটি ক্ষেত্রে বিভক্ত করে:
- কি আঁকতে হবে,
Canvas
দ্বারা পরিচালিত। - কিভাবে আঁকা,
Paint
দ্বারা পরিচালিত.
উদাহরণস্বরূপ, Canvas
একটি লাইন আঁকতে একটি পদ্ধতি প্রদান করে এবং Paint
সেই লাইনের রঙ নির্ধারণ করার পদ্ধতি প্রদান করে। Canvas
একটি আয়তক্ষেত্র আঁকার একটি পদ্ধতি রয়েছে এবং Paint
সেই আয়তক্ষেত্রটিকে একটি রঙ দিয়ে পূরণ করতে হবে নাকি খালি রাখতে হবে তা নির্ধারণ করে। Canvas
সেই আকারগুলিকে সংজ্ঞায়িত করে যা আপনি স্ক্রিনে আঁকতে পারেন এবং Paint
আপনার আঁকা প্রতিটি আকৃতির রঙ, শৈলী, ফন্ট এবং আরও অনেক কিছুকে সংজ্ঞায়িত করে।
আপনি কিছু আঁকার আগে, এক বা একাধিক Paint
অবজেক্ট তৈরি করুন। নিম্নলিখিত উদাহরণটি init
নামক একটি পদ্ধতিতে এটি করে। এই পদ্ধতিটি জাভা থেকে কনস্ট্রাক্টর থেকে বলা হয়, তবে এটি কোটলিনে ইনলাইনে শুরু করা যেতে পারে।
কোটলিন
@ColorInt private var textColor // Obtained from style attributes. @Dimension private var textHeight // Obtained from style attributes. private val textPaint = Paint(ANTI_ALIAS_FLAG).apply { color = textColor if (textHeight == 0f) { textHeight = textSize } else { textSize = textHeight } } private val piePaint = Paint(Paint.ANTI_ALIAS_FLAG).apply { style = Paint.Style.FILL textSize = textHeight } private val shadowPaint = Paint(0).apply { color = 0x101010 maskFilter = BlurMaskFilter(8f, BlurMaskFilter.Blur.NORMAL) }
জাভা
private Paint textPaint; private Paint piePaint; private Paint shadowPaint; @ColorInt private int textColor; // Obtained from style attributes. @Dimension private float textHeight; // Obtained from style attributes. private void init() { textPaint = new Paint(Paint.ANTI_ALIAS_FLAG); textPaint.setColor(textColor); if (textHeight == 0) { textHeight = textPaint.getTextSize(); } else { textPaint.setTextSize(textHeight); } piePaint = new Paint(Paint.ANTI_ALIAS_FLAG); piePaint.setStyle(Paint.Style.FILL); piePaint.setTextSize(textHeight); shadowPaint = new Paint(0); shadowPaint.setColor(0xff101010); shadowPaint.setMaskFilter(new BlurMaskFilter(8, BlurMaskFilter.Blur.NORMAL)); ... }
সময়ের আগে বস্তু তৈরি করা একটি গুরুত্বপূর্ণ অপ্টিমাইজেশন। দৃশ্যগুলি ঘন ঘন পুনরায় আঁকা হয়, এবং অনেক অঙ্কন বস্তুর জন্য ব্যয়বহুল আরম্ভের প্রয়োজন হয়। আপনার onDraw()
পদ্ধতির মধ্যে অঙ্কন বস্তু তৈরি করা উল্লেখযোগ্যভাবে কর্মক্ষমতা হ্রাস করে এবং আপনার UI মন্থর করে তুলতে পারে।
লেআউট ইভেন্টগুলি পরিচালনা করুন
আপনার কাস্টম ভিউ সঠিকভাবে আঁকতে, এটি কী আকারের তা খুঁজে বের করুন। জটিল কাস্টম দৃশ্যগুলিকে প্রায়শই স্ক্রিনে তাদের এলাকার আকার এবং আকৃতির উপর নির্ভর করে একাধিক লেআউট গণনা করতে হয়। স্ক্রিনে আপনার দৃশ্যের আকার সম্পর্কে কখনও অনুমান করবেন না। এমনকি যদি শুধুমাত্র একটি অ্যাপ আপনার ভিউ ব্যবহার করে, সেই অ্যাপটিকে পোর্ট্রেট এবং ল্যান্ডস্কেপ মোডে বিভিন্ন স্ক্রীন মাপ, একাধিক স্ক্রীনের ঘনত্ব এবং বিভিন্ন আকৃতির অনুপাত পরিচালনা করতে হবে।
যদিও পরিমাপ পরিচালনার জন্য View
অনেক পদ্ধতি রয়েছে, সেগুলির বেশিরভাগকে ওভাররাইড করার প্রয়োজন নেই৷ যদি আপনার দৃশ্যের আকারের উপর বিশেষ নিয়ন্ত্রণের প্রয়োজন না হয় তবে শুধুমাত্র একটি পদ্ধতি ওভাররাইড করুন: onSizeChanged()
।
onSizeChanged()
বলা হয় যখন আপনার ভিউকে প্রথমে একটি সাইজ বরাদ্দ করা হয় এবং আবার যদি কোনো কারণে আপনার ভিউয়ের আকার পরিবর্তন হয়। আপনি প্রতিবার আঁকার সময় তাদের পুনরায় গণনা করার পরিবর্তে onSizeChanged()
তে অবস্থান, মাত্রা এবং আপনার ভিউ এর আকারের সাথে সম্পর্কিত অন্য যেকোনো মান গণনা করুন। নিম্নলিখিত উদাহরণে, onSizeChanged()
হল যেখানে ভিউ চার্টের বাউন্ডিং আয়তক্ষেত্র এবং টেক্সট লেবেলের আপেক্ষিক অবস্থান এবং অন্যান্য ভিজ্যুয়াল উপাদান গণনা করে।
যখন আপনার ভিউ একটি মাপ বরাদ্দ করা হয়, লেআউট ম্যানেজার অনুমান করে যে আকারটি ভিউ এর প্যাডিং অন্তর্ভুক্ত করে। আপনি যখন আপনার দৃশ্যের আকার গণনা করেন তখন প্যাডিং মানগুলি পরিচালনা করুন। এখানে onSizeChanged()
থেকে একটি স্নিপেট যা দেখায় কিভাবে এটি করতে হয়:
কোটলিন
private val showText // Obtained from styled attributes. private val textWidth // Obtained from styled attributes. override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) { super.onSizeChanged(w, h, oldw, oldh) // Account for padding. var xpad = (paddingLeft + paddingRight).toFloat() val ypad = (paddingTop + paddingBottom).toFloat() // Account for the label. if (showText) xpad += textWidth.toFloat() val ww = w.toFloat() - xpad val hh = h.toFloat() - ypad // Figure out how big you can make the pie. val diameter = Math.min(ww, hh) }
জাভা
private Boolean showText; // Obtained from styled attributes. private int textWidth; // Obtained from styled attributes. @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); // Account for padding. float xpad = (float)(getPaddingLeft() + getPaddingRight()); float ypad = (float)(getPaddingTop() + getPaddingBottom()); // Account for the label. if (showText) xpad += textWidth; float ww = (float)w - xpad; float hh = (float)h - ypad; // Figure out how big you can make the pie. float diameter = Math.min(ww, hh); }
আপনার ভিউ এর লেআউট প্যারামিটারগুলির উপর আপনার যদি আরও সূক্ষ্ম নিয়ন্ত্রণের প্রয়োজন হয়, তাহলে onMeasure()
প্রয়োগ করুন। এই পদ্ধতির পরামিতিগুলি হল View.MeasureSpec
মান যা আপনাকে বলে যে আপনার ভিউয়ের অভিভাবক আপনার ভিউ কত বড় হতে চান এবং সেই আকারটি একটি কঠিন সর্বোচ্চ নাকি শুধুমাত্র একটি পরামর্শ। একটি অপ্টিমাইজেশান হিসাবে, এই মানগুলি প্যাক করা পূর্ণসংখ্যা হিসাবে সংরক্ষণ করা হয় এবং আপনি প্রতিটি পূর্ণসংখ্যাতে সংরক্ষিত তথ্য আনপ্যাক করতে View.MeasureSpec
এর স্ট্যাটিক পদ্ধতিগুলি ব্যবহার করেন৷
এখানে onMeasure()
এর একটি উদাহরণ বাস্তবায়ন। এই বাস্তবায়নে, এটি চার্টটিকে তার লেবেলের মতো বড় করার জন্য তার ক্ষেত্রটিকে যথেষ্ট বড় করার চেষ্টা করে:
কোটলিন
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { // Try for a width based on your minimum. val minw: Int = paddingLeft + paddingRight + suggestedMinimumWidth val w: Int = View.resolveSizeAndState(minw, widthMeasureSpec, 1) // Whatever the width is, ask for a height that lets the pie get as big as // it can. val minh: Int = View.MeasureSpec.getSize(w) - textWidth.toInt() + paddingBottom + paddingTop val h: Int = View.resolveSizeAndState(minh, heightMeasureSpec, 0) setMeasuredDimension(w, h) }
জাভা
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { // Try for a width based on your minimum. int minw = getPaddingLeft() + getPaddingRight() + getSuggestedMinimumWidth(); int w = resolveSizeAndState(minw, widthMeasureSpec, 1); // Whatever the width is, ask for a height that lets the pie get as big as it // can. int minh = MeasureSpec.getSize(w) - (int)textWidth + getPaddingBottom() + getPaddingTop(); int h = resolveSizeAndState(minh, heightMeasureSpec, 0); setMeasuredDimension(w, h); }
এই কোডে তিনটি গুরুত্বপূর্ণ বিষয় লক্ষ্য করা যায়:
- গণনাগুলি ভিউ এর প্যাডিংকে বিবেচনা করে। পূর্বে উল্লেখ করা হয়েছে, এটি ভিউ এর দায়িত্ব.
- চূড়ান্ত প্রস্থ এবং উচ্চতা মান তৈরি করতে সহায়ক পদ্ধতি
resolveSizeAndState()
ব্যবহার করা হয়। এই সাহায্যকারী একটি উপযুক্তView.MeasureSpec
মান ফেরত দেয় যা ভিউয়ের প্রয়োজনীয় আকারকেonMeasure()
এ পাস করা মানের সাথে তুলনা করে। -
onMeasure()
কোনো রিটার্ন মান নেই। পরিবর্তে, মেথডsetMeasuredDimension()
কল করে তার ফলাফলের সাথে যোগাযোগ করে। এই পদ্ধতিতে কল করা বাধ্যতামূলক। আপনি যদি এই কলটি বাদ দেন,View
ক্লাস একটি রানটাইম ব্যতিক্রম নিক্ষেপ করে।
আঁকা
আপনি আপনার অবজেক্ট তৈরি এবং পরিমাপ কোড সংজ্ঞায়িত করার পরে, আপনি onDraw()
বাস্তবায়ন করতে পারেন। প্রতিটি ভিউ onDraw()
ভিন্নভাবে প্রয়োগ করে, তবে কিছু সাধারণ ক্রিয়াকলাপ রয়েছে যা বেশিরভাগ ভিউ শেয়ার করে:
-
drawText()
ব্যবহার করে পাঠ্য আঁকুন।setTypeface()
কল করে টাইপফেস এবংsetColor()
কল করে পাঠ্যের রঙ নির্দিষ্ট করুন। -
drawRect()
,drawOval()
এবংdrawArc()
ব্যবহার করে আদিম আকার আঁকুন।setStyle()
কল করে আকারগুলি ভরা, রূপরেখা বা উভয়ই পরিবর্তন করুন। -
Path
ক্লাস ব্যবহার করে আরও জটিল আকার আঁকুন। একটিPath
অবজেক্টে লাইন এবং বক্ররেখা যোগ করে একটি আকৃতি নির্ধারণ করুন, তারপরdrawPath()
ব্যবহার করে আকৃতিটি আঁকুন। আদিম আকারের মতো,setStyle()
এর উপর নির্ভর করে পাথগুলি রূপরেখা, ভরাট বা উভয় হতে পারে। -
LinearGradient
অবজেক্ট তৈরি করে গ্রেডিয়েন্ট ফিল সংজ্ঞায়িত করুন। পূর্ণ আকারে আপনারLinearGradient
ব্যবহার করতেsetShader()
কল করুন। -
drawBitmap()
ব্যবহার করে বিটম্যাপ আঁকুন।
নিম্নলিখিত কোড পাঠ্য, লাইন এবং আকারের মিশ্রণ আঁকে:
কোটলিন
private val data = mutableListOf<Item>() // A list of items that are displayed. private var shadowBounds = RectF() // Calculated in onSizeChanged. private var pointerRadius: Float = 2f // Obtained from styled attributes. private var pointerX: Float = 0f // Calculated in onSizeChanged. private var pointerY: Float = 0f // Calculated in onSizeChanged. private var textX: Float = 0f // Calculated in onSizeChanged. private var textY: Float = 0f // Calculated in onSizeChanged. private var bounds = RectF() // Calculated in onSizeChanged. private var currentItem: Int = 0 // The index of the currently selected item. override fun onDraw(canvas: Canvas) { super.onDraw(canvas) canvas.apply { // Draw the shadow. drawOval(shadowBounds, shadowPaint) // Draw the label text. drawText(data[currentItem].label, textX, textY, textPaint) // Draw the pie slices. data.forEach {item -> piePaint.shader = item.shader drawArc( bounds, 360 - item.endAngle, item.endAngle - item.startAngle, true, piePaint ) } // Draw the pointer. drawLine(textX, pointerY, pointerX, pointerY, textPaint) drawCircle(pointerX, pointerY, pointerRadius, textPaint) } } // Maintains the state for a data item. private data class Item( var label: String, var value: Float = 0f, @ColorInt var color: Int = 0, // Computed values. var startAngle: Float = 0f, var endAngle: Float = 0f, var shader: Shader )
জাভা
private List<Item> data = new ArrayList<Item>(); // A list of items that are displayed. private RectF shadowBounds; // Calculated in onSizeChanged. private float pointerRadius; // Obtained from styled attributes. private float pointerX; // Calculated in onSizeChanged. private float pointerY; // Calculated in onSizeChanged. private float textX; // Calculated in onSizeChanged. private float textY; // Calculated in onSizeChanged. private RectF bounds; // Calculated in onSizeChanged. private int currentItem = 0; // The index of the currently selected item. protected void onDraw(Canvas canvas) { super.onDraw(canvas); // Draw the shadow. canvas.drawOval( shadowBounds, shadowPaint ); // Draw the label text. canvas.drawText(data.get(currentItem).label, textX, textY, textPaint); // Draw the pie slices. for (int i = 0; i < data.size(); ++i) { Item it = data.get(i); piePaint.setShader(it.shader); canvas.drawArc( bounds, 360 - it.endAngle, it.endAngle - it.startAngle, true, piePaint ); } // Draw the pointer. canvas.drawLine(textX, pointerY, pointerX, pointerY, textPaint); canvas.drawCircle(pointerX, pointerY, pointerRadius, textPaint); } // Maintains the state for a data item. private class Item { public String label; public float value; @ColorInt public int color; // Computed values. public int startAngle; public int endAngle; public Shader shader; }
গ্রাফিক্স প্রভাব প্রয়োগ করুন
অ্যান্ড্রয়েড 12 (এপিআই লেভেল 31) RenderEffect
ক্লাস যোগ করে, যা সাধারণ গ্রাফিক্স ইফেক্ট যেমন ব্লার, কালার ফিল্টার, অ্যান্ড্রয়েড শেডার ইফেক্ট এবং আরও অনেক কিছু View
অবজেক্ট এবং রেন্ডারিং হায়ারার্কিতে প্রয়োগ করে। আপনি চেইন প্রভাব হিসাবে প্রভাব একত্রিত করতে পারেন, যা একটি অভ্যন্তরীণ এবং বাইরের প্রভাব, বা মিশ্র প্রভাব নিয়ে গঠিত। এই বৈশিষ্ট্যের জন্য সমর্থন ডিভাইস প্রক্রিয়াকরণ ক্ষমতা উপর নির্ভর করে পরিবর্তিত হয়.
আপনি View.setRenderEffect(RenderEffect)
কল করে একটি View
জন্য অন্তর্নিহিত RenderNode
প্রভাব প্রয়োগ করতে পারেন।
একটি RenderEffect
অবজেক্ট বাস্তবায়ন করতে, নিম্নলিখিতগুলি করুন:
view.setRenderEffect(RenderEffect.createBlurEffect(radiusX, radiusY, SHADER_TILE_MODE))
আপনি প্রোগ্রামগতভাবে ভিউ তৈরি করতে পারেন বা এটি একটি XML লেআউট থেকে স্ফীত করতে পারেন এবং ভিউ বাইন্ডিং বা findViewById()
ব্যবহার করে এটি পুনরুদ্ধার করতে পারেন।