আমরা যখন XML ব্যবহার করে Android অ্যাপের graphical user interface design করি তখন Android Studio এর preview-তে ডিজাইনটা শো করে। অনেক সময়েই ঠিকঠাক বুঝা যায় না যে অ্যাপটা রান করার পর UI-টা কেমন দেখাতে পারে। যেমন, আমরা যখন ListView বা RecyclerView এর জন্য লেয়াউট ডিজাইন করি তখন কিন্তু প্রিভিউতে লিস্টটা আমাদের রিয়েল অ্যাপের মত দেখায় না। আমরা খানিকটা আন্দাজ করে ডিজাইন করি বা RecyclerView এর সব কাজকর্ম শেষ করে অ্যাপ রান করে এরপর দেখি যে লিস্টটা কেমন দেখায়। কিন্তু প্রিভিউতেই যদি আমরা লিস্টটা running App এর মত দেখতে পেতাম তাহলে বেশ সুবিধা হত তাই না? এরকম কিছু সুবিধার জন্য Android Studio-তে রয়েছে “tools” namespace attribute. খুব সিম্পল কিন্তু দারুণ সব কাজের জন্য tools attribute-গুলো বেশ ব্যবহৃত হয়। আজকের পোস্টে এগুলো নিয়েই কথা বলব।
তাত্ত্বিক আলোচনায় না গিয়ে আমরা বরং কয়েকটা UI design করার চেষ্টা করি। এগুলো ডিজাইন করতে করতে গিয়ে tools attribute-গুলোর ব্যবহার সম্পর্কে জেনে যাব।
Problem 1
প্রথমে একদম সিম্পল কাজ করি। পাশের এই UI-টা ডিজাইন করতে হবে। মনে হতে পারে এটা অ্যাপ রান করার পরের ইউজার ইন্টারফেস। কিন্তু আসলে তা না। বুঝাই যাচ্ছে এখানে যেই তথ্যগুলো দেখানো হচ্ছে সেগুলো অ্যাপ রান করার পর রানটাইমে লোড করাতে হবে। Name, Email ও Phone এর label-গুলো বামে নীল রঙে আর ডানে এদের corresponding value-গুলো কালো রঙে দেখানো হয়েছে। Label-গুলো প্রিডিফাইন্ড। অর্থাৎ এগুলো XML এ আগে থেকে text attribute এর মাধ্যমে define করে দেয়া যায়। Label-গুলোর ক্ষেত্রে যেহেতু XML এর মধ্যেই টেক্সটের ভ্যালু বসাতে পারি তাই চাইলে android:text=”Name”, android:text=”Email” ও android:text=”Phone” এভাবে লিখে টেক্সট ডিফাইন করে দিতে পারি। কিন্তু বিভিন্ন কারণেই আমাদের এরকম hardcoded string ইউজ করা উচিত না। এভাবে লিখলে Android Studio XML editor এ একটা yellow highlighted warning message দিবে। তাই আমাদের উচিত strings.xml ফাইলে স্ট্রিংগুলো রেখে Layout এ string resource-গুলো ইউজ করা। ব্যক্তিগত ভাবে আমার টার্গেট থাকে কোডের মধ্যে Android Studio এর ওয়ার্নিং মেসেজের সংখ্যা কিভাবে minimize করা যায়। কোডের জায়গায় জায়গায় হলুদ রঙের হাইলাইটেড ওয়ার্নিং দেখলেই বিরক্ত লাগে। এই পোস্টের সাথে যেই প্রোজেক্টের সোর্সকোড দেয়া আছে সেখানে ১০টার মত XML Layout ফাইল দেয়া আছে। কোনোটার মধ্যেই একটাও ওয়ার্নিং মেসেজ নাই। যাই হোক, এতো গেল predefined Label-গুলোর কথা। কিন্তু ভ্যালুগুলো আমরা চাই নেটওয়ার্ক কল করে বা অন্য কোনো data source থেকে ডেটা এনে জাভা কোড থেকে সেট করতে। তো আপনি xml preview-তে অ্যাপ রান করার পর কেমন দেখতে হবে সেটা বুঝবেন কিভাবে? সেজন্য xml এ ভ্যালুর widget-গুলোতেও কিছু dummy data বসিয়ে দিতে পারেন। তখন অাপনার Android Studio একটা ওয়ার্নিং দেখাবে যে হার্ডকোডেড স্ট্রিং এখানে না দিয়ে স্ট্রিং রিসোর্স ব্যবহার করতে। আপনি শুধু ডামি ডেটার জন্য নিশ্চয়ই কষ্ট করতে চাইবেন না। আর যেহেতু রানটাইমে ডেটাগুলো setText() করতেই হবে তাই TextView-গুলোতে text attribute এর মাধ্যমে টেক্সট সেট করলে এটার জন্যও অ্যাপের সাইজ কয়েক বাইট বাড়বে! আবার প্রতিশ্রুতিশীল ডেভেলপার হিসাবে আপনিও হয়ত চান না আপনার কোডে এত্তোগুলা ওয়ার্নিং দেখাক! তাহলে এখন উপায়?
ও ভালো কথা! আমাদের UI-তে একটা ProgressBar-ও আছে! আমরা চাই এই Activity ওপেন হবার সময় প্রয়োজন হলে একে শো করাবো। অন্যথায় by default এটি invisible থাকবে। যেহেতু সব সময় এটা দেখা যাবে না তাই হয়ত আপনি ProgressBar এ android:visibility=”gone” দিয়ে দিলেন। তাহলে কিন্তু এটা আপনার XML preview-তে আর শো করবে না। আপনার কাজ করার কয়েক মাস পর অন্য কেউ যখন এই কোড নিয়ে কাজ করতে শুরু করবে সে হয়ত বুঝবেও না এই লেআউটে কোনো ProgressBar আছে। তার মানে আমাদেরকে এমন একটা সিসটেম করতে হবে যেন ProgressBar-টা শুধু preview-তে শো করে, কিন্তু রান করার পর বাই ডিফল্ট এটা শো না করে।
প্রবলেম নিয়ে অনেক গপ্পোসপ্পো হয়েছে। এবার সলিউশনে আসা যাক! আমরা আসলে কী করতে চাচ্ছি এখানে?
- কিছু dummy text data বসাতে
- ডামি টেক্সটগুলো শুধু প্রিভিউতে শো করবে
- রানটাইমে ডামি টেক্সটগুলোর কোনো ইফেক্ট অ্যাপে থাকবে না
- ডামি টেক্সটগুলোর কারণে XML editor এ কোনো ওয়ার্নিং থাকা যাবে না
- ডামি টেক্সটগুলোর জন্য কষ্ট করে strings.xml এ স্ট্রিং রিসোর্স রাখা যাবে না (তাতে অযথা অ্যাপের সাইজ বাড়বে)
- প্রিভিউতে একটা ProgressBar শো করবে
- অ্যাপ রান হবার পর বাই ডিফল্ট ProgressBar hide থাকবে
Solution
XML এর মাধ্যমে কোনো একটা TextView-তে টেক্সট দেখানোর জন্য android:text=”John Doe” ব্যবহার করি। এই লাইনের ইফেক্ট অ্যাপ বিল্ড করার পরেও থাকবে। অর্থাৎ রান করলে এই লাইনের জন্য সংশ্লিষ্ট TextView-তে John Doe লেখা দেখা যাবে। কিন্তু শুধু প্রিভিউতে ডামি হিসাবে দেখানোর জন্য text attribute এর namespace হিসাবে android এর স্থলে দিতে হবে tools. তাহলে এই টেক্সট ফিল্ডটা উপরে উল্লেখিত ৫ টা শর্তই পূরণ করবে।
প্রথম যখন কোনো একটা layout ফাইলে tools attribute লিখবেন তখন red color দিয়ে একটা error দেখাবে। তখন tools এর উপর mouse cursor রেখে Alt+Enter প্রেস করলে tools namespace create করার অপশন দেখাবে। সেটা সিলেক্ট করলেই কাজ হয়ে যাবে। এটা ম্যানুয়্যালি করতে পারেন Layout এর root এ xmlns:tools=”http://schemas.android.com/tools” লিখে দেয়ার মাধ্যমে। Alt+Enter দিয়ে করলে এই লাইনটাই অটো জেনারেট হবে, কষ্ট করে আপনাকে টাইপ করতে হবে না। এখন আশা করি প্রিভিউতে আপনার ডামি টেক্সট শো করছে। আর অ্যাপ রান করলে দেখবেন সেখানে এই টেক্সট শো করছে না।
এবার ProgressBar এর কোডটা দেখে নেয়া যাক।
আমাদের টার্গেট ছিল ProgressBar রান করার পর by default শো করবে না। প্রয়োজন অনুসারে আমরা জাভা কোডের মাধ্যমে একে শো করাবো। তাই প্রথম visibility attribute এর ভ্যালু সেট করা হয়েছে “gone” (যার namespace হচ্ছে android). এই ভ্যালুর ইফেক্ট অ্যাপ রান করার পর থাকবে। কিন্তু আমরা প্রিভিউতে দেখতে চাচ্ছি ProgressBar এর পজিশন ঠিকঠাক আছে কিনা। তাই tools namespace ইউজ করে visibility attribute এর ভ্যালু সেট করা হয়েছে “visible”. যেহেতু tools namespace ইউজ করলে তার সংশ্লিষ্ট attribute-এর প্রভাব অ্যাপের রানটাইমে থাকে না শুধু প্রিভিউতেই দেখা যায় তাই ProgressBar-টি শুধু প্রিভিউতেই দেখা যাবে। রান করলে দেখা যাবে না। আমরা চাইলে যে কোনো অ্যাট্রিবিউটই tools namespace দিয়ে লিখে প্রিভিউ দেখতে পারি।
tools namespace ব্যবহার করে যে সকল attribute লিখা হয় সেগুলো অ্যাপ build করার সময় Android Studio বাদ দিয়ে দেয়। অর্থাৎ APK ফাইলে tools attribute-গুলো থাকেই না। ফলে প্রিভিউতে দেখা গেলেও tools attribute-গুলো অ্যাপের সাইজ বাড়ায় না। কিন্তু android namespace দিয়ে যদি কোনো ডামি টেক্সটও কোনো TextView-তে লিখা হয় তাহলে সেটা কিন্তু APK তে থাকে। রান টাইমে সেই টেক্সট ফিল্ডের ডেটা চেঞ্জ হলে ডামি ডেটা দেখা যায় না ঠিক, কিন্তু অ্যাপের সাইজ অতি অল্প পরিমাণে হলেও বাড়ে!
Problem 2
কোনো একটা food App এর জন্য একটা Activity-তে RecyclerView এর মাধ্যমে food list শো করতে হবে। UI design শেষ হবার পর আপনার food list দেখানোর Activity এর XML preview দেখতে হবে নিচের বাম পাশের ছবির মত। কিন্তু প্রিভিউতেও আমরা রান করা অ্যাপের মত ডামি ডেটা দিয়ে লিস্ট দেখাতে চাই। যেরকম দেখা যাচ্ছে ডানের ছবিতে।
একবার একটা প্রজেক্টে এরকম লিস্ট দেখানোর আগে UI-টা ম্যানেজমেন্টকে দেখানোর দরকার হয়েছিল। সেটা করতে গিয়ে RecyclerView এর আইটেমটাকে Activity-তে পরপর ৩-৪ বার বসিয়ে স্ক্রিনশট নিয়ে পাঠিয়েছিলাম। তখনও জানতাম না যে Android Studio-তে এই কাজটা কত সহজে করা যায়!
Solution
RecyclerView-তে যেই আইটেমটা প্রতিবার recycle হবে সেই আইটেমের UI টা প্রথমে ডিজাইন করতে হবে। অর্থাৎ উপেরর ডানের ছবির একটা আইটেম আমাদেরকে ডিজাইন করতে হবে। ধরি, এই আইটেমের ফাইলের নাম item_food.xml. প্রতিক্ষেত্রে এই আইটেমের বিভিন্ন widget এর ডেটাগুলো সেট করতে হবে “tools” attribute দিয়ে। যেমন একটা ইমেজ আছে। image set করার জন্য src property এর namespace হবে “tools”. তাহলে প্রিভিউতে শুধু একটা ডামি ইমেজ শো করবে। অ্যাপ রান করার পর ডামি ইমেজ শো করবে না। একই ভাবে ফুডের নাম, প্রাইস, ডেস্ক্রিপশন ইত্যাদিও “tools” namespace দিয়ে সেট করতে হবে। এগুলো আগের প্রবলেমের মতই। তাই আর বেশি ব্যাখ্যা করার দরকার মনে করছি না।
এরপর আসি Activity এর XML এর প্রিভিউতে। যেখানে RecyclerView widget অ্যাড করা হয়েছে। আপাতত activity এর প্রিভিউতে উপরের ছবির বামের মত একটা প্রিভিউ দেখা যাচ্ছে। এখানে আমাদের food item এর ডিজাইনটা লিস্ট আকারে দেখাতে চাই। নিচে RecyclerView widget এর কোডটুকু দেয়া হলো
লাল চিহ্নিত লাইন দুইটি আমাদের প্রিভিউয়ের জন্য কাজ করছে। itemCount এর মাধ্যমে বলে দিচ্ছি যে, লিস্টে আমরা ২ টি আইটেম দেখাতে চাচ্ছি। itemCount ব্যবহার না করলে by default ১০ টি আইটেম recycle করা হয়ে থাকে। পরের প্রোপার্টিটা হচ্ছে “listitem”. এর মাধ্যমে বলে দেয়া হচ্ছে যে, কোন XML file টা আমরা এই RecyclerView এর মাঝে recycle করাতে চাই। আমাদের ফুড আইটেমের ডিজাইন করা ফাইলের নাম দিয়েছিলাম item_food.xml. যা রাখা আছে layout directory-তে। এই দুইটা লাইন অ্যাড করাতেই সুন্দর একটা রিয়েল টাইমের মত প্রিভিউ জেনারেট করে দেখাচ্ছে। দারুণ না?
Problem 3
এবার কিছু ইউজারের লিস্ট শো করার জন্য একটা UI design করতে হবে। যথারীতি “tools” attribute ব্যবহার করা ছাড়া ডিজাইন করলে নিচের ছবির বামের মত প্রিভিউ দেখাবে। কিন্তু আমরা দেখাতে চাই ডানের ছবির মত ইউজার লিস্ট। আগের প্রবলেমের সাথে এটার পার্থক্য হচ্ছে এখানে সবগুলো ডেটা ডায়নামিক্যাল্যি চেঞ্জ হচ্ছে। ইউজারের নাম, ফোন, শর্ট বায়ো এবং ছবিও ডায়নামিক্যাল্যি সেট হবে। এতে আমাদের প্রিভিউ লিস্টটা আগের চেয়ে বেশি realistic হবে।
Solution
আগের প্রবলেমের সলুশনের মত প্রায় একই ভাবে এই প্রবলেমেরও সলুশন করা যাবে। শুরুতেই আমরা item_profile.xml নামের একটা ফাইল রেডি করব। এটা নির্দেশ করবে ইউজার প্রোফাইলের লিস্টের প্রতিটা আইটেম। নিচে item_profile.xml এর কোডটুকু তুলে দেয়া হল।
<?xml version="1.0" encoding="utf-8"?> <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="16dp"> <!--Check last lines of every widget who contains `tools` attribute. Remove and add them to understand the changes--> <ImageView android:id="@+id/profileImage" android:layout_width="120dp" android:layout_height="140dp" android:scaleType="centerCrop" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" tools:ignore="ContentDescription" tools:src="@tools:sample/avatars" /> <TextView android:id="@+id/profileNameTextView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="16dp" android:textColor="@color/colorPrimaryDark" android:textSize="22sp" app:layout_constraintStart_toEndOf="@id/profileImage" app:layout_constraintTop_toTopOf="parent" tools:text="@tools:sample/full_names" /> <ImageView android:id="@+id/phoneIcon" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="8dp" android:src="@drawable/ic_phone_black_24dp" app:layout_constraintStart_toStartOf="@id/profileNameTextView" app:layout_constraintTop_toBottomOf="@id/profileNameTextView" tools:ignore="ContentDescription" /> <TextView android:id="@+id/phoneTextView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="8dp" android:textColor="@color/colorPrimary" app:layout_constraintBottom_toBottomOf="@id/phoneIcon" app:layout_constraintStart_toEndOf="@id/phoneIcon" app:layout_constraintTop_toTopOf="@id/phoneIcon" tools:text="@tools:sample/us_phones" /> <TextView android:id="@+id/shortBioTitle" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="8dp" android:text="@string/short_bio" android:textStyle="bold" app:layout_constraintStart_toStartOf="@id/phoneIcon" app:layout_constraintTop_toBottomOf="@id/phoneIcon" /> <TextView android:id="@+id/shortBioTextView" android:layout_width="0dp" android:layout_height="wrap_content" android:ellipsize="end" android:maxLines="3" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="@id/phoneIcon" app:layout_constraintTop_toBottomOf="@id/shortBioTitle" tools:text="@tools:sample/lorem/random" /> </android.support.constraint.ConstraintLayout>
শুরু থেকে ব্যাখ্যা করি। প্রোফাইলের ইমেজ দেখানোর জন্য ইমেজ সোর্স হিসাবে আমরা ইউজ করেছি tools:src=”@tools:sample/avatars”. আমরা ইতমধ্যে জেনে ফেলেছি যে tools namespace দিয়ে ইমেজের src প্রোপার্টি সেট করা হলে ইমেজটা প্রিভিউতে শো করে। সোর্স হিসাবে drawable এ থাকা যে কোনো ইমেজ সেট করা যায়। কিন্তু drawable থেকে কোনো ইমেজ, tools attribute দিয়ে সেট করলে RecyclerView এর প্রতি আইটেমেই একই ইমেজ শো করবে। আমরা চাই প্রতিটা আইটেমে ভিন্ন ভিন্ন ইমেজ দেখাতে। সেজন্য Android Studio আমাদের কাজকে সহজ করার জন্য কিছু প্রিডিফাইন sample data দিয়ে রেখেছে। যেহেতু আমরা ইউজারের প্রোফাইল পিকচার বা avatar দেখাতে চাচ্ছি তাই সোর্স হিসাবে ভ্যালু সেট করা হচ্ছে “@tools:sample/avatars”.
শুধু avatar না, sample directory-তে full_name, us_phone, date time ইত্যাদি sample data ইউজ করা যাবে tools attribute এর ভ্যালু হিসাবে। item_food এর বাকি widget-গুলোর জন্য sampledata এর ডিফল্ট ভ্যালু থেকে কিছু ভ্যালু ব্যবহার করা হয়েছে। নিচে available sample data এর লিস্ট দেয়া হল।
উপরে দেয়া XML code এর সাথে এই টেবিলের স্যাম্পল ডেটা মিলিয়ে নিলে আশা করি বুঝতে কোনো সমস্যা হবে না। আর সমস্যা হলে কমেন্ট অপশন তো রয়েছেই!
ডায়নামিক্যাল্যি sample data সেট করার কাজ করা হয়ে গেছে। আগের প্রবলেমের মত RecyclerView তে tools:listitem প্রোপার্টিতে item_profile লেআউটটা সেট করলেই ডায়নামিক ডেটা সহ কাঙ্ক্ষিত RecyclerView এর প্রিভিউ পাওয়া যাবে।
Problem 4
এখন চাই একটা RecyclerView-তে book list শো করবে। আগের প্রবলেমে যেমন default sample data দিয়ে কাজ করেছিলাম এখনও চাইলে সেরকম কিছু একটা করা যায়। কিন্তু প্রিভিউটা আরেকটু স্মার্ট করার জন্য আমরা চাই সত্যিকারের কিছু বইয়ের নাম, বইয়ের দাম ইত্যাদি লিস্টে ডায়নামিক্যাল্যি শো করবে।
Solution
আগের মত এখানেও RecyclerView-তে listitem property যুক্ত করতে হবে। সেখানে সেট করে দিব item_book নামের layout. item_book.xml এর বিভিন্ন ডেটাগুলো আমাদের সেট করা কাস্টম ডেটা হবে। তার মানে কোথাও আমাদের নিজস্ব ডেটাগুলো স্টোর করে রাখতে হবে। এরপর সেই ডেটাগুলোকে item_book এর widget-গুলোর সাথে কানেক্ট করিয়ে দিতে হবে।
Custom sample data স্টোর করার জন্য একটা নির্দিষ্ট ডিরেক্টরিতে ডেটাগুলো রাখতে হবে। এজন্য Menu bar থেকে সিলেক্ট করতে হবে File > New > Sample Data directory. এই অপশন সিলেক্ট করলে প্রোজেক্টে sampledata নামের একটা ডিরেক্টরি তৈরি হবে। একটা অ্যান্ড্রয়েড প্রজেক্টে sampledata নামের সর্বোচ্চ একটাই ডিরেক্টরি থাকতে পারে। তাই একবার এই ডিরেক্টরি তৈরি হয়ে গেলে পরবর্তীতে File > New > অপশনে গেলে Sample Data directory অপশন পাওয়া যাবে না। Android Studio এই অপশনকে হাইড করে দিবে।
উপরের ছবি দেখে বুঝা যাচ্ছে যে, উদাহরণ দেয়ার জন্য শুধু বইয়ের নাম ও বইয়ের প্রাইস ডায়নামিক্যাল্যি সেট করা হয়েছে। বাকিগুলো ইচ্ছা করেই সেট করি নাই। আপনারা চাইলে প্র্যাক্টিস পারপাসে সেট করে নিতে পারেন। যেহেতু বইয়ের নাম আর প্রাইস সেট করতে চাচ্ছি তাই sampledata ডিরেক্টরির ভিতরে book_names ও book_prices নামের দুইটি ফাইল create করি। ফাইলের কোনো extension না দিলেও চলবে।
book_names ফাইলে আমি ৫ টি বইয়ের নাম plain text format এ লিখে দিয়েছি।
The Alchemist Time Management 2 States A Walk to Remember The Notebook
book_prices ফাইলে দিয়ে রেখেছি উপরের বইগুলোর dummy price.
Price 150 BDT Price 250 BDT Price 380 BDT Price 190 BDT Price 80 BDT
ডেটাগুলো ৫টাই হতে হবে এমন না। ইচ্ছা মত দিতে পারি। এই ডেটাগুলোকে সিরিয়াল্যি রিসাইক্লারভিউতে দেখানো হবে।
এবার এই ডেটাগুলো item_book.xml এর widget-গুলোর সাথে কানেক্ট করতে হবে। পুরো xml না দিয়ে শুধু book name আর book price এর সাথে সম্পর্কিত TextView দুটি নিচে দেয়া হল।
<TextView android:id="@+id/bookNameTextView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="16dp" android:textColor="@color/colorPrimaryDark" android:textSize="22sp" app:layout_constraintStart_toEndOf="@id/bookImage" app:layout_constraintTop_toTopOf="parent" tools:text="@sample/book_names" /> <TextView android:id="@+id/priceTextView" android:layout_width="wrap_content" android:layout_height="wrap_content" app:layout_constraintStart_toStartOf="@id/bookNameTextView" app:layout_constraintTop_toBottomOf="@id/bookNameTextView" tools:text="@sample/book_prices" />
উপরের দুটি TextView এর tools:text এর ভ্যালুগুলো দেখলেই কী করা হয়েছে পরিষ্কার হয়ে যাবার কথা। @sample/ এর পরে জাস্ট আমাদের ক্রিয়েট করা ফাইলের নামটা দিয়ে দিয়েছি। রিসাইক্লারভিউতে যখন বারবার এই TextView-কে রিসাইকেল করা হবে তখন সংশ্লিষ্ট ফাইল থেকে ডেটা নিয়ে এসে বসিয়ে দিবে। এখন হয়ত বুঝতে অসুবিধা হচ্ছে। পোস্টের শেষে গিটহাবের লিংক দেয়া আছে। প্রোজেক্টটা ক্লোন করে নামিয়ে Android Studio দিয়ে XML ফাইলগুলো ওপেন করে প্রিভিউ দেখলে আর কোডগুলো নিয়ে কিছু কাঁটাছেঁড়া করলে আশা করি বুঝতে সুবিধা হবে।
Problem 5
কিছু স্টুডেন্টদের ইনফো নিয়ে একটা লিস্টের প্রিভিউ দেখাতে হবে। যেখানে স্টুডেন্টের সব ইনফো ডায়নামিক্যাল্যি সেট হবে। তবে শর্ত হচ্ছে আগের প্রবলেমের মত স্টুডেন্টের নাম, রোল, বয়স, কলেজের জন্য আলাদা আলাদা ফাইল ক্রিয়েট করে সেখান থেকে ডেটা নেয়া যাবে না। একটা JSON array থেকে ডেটা নিয়ে এই লিস্টে সেট করে প্রিভিউ জেনারেট করতে হবে।
Student ডেটার JSON array-টি নিচে দেয়া হলো।
{ "data": [ { "name": "John Doe", "roll": 101, "school": "Saint Joseph Higher Secondary School", "age": 10 }, { "name": "Maria Teri", "roll": 102, "school": "Dhaka International School", "age": 8 }, { "name": "Ted Bil", "roll": 114, "school": "Model Academy School and College", "age": 15 }, { "name": "Johny Dev", "roll": 123, "school": "Cambrian International College", "age": 9 } ] }
Solution
এই প্রবলেমের জন্য সবগুলো কাজই প্রায় আগের মত। item_student.xml নামের একটা ফাইল ক্রিয়েট করে সেটাকে RecyclerView এর ভিতর tools:listitem দিয়ে Recycle করতে হবে। আগের প্রবলেমের মত ডেটার আলাদা আলদা ফাইল না খুলে students.json নামের একটা ফাইল তৈরি করেছি sampledata ডিরেক্টরিতে। সেখানে প্রবলেমে দেয়া JSON object টি paste করেছি। এখন students.json এর সাথে item_student.xml এর widget-গুলোর linkup করব।
নিচে item_student.xml এর ২টি widget এর কোড দেয়া হচ্ছে। এই দুটির tools:text properties-গুলো লক্ষ্য করলে বুঝে নেয়া যাবে বাকিগুলো কিভাবে কাজ করবে।
<TextView android:id="@+id/studentNameTextView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textColor="@color/colorPrimaryDark" android:textSize="22sp" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" tools:text="@sample/students.json/data/name" /> <TextView android:id="@+id/rollTextView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="8dp" android:textColor="@color/colorPrimary" app:layout_constraintBottom_toBottomOf="@id/phoneIcon" app:layout_constraintStart_toEndOf="@id/rollTitle" app:layout_constraintTop_toTopOf="@id/rollTitle" tools:text="@sample/students.json/data/roll" />
নামের ক্ষেত্রে, “@sample/students.json/data/name” দেয়া হয়েছে। এর মানে হচ্ছে sample ডিরেক্টরির ভিতর students.json ফাইলের data নামের JSON Array এর ভিতর যেই অবজেক্টগুলো আছে সেগুলোর মধ্যে থাকা name key এর ভ্যালুগুলো এই TextView এর মধ্যে সেট হবে। এই item_student যখন RecyclerView এর ভিতর tools:listitem এর মাধ্যমে প্রিভিউতে বারবার recycle হবে তখন data নামের অ্যারেতে যেই জ্যাসন অবজেক্টগুলো থাকবে সেগুলোর name-গুলোকে সিরিয়্যাল্যি নিয়ে এসে প্রতিটি আইটেমে বসাতে থাকবে।
sampledata directory তে ডেটা রেখে সেগুলোকে নিয়ে কাজ করার ক্ষেত্রে একটা কথা কমন। তা হচ্ছে যেই কয়টি ডেটা পাওয়া যাবে সেগুলোই একটার পর একটা লিস্টে শো করবে। উপরে দেয়া জ্যাসন অ্যারেতে যদি ২টি মাত্র স্টুডেন্টের ইনফো থাকে তাহলে প্রিভিউ লিস্টে এই দুইজনের ডেটাই বারবার শো করাবে। যদি একটা জ্যাসন অবজেক্ট থাকে তাহলে সবগুলো আইটেমে একজন স্টুডেন্টের ডেটাই দেখাবে। তাই Problem 4 বা Problem 5 এর জন্য গুণে গুণে ডেটা এন্ট্রি দেয়ার প্রয়োজন নাই। সুবিধা মত ৩-৪ টা ডেটা দিলেই যথেষ্ট। ওগুলো দিয়েই লিস্ট ফুলফিল হবে।
More usages of Android “tools” attribute
উপরের প্রবলেমগুলো সলভ করতে গিয়ে tools attribute এর বেশ কিছু ব্যবহার দেখেছি। এখন আরো কিছু অ্যাট্রিবিউট দেখব।
tools:context
কোনো একটা layout ফাইলের root element এ tools:context এর ভ্যালু হিসাবে associated Activity এর নাম লিখে দেয়া যায়। তাহলে এই লেআউটটা কোন অ্যাক্টিভিটির মধ্যে ইউজ হবে সে তথ্য Android Studio এর কাছে থাকে। ফলে ঐ অ্যাক্টিভিটির থিম অনুযায়ী এই লেআউটের প্রিভিউ দেখাতে পারে। পাশাপাশি কোনো বাটন বা অন্যান্য ভিউয়ের মধ্যে onClick attribute এ মেথডের নাম লিখে দিলে Alt+Enter ক্লিক করে অটো onClick মেথড ঐ অ্যাক্টিভিটিতে ক্রিয়েট করা যায়।
লেআউটের রুটে tools:context=”.MainActivity” লিখার ফলে উপরের কুইকফিক্সে MainActivity-তে মেথড ক্রিয়েটের সাজেশন এসেছে।
tools:layout
এই attribute টি শুধুমাত্র <fragment> tag এর ভিতর ইউজ করা যাবে। আমরা <fragment> tag কোনো লেআউটে অ্যাড করলে প্রিভিউতে সাধারণত কালো বা অ্যাশ কালারের একটা স্ক্রিন শো করে। আমরা এই অ্যাট্রিবিউটের মাধ্যমে প্রিভিতে দেখাতে পারি যে এখানে কোন লেআউটটা শো করবে। Example: tools:layout=”@layout/hom_fragment”
tools:ignore
Android Studio অনেক সময়েই অনেক warning message দিয়ে থাকে। কোনো কারণে কোনো ওয়ার্নিং আমলে নেয়া দরকার মনে না করলে ওয়ার্নিংটাকে আমরা এই অ্যাট্রিবিউট দিয়ে বন্ধ করতে পারি। যেমন কোনো ImageView থাকলে Android Studio সেখানে content description দেয়ার জন্য ওয়ার্নিং দেয়। যেই ডেসক্রিপশনটা আপনার অ্যাপের accessibility এর জন্য ভাল। কিন্তু সেটাকে গুরুত্বপূর্ণ মনে না করলে ImageView-তে tools:ignore=”ContentDescription” অ্যাড করে দিতে পারেন। তাহলে কনটেন্ট ডেস্ক্রিপশন না থাকায় ওয়ার্নিং দেখাবে না। ওয়ার্নিং দেখালেও আসলে কোনো সমস্যা নাই। পারসোনাল্যি আমার কাছে ওয়ার্নিং দেখতে ভাল লাগে না। তাই আমি সর্বোচ্চ চেষ্টা করি কোডকে warningless আর more readable রাখতে।
tools:targetApi
Android এর কোনো নির্দিষ্ট API version এর জন্য যখন কোনো প্রিভিউ দেখতে চাইব বা কোনো নির্দিষ্ট ভার্সনে অ্যাপের কোনো widget দেখতে কেমন হবে সেটা জানার জন্য targetApi অ্যাট্রিভিউট ইউজ করা হয়।
<Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:elevation="4dp" tools:targetApi="lollipop"/>
elevation attribute-টি Android API 21 এ যুক্ত করা হয়েছে। তাই যখন এটি xml এ অ্যাড করা হবে তখন আপনার প্রোজেক্টের minimum SDK version 21 এর কম হলে এখানে একটা ওয়ার্নিং দেখাবে। তো আপনি যদি জানেন এই লেআউট শুধু 21 এ বা Lollipop এ কাজ করবে তখন ওয়ার্নিংটা অফ করতে পারেন targetApi সেট করার মাধ্যমে।
tools:listheader ও tools:listfooter
ListView এর header ও footer এর প্রিভিউ দেখার জন্য এই দুটি অ্যাট্রিবিউট ব্যবহার করা হয়। ভ্যালু হিসাবে যেই লেআউটকে হেডার বা ফুটার হিসাবে দেখতে চাই সেটাকে সেট করে দিতে হবে। একটা আর্টিকেলে পেলাম RecyclerView এর ক্ষেত্রেও এটা কাজ করে। কিন্তু আমি ট্রাই করে আউটপুট পাই নাই।
Learn More about Android tools attribute
- Tools Attribute Reference (Official doc)
- Android Tools Attributes — Hidden Gems of Android Studio
- Android Tools attributes: listItem & sample data rocks!
- Android Tools Attributes (GitHub Gist)
উপরে প্রবলেমগুলোর সলিউশন সবগুলো পাওয়া যাবে আমার গিটহাব রিপোজিটরিতে। প্রোজেক্টটা ক্লোন করে লেআউট ফাইলগুলো Android Studio দিয়ে ওপেন করে দেখতে হবে। প্রোজেক্ট রান করলে আসলে কিছু বুঝা যাবে না। যেহেতু পোস্টটা প্রিভিউ দেখা বিষয়ক, তাই এটার XML-গুলো প্রিভিউতেই দেখলে বুঝা যাবে।
Conclusion
অনেকেই ৩৩০০+ শব্দের এই লেখা পড়ে বিরক্ত হয়ে হয়ত গাল দিবেন আমাকে। বলবেন শুধু প্রিভিউ দেখার জন্য এত কাহিনী করার কী দরকার? ক্লায়েন্ট তো এটা চায় নাই। এটা ছাড়াই তো অ্যাপ কাজ করে। অ্যাসাইন করা কাজ করেই তো কূল পাই না এইসব করার টাইম কই? আপনার কথা একেবারে ভুল না। অনেকাংশেই ঠিক। কিন্তু সব কিছুর একটা সৌন্দর্য আছে। যিনি মাটি কাটেন তিনিও দেখবেন সুন্দর করে কাজটা করার চেষ্টা করেন। কোড করাটা একটা শিল্প। একজন মানুষের কোডের চেহারা দেখলে খানিকটা আন্দাজ করা যায় তার সৌন্দর্য বোধ আর ব্যক্তিত্ব। এটা একান্তই আমার মত। ভুল হতে পারে।
শুধু সৌন্দর্যই নয়, আপনার ডেভেলপ করা কোডবেজ নিয়ে যখন নতুন কোনো ডেভেলপার কাজ করা শুরু করবেন তখন আপনার এই কয়েক লাইনের tools attribute অনেক বড় আসানের কারণ হতে পারে। আতিপাতি করে খুঁজে খুঁজে আপনার visibility GONE করা ভিউগুলো খুঁজে বের করতে দিন পার হবে না তার। একটা XML open করেই সে বুঝতে পারবে যেই লেআউট সে খুঁজছে এটা সেই লেআউট কিনা। UI তে স্পেসিফিক চেঞ্জ আনা তার জন্য এবং আপনি অনেক দিন পর আবার একই প্রোজেক্টে কাজ শুরু করলে আপনার জন্যও সুবিধা হবে।
সে দিন বেশ পুরানো একটা প্রোজেক্টের কোডবেজ নিয়ে কাজ করছিলাম। প্রোডাক্ট লিস্টের কোনো আইটেমে ক্লিক করলে পরের অ্যাক্টিভিটিতে গিয়ে ঐ প্রোডাক্টের ডিটেইলস দেখানো হবে। প্রোডাক্ট ডিটেইলস আসবে নেটওয়ার্ক থেকে। তো দেখা গেল প্রতিবার যে কোনো আইটেমে ক্লিক করলে ডিটেইলস পেজে গিয়ে একটা নির্দিষ্ট প্রোডাক্টের নাম আর প্রাইস শো করে। এরপর নেটওয়ার্ক থেকে ডেটা আসলে আগের শো করা ডেটা রিপ্লেস হয়! এই অ্যাপ প্রোডাকশনে চলছে!!! কী সাংঘাতিক ব্যাপার না? ঘটনা হচ্ছে, এই tools attribute ইউজ না করে android namespace দিয়ে hardcoded string দিয়ে একটা প্রোডাক্টের নাম বসিয়ে দেয়া হয়েছিল। তাই নেটওয়ার্ক কলের আগে এই হার্ডকোডেড টেক্সই শো হচ্ছিল। এরকম ভুল অনেকেই আমরা করে থাকি। রানটাইমে চেঞ্জ হবে এমন প্রতিটা ফিল্ডেই tools namespace দিয়ে dummy data সেট করা উচিত।
আমি অ্যান্ড্রয়েড টুকটাক করে শিখছি। কোথাও কোনো অস্পষ্টতা বা তথ্যগত ভুল চোখে পড়লে অনুগ্রহ করে কমেন্ট করবেন। আমি ইনশাআল্লাহ ঠিক করে দিব। এছাড়াও পোস্ট সম্পর্কে বা ব্লগ সম্পর্কে আপনার যে কোনো গঠনমূলক আলোচনা, সমালোচনা, মন্তব্য একান্ত কাম্য। ধন্যবাদ এতটা সময় দিয়ে পোস্টটি পড়ার জন্য।
Very Helpful !
Thank you for your feedback
very resourceful tutorial
Thank you for your feedback
thanks a lot for your hard work for no interest,i hope you will provide more post on advance topic of android in future which you have learnt in professional level and which is not available in internet or youtube
Very informative