Post updated on 23rd January, 2018 at 10:00 pm
একটা সিনারিও চিন্তা করা যাক।
যে কোনো বিষয়ের উপর আর্টিকেল পড়ার জন্য অ্যাপের একটা Activity চালু হবে। এখানে আমাদেরকে আর্টিকেলের একটা লিস্ট দেখাতে হবে ছবি সহ। যে কোনো আর্টিকেলে ক্লিক করলে আমরা সাধারনত আরেকটা activity বা fragment এ নিয়ে সেটাকে লোড করি। কিন্তু যদি কোনো কারণে আমরা চাই অন্য কোনো অ্যাক্টিভিটি বা ফ্রেগমেন্টে আর্টিকেল শো করব না। যেই অ্যাক্টিভিটিতে লিস্ট দেখাচ্ছে সেখানেই আর্টিকেল দেখাবো। তাহলে কী করা যায়?
এক্ষেত্রে একটা উপায় হতে পারে Expandable Layout. অর্থাৎ প্রতিটা আর্টিকেলের লিস্ট আইটেমকে একটা লেয়াউট চিন্তা করলে কাজটা হবে যে কোনো আইটেমে ক্লিক করলে লেয়াউটটা যেন expand হয়। আর্টিকেলটা পড়া শেষ করে পরের আইটেমে ক্লিক করলে এবার ঐ আইটেমটা এক্সপান্ড হয়ে পুরো আর্টিকেলটা পড়া যাবে। আর্টিকেল পড়ার জন্য আরেক পেজে পাঠানো, পড়া শেষে ব্যাকে আসা। আবার ক্লিক করে আরেক পেজে পাঠানো এভাবে করার চেয়ে একই ভিউতে যদি আর্টিকেলটা দেখানো যায় UX টা may be better হয়। এই ফিচারটি আমি খুব রিসেন্টলিই ইমপ্লিমেন্ট করেছি আমাদের টিমের ডেভেলপ করা সর্বশেষ বিসিএস প্রস্তুতির অ্যাপ Editorial Word এ।
এই কাজটা করার জন্য গুগলে সার্চ করলে অনেকগুলো উপায় পাওয়া যায়। এটার জন্য অনেকগুলো লাইব্রেরিও আছে। আমি এজন্য ExpandableLayout নামের একটা Open Source Library ব্যবহার করেছি।
লাইব্রেরিটা ইন্সটলের জন্য gradle এ গিয়ে এটা এড করে নিনঃ
dependencies { [...] compile 'net.cachapa.expandablelayout:expandablelayout:[latest_version]' }
বর্তমানে সর্বশেষ ভার্সন হচ্ছে 2.8. [latest_version] রিপ্লেস হবে [2.8] দ্বারা। এরপর যথারীতি Sync করে নিন।
আসলে সব সময় যে এই লাইব্রেরি ব্যবহারের জন্য লিস্টভিউ বা এই ধরনের কিছু ব্যবহার করতে হবে এমন না। আপনি চাইলে ভিউতে একটা বাটন রেখে সেই বাটনের ক্লিকে কোনো একটা লেয়াউট এক্সপান্ড করতে পারবেন। একটা বাটনে ক্লিক করে নিচে একটা TextView দেখানোর কাজটা একদম সোজা। তাই এই উদাহরণ উল্লেখ না করে একটু কমপ্লেক্স কাজ করি।
আমাদের উদ্দেশ্য হচ্ছে সার্ভারে থাকা একটা JSON Array অ্যাপে নিয়ে আসা। এই অ্যারেতে এক বা একাধিক আর্টিকেলের ডেটা JSON Object হিসাবে থাকবে। প্রতিটা আর্টিকেল অবজেক্টে থাকবে আর্টিকেলের হেডলাইন, লেখকের নাম, ইমেজ লিংক, তারিখ ও সম্পূর্ণ আর্টিকেল। নিচে JSON format টা দেখানো হলোঃ
[ { "headline": "Android Development: Getting Started", "image_url": "http://s01.shiftdelete.net/img/general_b/14-06/21/android-1.jpg", "author_name": "Hasan Abdullah", "published_date": "Dec 03, 2016", "article": "This is an article" }, { "headline": "WHY PYTHON FOR BIG DATA?", "image_url": "http://www.pixelstech.net/article/images/Python.png", "author_name": "Khalid Saifullah", "published_date": "Mar 26, 2010", "article": "This is another article" } ]
এই ডেটাগুলো সহজে access ও parse করার জন্য একটা Model Class বানিয়েছি। মডেল ক্লাসটা বেশ বড়। আশার কথা হচ্ছে কষ্ট করে এই বোরিং মডেল ক্লাসটা হাতে লিখতে হয় নাই। Android Studio এর POJO Generator এর মাধ্যমে ২-৩ ক্লিকেই মডেল ক্লাস বানিয়ে নেয়া যায়।
সার্ভারের ডেটা নিয়ে আসার জন্য নেটওয়ার্ক লাইব্রেরি হিসাবে ব্যবহার করেছি Retrofit. রেট্রোফিটের সাহায্যে কিভাবে সার্ভারের সাথে লেনদেন করা যায় সে বিষয়ে আমার লেখা বিস্তারিত একটা ব্লগ পোস্ট রয়েছে। Retrofit এর পোস্টটি পড়া যাবে এখান থেকে।
ডেটা স্টোর করার জন্য নিজস্ব সার্ভার ব্যবহার করি নাই। গিটহাবে একটা JSON ফাইল রেখে দিয়েছি। GET request এর মাধ্যমে ডেটা নিয়ে আসা হচ্ছে।
ডেটাগুলো উপস্থাপনের জন্য আমরা ব্যবহার করব CardView ও RecyclerView.
একটা কার্ডের ভিতরে দুইটা অংশ থাকবে। একটা অংশ থাকবে যেটা সব সময় visible থাকবে। এই ক্ষেত্রে ভিজিবল অংশ হিসাবে রাখব আর্টিকেলের ইমেজ, আর্টিকেলের হেডলাইন, লেখকের নাম ও তারিখ। এই ডেটাগুলো সব সময় ভিউতে শো করে থাকবে। এই ভিজিবল অংশে ক্লিক করলে একটা TextView expand হয়ে মূল আর্টিকেল শো করবে। আশা করি আপনি CardView, RecyclerView, RecyclerView Adapter ইত্যাদি প্রাসঙ্গিক বিষয় আগে থেকেই জানেন। না জেনে থাকলে পড়তে পারেন CardView ও RecyclerView এর উপর আমার ব্লগের এই লেখাটি। লেখার শেষে পুরো প্রোজেক্টের লিংক দেয়া আছে। নামিয়ে দেখে নিলেই বুঝে যাবেন।
তো যা বলছিলাম… একটা কার্ডের মধ্যে দুইটা পার্ট। ভিজিবল অংশ নিয়ে তেমন কিছু করার নাই। যেই অংশটাকে আমরা expand করতে চাই সেই অংশটুকু রাখতে হবে এই লাইব্রেরির একটা custom widget এর ভিতরে।
<net.cachapa.expandablelayout.ExpandableLayout android:id="@+id/expandable_layout" android:layout_width="match_parent" android:layout_height="wrap_content" app:el_duration="1000" app:el_expanded="true" app:el_parallax="0.5" android:layout_below="@+id/recyclerHeader"> // our expanded part [article TextView or anything] </net.cachapa.expandablelayout.ExpandableLayout>
এরপর RecylerView এর adapter class এর onBindViewHolder() মেথডের ভিতর কল করতে হবে এই মেথড দুটিঃ
@Override public void onBindViewHolder(CustomViewHolder holder, int position) { //more code holder.relativeLayoutHead.setSelected(false); holder.expandableLayout.collapse(false); }
এরপর onClick() মেথডে বলে দিতে হবে ক্লিক করলে কখন expand করবে আর কখন collapse করবে।
@Override public void onClick(View v) { CustomViewHolder holder = (CustomViewHolder) recyclerView.findViewHolderForAdapterPosition(selectedItem); if (holder != null) { holder.relativeLayoutHead.setSelected(false); holder.expandableLayout.collapse(); } if (position == selectedItem) { selectedItem = UNSELECTED; } else { relativeLayoutHead.setSelected(true); expandableLayout.expand(); selectedItem = position; } }
উপরের এই কোডটা বেশ বড়। এই কাজটা এত ঘুরিয়ে পেচিয়ে না করে এক লাইনের কোড করেও করা যায়ঃ
@Override public void onClick(View v) { holder.expandableLayout.toggle(); }
toggle() মেথড কল করা হয়েছে। অর্থাৎ layout-টা collapsed থাকলে toggle() কল করা হয়ে expand করে দিবে। আর expanded থাকা অবস্থায় toggle() call করা হলে collapsed করে দিবে।
এরকম আলাদা আলাদা কোড দেখা ব্যাপারটা বুঝা কঠিন। পুরো কোডটা দেখা যাবে এখান থেকে।
এই লাইব্রেরির আরো অনেক সুন্দর সুন্দর ফিচার রয়েছে। বিস্তারিত জানা যাবে লাইব্রেরিটির গিটহাব রিপোজিটরিতে।
অনেক কিছুই একটা তে কাভার করলেন, ধন্যবাদ।
আপনাকেও ধন্যবাদ 🙂