Android RecyclerView: คืออะไร เรียนรู้จากตัวอย่างง่ายๆ
RecyclerView คืออะไร Android?
บริษัท รีไซเคิลวิว เป็นวิดเจ็ตที่มีความยืดหยุ่นและล้ำหน้ากว่าของ GridView และ ListView เป็นคอนเทนเนอร์สำหรับแสดงชุดข้อมูลขนาดใหญ่ที่สามารถเลื่อนได้อย่างมีประสิทธิภาพโดยคงจำนวนการดูที่จำกัด คุณสามารถใช้วิดเจ็ต RecyclerView เมื่อคุณมีการรวบรวมข้อมูลซึ่งองค์ประกอบเปลี่ยนแปลงเมื่อรันไทม์ขึ้นอยู่กับเหตุการณ์เครือข่ายหรือการกระทำของผู้ใช้
เข้าชม
บริษัท Android แพลตฟอร์มใช้คลาส View และ ViewGroup เพื่อวาดรายการบนหน้าจอ คลาสเหล่านี้เป็นนามธรรมและขยายไปสู่การใช้งานที่แตกต่างกันเพื่อให้เหมาะกับกรณีการใช้งาน ตัวอย่างเช่น TextView มีวัตถุประสงค์ง่ายๆ ในการแสดงเนื้อหาข้อความบนหน้าจอ EditText ขยายจากคลาส View เดียวกันและเพิ่มฟังก์ชันการทำงานเพิ่มเติมเพื่อให้ผู้ใช้สามารถป้อนข้อมูลได้
เป็นไปได้ที่จะสร้างมุมมองที่กำหนดเองของเราเองเพื่อให้เกิดความยืดหยุ่นมากขึ้นเมื่อพัฒนาส่วนต่อประสานกับผู้ใช้ คลาส View จัดเตรียมวิธีการที่เราสามารถแทนที่เพื่อวาดบนหน้าจอได้ และวิธีการส่งผ่านพารามิเตอร์ เช่น ความกว้าง ความสูง และแอตทริบิวต์ที่เรากำหนดเองซึ่งเราต้องการเพิ่มลงใน View เพื่อให้มันทำงานตามที่เราต้องการ
ดูกลุ่ม
คลาส ViewGroup นั้นเป็น View ชนิดหนึ่ง แต่ต่างจากคลาส View ธรรมดาที่มีหน้าที่แค่แสดง ViewGroup ให้ความสามารถแก่เราในการรวมหลายมุมมองไว้ในมุมมองเดียว ซึ่งเราสามารถอ้างอิงโดยรวมได้ ในกรณีนี้ มุมมองที่สร้างขึ้นที่ระดับบนสุดที่เราเพิ่มมุมมองแบบง่ายอื่นๆ (เรายังสามารถเพิ่ม viewGroups ได้) เรียกว่า "พาเรนต์" และมุมมองที่เพิ่มเข้าไปข้างในคือ "ลูก"
เราสามารถสร้างภาพ View เป็น Array และ ViewGroup เป็น Array of Arrays เนื่องจาก Array of Arrays ก็คือ Array นั่นเอง เราจะเห็นว่า ViewGroup สามารถปฏิบัติได้เหมือน View ได้อย่างไร
var arr1 = [1,2,3] //imagine a simple View as an Array //we can imagine this as a NumberTextView which doesn't really exist //but we could imagine there's one that makes it easy to use numbers var arr2 = ["a","b","c"] // We can imagine this as another simple view var nestedArr = [arr1,arr2] //in our anology, we can now group views //together and the structure that would hold that would be what we call the ViewGroup
ViewGroup ยังช่วยให้เรากำหนดได้ว่าลูกๆ จะถูกจัดเรียงอย่างไรภายในมุมมอง เช่น วางในแนวตั้งหรือแนวนอน เราสามารถมีกฎที่แตกต่างกันสำหรับการโต้ตอบภายในมุมมองได้ ตัวอย่างเช่น TextViews ที่อยู่ติดกันควรมีระยะห่าง 12dp ในขณะที่ ImageViews ที่อยู่ติดกับ TextView ควรมีระยะห่าง 5dp
กรณีนี้จะเป็นเช่นนั้นหากเราพัฒนา ViewGroup ของเราเองตั้งแต่เริ่มต้น เพื่อให้การกำหนดค่าเหล่านี้ง่ายขึ้น Android จัดเตรียมคลาสที่เรียกว่า LayoutParams ซึ่งเราสามารถใช้เพื่ออินพุตการกำหนดค่าเหล่านี้
Android เอกสาร ให้พารามิเตอร์เริ่มต้นบางอย่างที่เราจะนำไปใช้เมื่อกำหนดค่า ViewGroup ของเราเอง พารามิเตอร์ทั่วไปบางอย่างได้แก่ ความกว้าง ความสูง และระยะขอบ โดยค่าเริ่มต้น การกำหนดค่าเหล่านี้จะมีโครงสร้าง android:layout_height สำหรับความสูง เช่น android:layout_width ในเรื่องนี้ เมื่อคุณสร้าง ViewGroup ของคุณ คุณสามารถสร้าง LayoutParams เพิ่มเติมตามลักษณะที่คุณต้องการให้ ViewGroup ของคุณทำงาน
Android มาพร้อมกับ Views และ ViewGroups เริ่มต้นที่เราสามารถใช้เพื่อทำงานทั่วไปหลายอย่างที่เราต้องการ ตัวอย่างหนึ่งที่เราได้กล่าวถึงคือ TextView นั่นเป็นมุมมองธรรมดาที่มาพร้อมกับลักษณะที่กำหนดค่าได้ เช่น ความสูง ความกว้าง ขนาดข้อความ และอื่นๆ เรามี ImageView สำหรับแสดงรูปภาพและ EditText ดังที่เราได้กล่าวไปแล้ว และอื่นๆ อีกมากมาย Android ยังมี ViewGroups แบบกำหนดเองที่เราสามารถเพิ่ม Views ของเราและรับลักษณะการทำงานที่คาดหวังได้
เลย์เอาต์เชิงเส้น
LinearLayout ช่วยให้เราเพิ่มรายการมุมมองลงไปได้ LinearLayout คือแอตทริบิวต์การวางแนวที่กำหนดว่าจะจัดวางอย่างไรบนหน้าจอ นอกจากนี้ยังมี LinearLayout.LayoutParams ที่กำหนดกฎสำหรับมุมมองภายใน เช่น แอตทริบิวต์ android:center_horizontal จะทำให้มุมมองอยู่ตรงกลางตามแกนแนวนอน ในขณะที่ `android:center_vertical จะทำให้เนื้อหาของมุมมองอยู่ตรงกลางตามแกนแนวตั้ง
ต่อไปนี้เป็นภาพบางส่วนเพื่อให้เข้าใจถึงการจัดกึ่งกลาง เราจะใช้สิ่งนี้เป็น TextView แบบธรรมดาภายในพื้นที่ 200px คูณ 200px โดยแอตทริบิวต์ที่อยู่ตรงกลางจะทำให้มีลักษณะดังนี้
แอนดรอยด์:กึ่งกลางแนวนอน
แอนดรอยด์:ศูนย์กลางแนวตั้ง
แอนดรอยด์:ศูนย์กลาง
ส่วนประกอบหลักของ RecyclerView
ต่อไปนี้เป็นส่วนประกอบสำคัญของ RecyclerView:
RecyclerView.อะแดปเตอร์
รากฐาน #1 – รูปแบบอะแดปเตอร์
อะแดปเตอร์คืออุปกรณ์ที่แปลงคุณลักษณะของระบบหรืออุปกรณ์ให้เป็นคุณลักษณะของอุปกรณ์หรือระบบที่ไม่สามารถเข้ากันได้ บางส่วนปรับเปลี่ยนคุณลักษณะของสัญญาณหรือพลังงาน ในขณะที่บางส่วนเพียงแค่ปรับเปลี่ยนรูปร่างทางกายภาพของขั้วต่อหนึ่งให้เป็นอีกอันหนึ่ง
ตัวอย่างง่ายๆ ที่เราพบในชีวิตจริงเพื่ออธิบายอะแดปเตอร์คือเมื่อเราต้องเชื่อมต่ออุปกรณ์เข้าด้วยกัน แต่มีพอร์ตการเชื่อมต่อที่ไม่ตรงกัน กรณีนี้อาจเกิดขึ้นได้เมื่อคุณไปเยือนประเทศอื่นซึ่งใช้ปลั๊กไฟต่างกัน หากคุณพกที่ชาร์จโทรศัพท์หรือแล็ปท็อปไปด้วย คุณจะไม่สามารถเชื่อมต่อกับปลั๊กไฟได้ อย่างไรก็ตาม คุณจะไม่ยอมแพ้ แต่เพียงได้รับอะแดปเตอร์ที่อยู่ระหว่างเต้ารับไฟฟ้าและอุปกรณ์ชาร์จของคุณ เพื่อให้สามารถชาร์จได้
กรณีนี้ในการเขียนโปรแกรมเมื่อเราต้องการเชื่อมต่อโครงสร้างข้อมูลทั้งสองเข้าด้วยกันเพื่อให้งานสำเร็จ แต่พอร์ตเริ่มต้นไม่มีวิธีสื่อสารระหว่างกัน
เราจะใช้ตัวอย่างง่ายๆ ของอุปกรณ์และที่ชาร์จ เราจะมีที่ชาร์จสองกรณี เป็นแบบอเมริกันและแบบอังกฤษ
class AmericanCharger() { var chargingPower = 10 } class BritishCharger(){ var charginPower = 5 }
จากนั้นเราจะสร้างอุปกรณ์สองเครื่อง
class AmericanDevice() class BritishDevice()
ตัวอย่างเช่น เราสามารถสร้างอินสแตนซ์ของอุปกรณ์สำหรับเล่นตามได้
var myAmericanPhone = new AmericanDevice() var myBritishPhone = new BritishDevice()
จากนั้นเราจะแนะนำแนวคิดในการชาร์จอุปกรณ์ทั้งสองเครื่องโดยเพิ่มวิธีการในอุปกรณ์ที่เรียกว่า charge()
วิธีการนี้เป็นอินพุตของเครื่องชาร์จตามลำดับและทำการชาร์จตามนั้น
sealed trait Device class AmericanDevice : Device{ fun charge(charger:AmericanCharger){ //Do some American charging } } class BritishDevice: Device{ fun charge(charger:BritishCharger){ //Do some British charging } }
ในกรณีนี้ จากการเปรียบเทียบของเรา เราจะต้องใช้ BritishCharger ด้วยเหตุผลใดก็ตาม เมื่อใช้ AmericanDevice หรือในทางกลับกัน
ในโลกของการเขียนโปรแกรม โดยปกติแล้วจะเกิดขึ้นเมื่อผสมไลบรารีเข้าด้วยกันซึ่งมีฟังก์ชันการทำงานเดียวกัน (ในบริบทของเรา ฟังก์ชันที่ใช้ร่วมกันของเรากำลังชาร์จอยู่) เราจะต้องหาวิธีในการเปิดใช้งานสิ่งนี้
หากเราลองเปรียบเทียบดู เราจะต้องไปที่ร้านขายอุปกรณ์อิเล็กทรอนิกส์และซื้ออะแดปเตอร์ที่จะช่วยให้ชาร์จอุปกรณ์ AmericanDevices ได้โดยใช้ BritishChargers จากมุมมองของการเขียนโปรแกรม เราจะเป็นผู้ผลิตอะแดปเตอร์เอง
เราจะสร้างอะแดปเตอร์สำหรับอันหนึ่งซึ่งตรงกับรูปแบบที่เราต้องการสำหรับการสร้างอันอื่น เราจะนำไปใช้เป็นคลาสดังนี้ ไม่จำเป็นต้องเป็นคลาสและอาจเป็นฟังก์ชันที่เน้นว่ารูปแบบอะแดปเตอร์โดยทั่วไปทำอะไร เราจะใช้คลาสตามที่มันตรงกับการใช้งานส่วนใหญ่ Android.
class AmericanToBritishChargerAdapter(theAmericanCharger:AmericanCharger){ fun returnNewCharger(): BritishCharger{ //convert the American charger to a BritishCharger //we would change the American charging functionality //to British charging functionality to make sure the //adapter doesn't destroy the device. The adapter could //, for example, control the power output by dividing by 2 //our adapter could encompass this functionality in here var charingPower:Int = charger.chargingPower / 2 var newBritishCharger = new BritishCharger() newBritishCharger.chargingPower = theAmericanCharger.chargingPower/2 return newBritishCharger } }
ในโลกของการเขียนโปรแกรม ความแตกต่างของซ็อกเก็ตนั้นคล้ายคลึงกับความแตกต่างในวิธีการด้านในที่ใช้ในการชาร์จ เครื่องชาร์จที่มีวิธีการต่างกันจะทำให้ไม่สามารถใช้เครื่องชาร์จได้
var myBritishDevice = new BritishDevice() var americanChargerIFound = new AmericanCharger()
การพยายามเรียกใช้เมธอด charge() ใน myBritishDevice ด้วย americanChargerIFound จะไม่ทำงานเนื่องจาก AmericanDevice ยอมรับเฉพาะ AmericanCharger เท่านั้น
ดังนั้นจึงเป็นไปไม่ได้ที่จะทำเช่นนี้
var myBritishDevice = new BritishDevice() var americanChargerIFound = new AmericanCharger() myBritishDevice.charge(americanChargerIFound)
ในสถานการณ์สมมตินี้อะแดปเตอร์ที่เราสร้างขึ้น
AmericanToBritishChargerAdapter ตอนนี้มีประโยชน์แล้ว เราสามารถใช้เมธอด returnNewCharger() เพื่อสร้าง BritishCharger ใหม่ ซึ่งเราสามารถใช้เพื่อชาร์จได้ สิ่งที่เราต้องทำก็แค่สร้างอินสแตนซ์ของอะแดปเตอร์ของเราแล้วป้อนด้วย AmericanCharger ที่เรามี จากนั้นมันจะสร้าง BritishCharger ที่เราสามารถใช้ได้
var myBritishDevice = new BritishDevice() var americanChargerIFound = new AmericanCharger() //We create the adapter and feed it the americanCharger var myAdapter = AmericanToBritishChargerAdapter(theAmericanCharger) //calling returnNewCharger from myAdapter would return a BritishCharger var britishChargerFromAdapter = myAdapter.returnNewCharger() //and once we have the britishCharger we can now use it myBritishDevice.charge(britishChargerFromAdapter)
RecyclerView.LayoutManager
เมื่อจัดการกับ ViewGroup เราจะมี Views อยู่ข้างใน LayoutManager มีหน้าที่อธิบายว่า Views ถูกจัดวางภายในอย่างไร
เพื่อวัตถุประสงค์ในการเปรียบเทียบ เมื่อทำงานกับ Linearlayout ViewGroup กรณีการใช้งานที่เราต้องการคือความสามารถในการวางรายการในแนวตั้งหรือแนวนอน สิ่งนี้สามารถนำไปใช้ได้อย่างง่ายดายโดยการเพิ่มแอตทริบิวต์การวางแนว ซึ่งบอกเราว่าเค้าโครงเชิงเส้นจะถูกวางบนหน้าจออย่างไร เราสามารถทำได้โดยใช้ android:orientation=VERTICAL|HORIZONTAL
คุณลักษณะ
นอกจากนี้เรายังมี ViewGroup อีกอันที่เรียกว่า GridLayout ซึ่งกรณีการใช้งานคือเมื่อเราต้องการวาง Views ไว้ในโครงสร้างตารางสี่เหลี่ยม นี่อาจเป็นเพราะเหตุผลต่างๆ เช่น การทำให้ข้อมูลที่เรานำเสนอแก่ผู้ใช้แอปง่ายต่อการใช้งาน จากการออกแบบ GridLayout ช่วยให้สามารถกำหนดค่าเพื่อช่วยให้คุณบรรลุเป้าหมายนี้ได้ โดยมีการกำหนดค่าที่เราสามารถกำหนดขนาดของตารางได้ เช่น เราสามารถมีตาราง 4×4, ตาราง 3 x 2 ได้
RecyclerView.ViewHolder
ViewHolder เป็นคลาสนามธรรมที่เราขยายมาจาก RecyclerView ViewHolder มีวิธีการทั่วไปเพื่อช่วยเราอ้างอิงมุมมองที่เราวางไว้บน RecyclerView แม้ว่าเครื่องจักร Recycling ใน RecyclerView ได้เปลี่ยนแปลงข้อมูลอ้างอิงต่างๆ ที่เราไม่รู้จักแล้วก็ตาม
รายการขนาดใหญ่
RecyclerViews ถูกใช้เมื่อเราต้องการนำเสนอชุด Views ที่มีขนาดใหญ่มากให้กับผู้ใช้ โดยที่ไม่ทำให้เราหมดแรง แรม บนอุปกรณ์ของเราสำหรับแต่ละอินสแตนซ์ของมุมมองที่สร้างขึ้น
หากเราใช้กรณีของรายชื่อผู้ติดต่อ เราจะมีแนวคิดทั่วไปว่ารายชื่อผู้ติดต่อหนึ่งรายการจะมีลักษณะอย่างไร จากนั้นเราจะสร้างเค้าโครงเทมเพลต ซึ่งจริงๆ แล้วคือมุมมอง โดยมีช่องสำหรับกรอกข้อมูลต่างๆ จากรายชื่อผู้ติดต่อของเรา ต่อไปนี้คือรหัสหลอกที่อธิบายวัตถุประสงค์ทั้งหมด:
//OneContactView <OneContact> <TextView>{{PlaceHolderForName}}</TextView> <TextView>{{PlaceHolderForAddress}}</TextView> <ImageView>{{PlaceHolderForProfilePicture}}</ImageView> <TextView>{{PlaceHolderForPhoneNumber}}</TextView> </OneContact>
จากนั้นเราก็จะมี ContactList ในลักษณะนี้
<ContactList> </ContactList>
หากเป็นกรณีนี้ เรากำลังฮาร์ดโค้ดเนื้อหา เราจะไม่มีวิธีทางโปรแกรมในการเพิ่มเนื้อหาใหม่ลงในรายการโดยไม่ต้องเขียนแอปใหม่ โชคดีสำหรับเรา การเพิ่มมุมมองให้กับ ViewGroup ได้รับการสนับสนุนโดย addView(view:View)
วิธี
แม้ว่าจะเป็นเช่นนั้น แต่ก็ไม่ใช่วิธีที่ RecyclerView เพิ่มมุมมองลูกๆ เข้าไป
ในกรณีการใช้งานของเรา เราจะมีรายชื่อผู้ติดต่อที่ยาวเหยียด สำหรับผู้ติดต่อแต่ละรายในรายการ เราจะต้องสร้าง OneContactView และเติมข้อมูลภายใน View เพื่อให้ตรงกับฟิลด์ในคลาส Contact ของเรา จากนั้นเมื่อเราได้ view แล้ว เราจะต้องเพิ่มมันเข้าไปใน RecyclerView เพื่อแสดงรายการ
data class Contact(var name:String, var address:String, var pic:String, var phoneNumber:Int) var contact1 = Contact("Guru","Guru97", "SomePic1.jpg", 991) var contact2 = Contact("Guru","Guru98", "SomePic2.jpg", 992) var contact3 = Contact("Guru","Guru99", "SomePic3.jpg", 993) var myContacts:ArrayList<Contact> = arrayListOf<Contact>(contact1,contact2,contact3)
เรามีรายชื่อติดต่อมากมายที่เรียกว่า OneContactView มีช่องสำหรับนำเนื้อหาจากคลาส Contact และแสดง ใน RecyclerView เราต้องเพิ่ม Views ลงไปเพื่อที่จะสามารถช่วยเราในเรื่องความสามารถในการรีไซเคิลได้
RecyclerView ไม่อนุญาตให้เราเพิ่มมุมมองจริงๆ แต่ช่วยให้เราสามารถเพิ่ม ViewHolder ได้ ดังนั้นในสถานการณ์นี้ เรามีสองสิ่งที่เราต้องการเชื่อมต่อแต่ไม่ตรงกัน นี่คือที่มาของ Adapter ของเรา RecyclerView ให้ Adapter เหมือนกับเรามาก AmericanToBritishChargerAdapter()
จากก่อนหน้านี้ทำให้เราสามารถแปลง AmericanCharger ที่ไม่สามารถใช้งานได้กับอุปกรณ์ BritishDevice ของเราไปเป็นอุปกรณ์ที่ใช้งานได้ คล้ายกับอะแดปเตอร์แปลงไฟในชีวิตจริง
ในสถานการณ์สมมตินี้ อะแดปเตอร์จะนำอาร์เรย์ผู้ติดต่อและมุมมองของเรา และจากนั้นจะสร้าง ViewHolders ซึ่ง RecyclerView ยินดีที่จะยอมรับ
RecyclerView จัดเตรียมอินเทอร์เฟซที่เราสามารถขยายเพื่อสร้างอะแดปเตอร์ของเราผ่านคลาส RecyclerView.Adapter ภายในอะแดปเตอร์นี้เป็นวิธีสร้างคลาส ViewHolder ที่ RecyclerView ต้องการทำงานด้วย ดังนั้นสิ่งที่เรามีคือสถานการณ์เหมือนเดิม แต่ที่พิเศษอีกอย่างหนึ่งนั่นคืออะแดปเตอร์
เรามีอาร์เรย์ของผู้ติดต่อ มุมมองที่จะแสดงผู้ติดต่อ OneContactView หนึ่งรายการ RecyclerView คือรายการ Views ที่ให้บริการรีไซเคิล แต่ยินดีรับเฉพาะ ViewHolders เท่านั้น
แต่ในสถานการณ์นี้ ตอนนี้เรามีคลาส RecyclerView.Adapter ซึ่งมีวิธีการสร้าง ViewHolders ภายใน
fun createViewHolder(@NonNull parent: ViewGroup, viewType: Int): ViewHolder
RecyclerView.ViewHolder เป็นคลาสนามธรรมที่ใช้ View ของเราเป็นอาร์กิวเมนต์และแปลงเป็น ViewHolder
ใช้รูปแบบ wrapper ที่ใช้ในการขยายความสามารถของคลาส
รากฐาน #2 – รูปแบบกระดาษห่อ
เราจะใช้ตัวอย่างง่ายๆ เพื่อแสดงให้เห็นว่าเราจะทำให้สัตว์พูดได้อย่างไร
sealed trait Animal{ fun sound():String } data class Cat(name:String):Animal{ fun sound(){ "Meow" } } data class Dog(name:String):Animal{ fun sound(){ "Woof" } } var cat1 = Cat("Tubby") var dog1 = Dog("Scooby") cat1.sound() //meow dog1.sound() //woof
ในตัวอย่างข้างต้น เรามีสัตว์สองตัว หากบังเอิญเราต้องการเพิ่มวิธีการพูด แต่ผู้เขียนห้องสมุดไม่สนุก เราก็ยังสามารถหาวิธีได้ สิ่งที่เราต้องการคือกระดาษห่อสำหรับชั้นเรียนสัตว์ของเรา เราจะทำสิ่งนี้โดยรับ Animal มาเป็นคอนสตรัคเตอร์สำหรับชั้นเรียนของเรา
class SpeechPoweredAnimalByWrapper(var myAnimal:Animal){ fun sound(){ myAnimal.sound() } speak(){ println("Hello, my name is ${myAnimal.name}") } }
ตอนนี้เราสามารถส่งตัวอย่างสัตว์ไปยัง SpeechPoweredAnimalByWrapper ได้แล้ว การเรียกเมธอด sound() จะเรียกเมธอดเสียงสัตว์ที่ส่งผ่าน () นอกจากนี้เรายังมีเมธอด say() เพิ่มเติม ซึ่งนับเป็นฟังก์ชันใหม่ที่เราเพิ่มให้กับสัตว์ที่ผ่านไป เราสามารถใช้ได้ดังนี้:
var cat1 = Cat("Garfield") cat1.sound()//"meow" cat1.speak()// doesn't work as it isn't implemented var talkingCat = new SpeechPoweredAnimalByWrapper(cat1) talkingCat.sound() //"meow" the sound method calls the one defined for cat1 talkingCat.speak() //"Hello, my name is Garfield"
การใช้รูปแบบนี้ทำให้เราสามารถเรียนและเพิ่มฟังก์ชันการทำงานได้ สิ่งที่เราต้องมีก็แค่ส่งคลาสอินสแตนซ์และวิธีการใหม่ที่กำหนดโดยคลาสการห่อของเรา
ในกรณีข้างต้น เราใช้คลาสที่เป็นรูปธรรม นอกจากนี้ยังสามารถนำไปใช้ในคลาส Abstract ได้อีกด้วย สิ่งที่เราต้องทำคือเพิ่มการเปลี่ยนแปลงคลาส SpeechPoweredAnimalByWrapper ให้เป็น abstract และเสร็จเรียบร้อย เราจะเปลี่ยนชื่อคลาสให้สั้นลงเพื่อให้อ่านง่ายขึ้น
abstract class SpeechPowered(var myAnimal:Animal){ fun sound(){ myAnimal.sound() } speak(){ println("Hello, my name is ${myAnimal.name}") } }
มันเหมือนเดิมแต่มันจะมีความหมายอย่างอื่น ในคลาสปกติ เราสามารถมีอินสแตนซ์ของคลาสได้ในลักษณะเดียวกับที่เราสร้าง cat1 และ dog1 อย่างไรก็ตาม คลาสนามธรรมไม่ได้มีไว้สำหรับสร้างอินสแตนซ์ แต่มีไว้เพื่อขยายคลาสอื่นๆ แล้วเราจะใช้คลาสนามธรรม SpeechPowered(var myAnimal:Animal) ใหม่ได้อย่างไร เราสามารถใช้มันโดยการสร้างคลาสใหม่ที่จะขยายและเพิ่มฟังก์ชันการทำงานให้กับมัน
ในตัวอย่างของเรา เราจะสร้างคลาส SpeechPoweredAnimal ที่ขยายคลาส
class SpeechPoweredAnimal(var myAnimal:Animal):SpeechPowered(myAnimal)
var cat1 = Cat("Tubby") var speakingKitty = SpeechPoweredAnimal(cat1) speakingKitty.speak() //"Hello, my name is Tubby"
นี่เป็นรูปแบบเดียวกับที่ใช้ใน ViewHolder คลาส RecyclerView.ViewHolder เป็นคลาสนามธรรมที่เพิ่มฟังก์ชันการทำงานให้กับ View เหมือนกับที่เราเพิ่มวิธีการพูดให้กับสัตว์ ฟังก์ชั่นที่เพิ่มเข้ามาคือสิ่งที่ทำให้ใช้งานได้เมื่อต้องจัดการกับ RecyclerView
นี่คือวิธีที่เราจะสร้าง OneContactViewHolder จาก OneContactView
//The View argument we pass is converted to a ViewHolder which uses the View to give it more abilities and in turn work with the RecyclerView class OneContactViewHolder(ourContactView: View) : RecyclerView.ViewHolder(ourContactView)
RecyclerView มีอะแดปเตอร์ที่ช่วยให้เราสามารถเชื่อมต่ออาร์เรย์ Contacts ของเรากับ ContactsView ด้วย RecyclerView
การเพิ่มมุมมอง
ViewGroup จะไม่วาด ViewGroup ใหม่โดยอัตโนมัติ แต่จะทำตามตารางเวลาเฉพาะ อาจเป็นกรณีที่อุปกรณ์ของคุณวาดใหม่ทุกๆ 10 มิลลิวินาทีหรือ 100 มิลลิวินาที หรือถ้าเราเลือกตัวเลขที่ไร้สาระ เช่น 1 นาที เมื่อเราเพิ่ม View ลงใน ViewGroup คุณจะเห็นการเปลี่ยนแปลง 1 นาทีต่อมาเมื่อ ViewGroup "รีเฟรช"
RecyclerView.Recycler
รากฐาน #3 เก็บเอาไว้
ตัวอย่างที่ดีที่สุดประการหนึ่งของการที่เรารีเฟรชเป็นประจำคือในเบราว์เซอร์ ตัวอย่างเช่น สมมติว่าไซต์ที่เรากำลังเยี่ยมชมเป็นแบบคงที่และไม่ได้ส่งเนื้อหาแบบไดนามิก เราจะต้องรีเฟรชเพื่อดูการเปลี่ยนแปลง
สำหรับตัวอย่างนี้ ลองจินตนาการว่าไซต์ที่เป็นปัญหาคือ Twitter เราจะมีรายการทวีตแบบคงที่และวิธีเดียวที่เราจะเห็นทวีตใหม่คือการคลิกปุ่มรีเฟรชเพื่อเรียกเนื้อหาอีกครั้ง
เห็นได้ชัดว่าการทาสีใหม่ทั้งหน้าจอเป็นงานที่มีค่าใช้จ่ายสูง หากเราจินตนาการว่าเป็นกรณีนี้ เรามีแบนด์วิธที่จำกัดกับผู้ให้บริการโทรศัพท์ของเรา และรายการทวีตของเรามีรูปภาพและวิดีโอจำนวนมาก การดาวน์โหลดเนื้อหาทั้งหมดของหน้าซ้ำทุกครั้งที่รีเฟรชจะมีค่าใช้จ่ายสูง
เราต้องการวิธีจัดเก็บทวีตที่โหลดไว้แล้ว และให้แน่ใจว่าคำขอถัดไปของเราสามารถพูดทวีตที่มีอยู่แล้วได้ ดังนั้นจึงไม่ดาวน์โหลดใหม่ทั้งหมดและรับเฉพาะทวีตใหม่ที่มีอยู่และตรวจสอบว่าทวีตบางรายการที่บันทึกไว้ในเครื่องไม่มีอยู่อีกต่อไปเพื่อให้สามารถลบออกในเครื่องได้หรือไม่ สิ่งที่เรากำลังอธิบายเรียกว่าการแคช
ข้อมูลที่เราส่งไปยังเว็บไซต์เกี่ยวกับเนื้อหาที่เรามีเรียกว่าเมตาดาต้า ดังนั้น ตามความเป็นจริงแล้ว เราไม่ได้แค่พูดว่า "เราต้องการโหลดไซต์ของคุณ" แต่เราพูดว่า "เราต้องการโหลดไซต์ของคุณ และนี่คือเนื้อหาบางส่วนที่เราบันทึกไว้แล้วจากครั้งล่าสุดที่เราโหลด โปรดใช้เพื่อ ส่งเฉพาะสิ่งที่ไม่มีในนั้นมาให้เราเท่านั้น เพื่อที่เราจะได้ไม่ใช้แบนด์วิดท์มากนักเนื่องจากเรามีทรัพยากรไม่มาก”
การโทรแบบเลย์เอาต์ – รายการทวีตต้องบ้าแน่ๆ
ตัวอย่างของการเรียกโครงร่างคือ scrollToPosition
นี่เป็นตัวอย่างทั่วไปที่มีอยู่ในสิ่งต่างๆ เช่น แอปแชท หากมีคนในกลุ่มแชทตอบกลับฟองแชทจากก่อนหน้านี้ แอปแชทบางแอปจะมีการตอบกลับและลิงก์ไปยังฟองแชท ซึ่งเมื่อคลิกจะนำทางคุณไปยังตำแหน่งข้อความต้นฉบับของคุณ
ในกรณีนี้ เราเรียกเมธอดนี้ก่อนที่เราจะเพิ่ม LayoutManager ให้กับ RecyclerView ของเรา และก่อนที่เราจะมี RecyclerView.Adapter นั้น scrollToPosition(n:Int) จะถูกละเว้นไป
การสื่อสารระหว่างส่วนประกอบ RecyclerView
รากฐาน #4 โทรกลับ
ในการทำงาน RecyclerView มีชิ้นส่วนที่เคลื่อนไหวได้มากมาย มันต้องจัดการกับ LayoutManager ซึ่งบอกเราถึงวิธีการจัดระเบียบ Views ไม่ว่าจะเป็นแบบเชิงเส้นหรือแบบตาราง มันต้องจัดการกับอะแดปเตอร์ที่ทำหน้าที่แปลงรายการ contactList ของเราเป็น Views OneContactView จากนั้นเป็น ViewHolders OneContactViewHolder ที่ RecyclerView เต็มใจที่จะทำงานภายในวิธีการที่มอบให้เรา
วัตถุดิบสำหรับ RecyclerView คือมุมมองของเรา เช่น OneContactView และแหล่งข้อมูล
contactList:Array<Contact>
เราใช้สถานการณ์สมมติง่ายๆ เป็นจุดเริ่มต้นในการทำความเข้าใจว่า RecyclerView พยายามบรรลุผลอย่างไร
กรณีฐานเมื่อเรามีอาร์เรย์คงที่ของผู้ติดต่อ 1000 รายที่เราต้องการแสดงให้ผู้ใช้เข้าใจได้ง่าย
เครื่องจักร RecyclerView เริ่มใช้งานได้จริงเมื่อรายการไม่คงที่อีกต่อไป
ด้วยรายการแบบไดนามิก เราต้องคิดถึงสิ่งที่เกิดขึ้นกับมุมมองบนหน้าจอเมื่อเราเพิ่มรายการลงในรายการหรือลบรายการออกจากรายการ
RecyclerView.LayoutManager
นอกเหนือจากการตัดสินใจว่ามุมมองของเราจะถูกจัดวางอย่างไรให้เป็นแบบเชิงเส้นหรือแบบกริดแล้ว LayoutManager ยังทำงานมากมายเพื่อช่วยให้ Recycler รู้ว่าเมื่อใดควรทำการ Recycling
มีหน้าที่รับผิดชอบในการติดตามมุมมองที่ปรากฏบนหน้าจอในปัจจุบันและสื่อสารข้อมูลนี้ไปยังกลไกการรีไซเคิล เมื่อผู้ใช้เลื่อนลง ตัวจัดการเค้าโครงมีหน้าที่แจ้งระบบรีไซเคิลของมุมมองที่ไม่อยู่ในโฟกัสที่ด้านบน เพื่อให้สามารถนำมาใช้ซ้ำได้ แทนที่จะทิ้งไว้ตรงนั้นและใช้หน่วยความจำ หรือแทนที่จะทำลายและต้องสร้าง อันใหม่.
ความหมายก็คือ LayoutManager จำเป็นต้องติดตามว่าผู้ใช้อยู่ที่ไหนในขณะที่พวกเขาเลื่อนดูรายการของเรา โดยจะมีรายการตำแหน่งที่เป็นฐานดัชนี เช่น รายการแรกให้เริ่มจาก 0 แล้วเพิ่มขึ้นให้ตรงกับจำนวนรายการในรายการของเรา
หากเราสามารถดู 10 รายการในรายการของเราที่บอกว่า 100 ในตอนเริ่มต้น LayoutManager จะทราบว่ามันมีอยู่ในโฟกัส view-0 ไปจนถึง View-9 ขณะที่เราเลื่อน LayoutManager จะสามารถคำนวณมุมมองที่ออกมาได้ ของการมุ่งเน้น
LayoutManager สามารถปล่อยมุมมองเหล่านี้ไปยังกลไกการรีไซเคิลเพื่อให้สามารถนำกลับมาใช้ใหม่ได้ (สามารถผูกข้อมูลใหม่เข้ากับมุมมองเหล่านั้นได้ เช่น ข้อมูลผู้ติดต่อของ View สามารถลบออกได้ และข้อมูลผู้ติดต่อใหม่จากส่วนถัดไปสามารถแทนที่ตัวยึดตำแหน่งได้)
นี่คงจะเป็นกรณีที่มีความสุขหากรายการที่เรามีเป็นแบบคงที่ แต่กรณีการใช้งานที่พบบ่อยที่สุดประการหนึ่งของการใช้ RecyclerView คือรายการแบบไดนามิกที่ข้อมูลสามารถมาจากจุดสิ้นสุดออนไลน์หรือแม้กระทั่งจากเซ็นเซอร์ก็ได้ ไม่เพียงเพิ่มข้อมูลเท่านั้น แต่บางครั้งข้อมูลในรายการของเราก็ถูกลบหรืออัปเดตอีกด้วย
สถานะไดนามิกของข้อมูลของเราอาจทำให้ยากต่อการให้เหตุผลเกี่ยวกับ LayoutManager ด้วยเหตุนี้ LayoutManager จึงเก็บรักษารายการของตนเองเกี่ยวกับรายการและตำแหน่ง ซึ่งแยกจากรายการที่คอมโพเนนต์การรีไซเคิลใช้ เพื่อให้แน่ใจว่างานเลย์เอาต์ถูกต้อง
ในขณะเดียวกัน LayoutManager ของ RecyclerView ไม่ต้องการแสดงข้อมูลที่ไม่ถูกต้อง เพื่อให้ทำงานได้อย่างถูกต้อง LayoutManager จะซิงโครไนซ์กับ RecyclerView.Adapter ในช่วงเวลาที่กำหนด (60 มิลลิวินาที) โดยแบ่งปันข้อมูลเกี่ยวกับรายการของเรา เช่น รายการที่เพิ่ม อัปเดต ลบ ย้ายจากตำแหน่งหนึ่งไปยังอีกตำแหน่งหนึ่ง LayoutManager จะจัดระเบียบเนื้อหาบนหน้าจอใหม่เพื่อให้ตรงกับการเปลี่ยนแปลงเมื่อจำเป็นเมื่อได้รับข้อมูลนี้
การทำงานหลักหลายอย่างที่เกี่ยวข้องกับ RecylerView หมุนเวียนเกี่ยวกับการสื่อสารระหว่าง RecyclerView.LayoutManager และ RecyclerView.Adapter ที่จัดเก็บรายการข้อมูลของเรา ซึ่งบางครั้งอาจเป็นแบบคงที่หรือบางครั้งอาจเป็นแบบไดนามิก
ยิ่งไปกว่านั้น RecyclerView ยังนำเสนอวิธีที่เราสามารถใช้เพื่อรับฟังเหตุการณ์ต่างๆ เช่น onBindViewHolder เมื่อ RecyclerView.Adapter ของเราเชื่อมโยงเนื้อหาจากรายการของเรา เช่น ผู้ติดต่อไปยัง ViewHolder เพื่อให้ตอนนี้คุ้นเคยกับการแสดงข้อมูลบนหน้าจอ
อีกประการหนึ่งคือ onCreateViewHolder ซึ่งจะแจ้งให้เราทราบเมื่อ RecyclerView อะแดปเตอร์ใช้ View ปกติ เช่น OneContactView และแปลงเป็นรายการ ViewHolder ที่ RecyclerView สามารถใช้งานได้ จาก ViewHolder ของเราสำหรับการใช้งานโดย RecyclerView onViewDetached
นอกเหนือจากกลไกหลักที่ทำให้สามารถรีไซเคิลได้ RecyclerView มีวิธีปรับแต่งพฤติกรรมโดยไม่ส่งผลกระทบต่อการรีไซเคิล
การใช้มุมมองซ้ำทำให้ยากต่อการทำสิ่งทั่วไปที่เราคุ้นเคยด้วยมุมมองคงที่ เช่น การตอบสนองต่อเหตุการณ์ onClick
อย่างที่เราทราบกันดีว่า RecyclerView.LayoutManager
ที่นำเสนอมุมมองให้กับผู้ใช้อาจมีรายการที่แตกต่างไปจาก RecyclerView.Adapter
ที่มีรายการที่เราเก็บไว้ในฐานข้อมูลหรือสตรีมมิ่งจากแหล่งที่มา การวางเหตุการณ์ OnClick บน Views โดยตรงอาจทำให้เกิดพฤติกรรมที่ไม่คาดคิด เช่น การลบผู้ติดต่อผิดหรือการเปลี่ยนแปลง
Gradle
หากเราต้องการใช้ RecyclerView เราจำเป็นต้องเพิ่มมันเป็นการพึ่งพาในไฟล์ build .gradle ของเรา
ในตัวอย่างต่อไปนี้ เราได้ใช้การใช้งาน “androidx.recyclerview:recyclerview:1.1.0” ซึ่งเป็นเวอร์ชันล่าสุดตามบทความนี้
หลังจากเพิ่มการอ้างอิงของเราแล้ว Gradle ไฟล์เราจะได้รับการแจ้งเตือนโดย Android สตูดิโอถึง Syncแก้ไขการเปลี่ยนแปลง
นี่คือวิธีที่เรา Gradle ไฟล์จะมีลักษณะเช่นนี้หลังจากเพิ่ม RecyclerView ลงในโปรเจ็กต์ว่างโดยมีเพียงค่าเริ่มต้น
apply plugin: 'com.android.application' apply plugin: 'kotlin-android' apply plugin: 'kotlin-android-extensions' android { compileSdkVersion 29 buildToolsVersion "29.0.2" defaultConfig { applicationId "com.guru99.learnrecycler" minSdkVersion 17 targetSdkVersion 29 versionCode 1 versionName "1.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } } dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" implementation 'androidx.appcompat:appcompat:1.1.0' implementation 'androidx.core:core-ktx:1.1.0' implementation 'androidx.constraintlayout:constraintlayout:1.1.3' testImplementation 'junit:junit:4.12' androidTestImplementation 'androidx.test.ext:junit:1.1.1' androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' implementation "androidx.recyclerview:recyclerview:1.1.0" }
ขณะนี้เรามีไฟล์เลย์เอาต์เพียงไฟล์เดียว เราจะเริ่มต้นด้วยตัวอย่างง่ายๆ โดยเราจะใช้ RecyclerView เพื่อแสดงรายการชื่อผลไม้บนหน้าจอ
รายการสินค้า
เราจะไปที่ไฟล์ MainActivity ของเราและสร้างอาร์เรย์ที่มีชื่อผลไม้อยู่ข้างในก่อนเมธอด onCreate() ที่สร้างขึ้นระหว่างการตั้งค่า
package com.guru99.learnrecycler import androidx.appcompat.app.AppCompatActivity import android.os.Bundle class MainActivity : AppCompatActivity() { var fruitNames:Array<String> = arrayOf<String>("Banana", "Mango", "Passion fruit", "Orange", "Grape") override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) } }
เป้าหมายต่อไปของเราคือการนำเสนอรายการนี้บนหน้าจอโดยใช้ RecyclerView
ในการดำเนินการนี้ เราจะไปที่ไดเร็กทอรีเค้าโครงที่เก็บเค้าโครงของเรา และสร้างมุมมองที่จะรับผิดชอบในการแสดงผลไม้หนึ่งชนิด
เค้าโครงที่จะใช้สำหรับแต่ละรายการในรายการของเรา
<?xml version="1.0" encoding="utf-8"?> <TextView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:id="@+id/fruitName" /> </TextView>
ใน TextView ด้านบน เราได้เพิ่มฟิลด์ ID ที่จะใช้ในการระบุมุมมอง
มันไม่ได้ถูกสร้างขึ้นโดยค่าเริ่มต้น เรามี TextView id FruitName ให้ตรงกับข้อมูลซึ่งจะเชื่อมโยงกับข้อมูลนั้น
การเพิ่ม RecyclerView ให้กับเลย์เอาต์หลัก
ในกิจกรรมเดียวกัน จะมีไฟล์โครงร่าง main_layout.xml ที่สร้างให้เราตามค่าเริ่มต้น
ถ้าเราเลือกโครงการที่ว่างเปล่า มันจะได้ก่อให้เกิด XML ที่มี ConstraintLayout และด้านในจะเป็น TextView พร้อมข้อความ "Hello"
เราจะลบเนื้อหาทั้งหมดและให้เค้าโครงมีเพียง RecyclerView ดังต่อไปนี้:
<?xml version="1.0" encoding="utf-8"?> <androidx.recyclerview.widget.RecyclerView 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:id="@+id/fruitRecyclerView" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity" />
นอกจากนี้เรายังได้เพิ่มแอตทริบิวต์ id สำหรับ RecyclerView ซึ่งเราจะใช้เพื่ออ้างอิงในโค้ดของเรา
android:id="@+id/fruitRecyclerView"
จากนั้นเราจะกลับไปที่ไฟล์ MainActivity ของเรา เมื่อใช้รหัสที่เราสร้างขึ้น เราจะสามารถอ้างอิงมุมมองที่เราเพิ่งสร้างขึ้นได้
เราจะเริ่มต้นด้วยการอ้างอิง RecyclerView โดยใช้เมธอด findViewById() ที่จัดทำโดย Android- เราจะทำสิ่งนี้โดยใช้วิธี onCreate() ของเรา
วิธีการ onCreate() ของเราจะมีลักษณะดังนี้
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) var myFruitRecyclerView: RecyclerView = findViewById(R.id.fruitRecyclerView) }
สร้าง ViewHolder
ต่อไป เราจะสร้าง RecyclerView.ViewHolder ซึ่งมีหน้าที่รับมุมมองของเราและแปลงเป็น ViewHolder ซึ่ง RecyclerView ใช้เพื่อแสดงรายการของเรา
เราจะทำสิ่งนี้ทันทีหลังจาก fun onCreate() วิธีการของเรา
package com.guru99.learnrecycler import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import android.view.View import androidx.recyclerview.widget.RecyclerView class MainActivity : AppCompatActivity() { var fruitNames:Array<String> = arrayOf<String>("Banana", "Mango", "Passion fruit", "Orange", "Grape") override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) var myFruitRecyclerView: RecyclerView = findViewById(R.id.fruitRecyclerView) } class FruitViewHolder(fruitView: View): RecyclerView.ViewHolder(fruitView) }
สร้าง RecyclerViewAdapter
ต่อไป เราจะสร้างคลาส FruitArrayAdapter ที่ขยายคลาส RecyclerView.Adapter
FruitArrayAdapter ที่เราสร้างจะรับผิดชอบในการทำสิ่งต่อไปนี้
โดยจะใช้ชื่อผลไม้จากอาร์เรย์ผลไม้ มันจะสร้าง ViewHolder โดยใช้มุมมองของเรา one_fruit_view.xml จากนั้นมันจะผูกผลไม้เข้ากับ ViewHolder และผูกเนื้อหาแบบไดนามิกกับมุมมองที่เราสร้าง one_fruit_view.xml
package com.guru99.learnrecycler import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import android.view.View import androidx.recyclerview.widget.RecyclerView class MainActivity : AppCompatActivity() { var fruitNames:Array<String> = arrayOf<String>("Banana", "Mango", "Passion fruit", "Orange", "Grape") override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) var myFruitRecyclerView: RecyclerView = findViewById(R.id.fruitRecyclerView) } class FruitViewHolder(fruitView: View): RecyclerView.ViewHolder(fruitView) class FruitArrayAdapter(var fruitArray: Array<String>) : RecyclerView.Adapter<FruitViewHolder>() }
Android Studio จะเพิ่ม squiggles สีแดงบน FruitArrayAdapter ของเรา โดยบอกเราว่าเราจำเป็นต้องใช้วิธีการที่ RecyclerView สามารถใช้เพื่อเชื่อมต่ออาร์เรย์ของเรากับ ViewHolder ที่ RecyclerView สามารถใช้ได้
class FruitListAdapter(var fruitArray: Array<String>) : RecyclerView.Adapter<FruitViewHolder>() { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FruitViewHolder { } override fun getItemCount(): Int { } override fun onBindViewHolder(holder: FruitViewHolder, position: Int) { } }
เราจะเริ่มต้นด้วยบิตที่ง่ายที่สุดจากโค้ดที่สร้างขึ้นคือเมธอด getItemCount() เรารู้วิธีรับจำนวนรายการในอาร์เรย์ของเราโดยการเรียกเมธอด array.length
class FruitListAdapter(var fruitArray: Array<String>) : RecyclerView.Adapter<FruitViewHolder>() { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FruitViewHolder { } override fun getItemCount(): Int { return fruitArray.size } override fun onBindViewHolder(holder: FruitViewHolder, position: Int) { } }
จากนั้นเราจะใช้เมธอด override fun onCreateViewHolder
นี่คือจุดที่ RecyclerView ขอให้เราช่วยสร้าง FruitHolder สำหรับมัน
หากเราจำได้ว่าคลาส FruitViewHolder ของเรามีลักษณะดังนี้:
class FruitViewHolder(fruitView: View): RecyclerView.ViewHolder(fruitView)
ต้องใช้ FruitView ของเราซึ่งเราสร้างเป็นไฟล์ xml one_fruit_view.xml
เราสามารถสร้างการอ้างอิงถึง xml นี้และแปลงเป็น View ได้ดังนี้
class FruitListAdapter(var fruitArray: Array<String>) : RecyclerView.Adapter<FruitViewHolder>() { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FruitViewHolder { var fruitView = LayoutInflater.from(parent.context).inflate(R.layout.one_fruit_view, parent, false) var fruitViewHolder = FruitViewHolder(fruitView) return fruitViewHolder } override fun getItemCount(): Int { return fruitArray.size } override fun onBindViewHolder(holder: FruitViewHolder, position: Int) { } }
บิตที่เหลือคือการแทนที่
fun onBindViewHolder(holder: FruitViewHolder, position: Int)
RecyclerView.Adapter ถามด้วยจำนวนเต็มตำแหน่ง ซึ่งเราจะใช้เพื่อดึงรายการจากรายการของเรา มันยังให้ผู้ถือแก่เราเพื่อให้เราสามารถผูกรายการที่เราได้รับจาก FruitArray ไปยัง View ที่ถูกจัดขึ้นภายในผู้ถือมุมมอง
มุมมองที่จัดขึ้นภายใน ViewHoder สามารถเข้าถึงได้ผ่านฟิลด์ ViewHolder.itemView เมื่อเราได้รับมุมมองแล้ว เราสามารถใช้ id FruitName ที่เราสร้างไว้ก่อนหน้านี้เพื่อตั้งค่าเนื้อหาได้
override fun onBindViewHolder(holder: FruitViewHolder, position: Int) { var ourFruitTextView = holder.itemView.findViewById<TextView>(R.id.fruitName) var aFruitName = fruitArray.get(position) ourFruitTextView.setText(aFruitName) }
ด้วยเหตุนี้ FruitArrayAdapter ของเราจึงเสร็จสมบูรณ์และมีลักษณะดังนี้
class FruitListAdapter(var fruitArray: Array<String>) : RecyclerView.Adapter<FruitViewHolder>() { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FruitViewHolder { var fruitView = LayoutInflater.from(parent.context).inflate(R.layout.one_fruit_view, parent, false) var fruitViewHolder = FruitViewHolder(fruitView) return fruitViewHolder } override fun getItemCount(): Int { return fruitArray.size } override fun onBindViewHolder(holder: FruitViewHolder, position: Int) { var ourFruitTextView = holder.itemView.findViewById<TextView>(R.id.fruitName) var aFruitName = fruitArray.get(position) ourFruitTextView.setText(aFruitName) } }
ในที่สุด เราก็พร้อมที่จะเชื่อมโยงส่วนที่เหลือของ RecyclerView ของเราแล้ว ซึ่งกำลังสร้าง LayoutManager ซึ่งจะบอก RecyclerView ว่าจะแสดงเนื้อหาของรายการอย่างไร จะแสดงในลักษณะเชิงเส้นโดยใช้ LinearLayoutManager หรือในตารางโดยใช้ GridLayoutManager หรือ StaggeredGridLayoutManager
สร้างตัวจัดการเค้าโครง
เราจะกลับเข้าไปในฟังก์ชัน onCreate ของเราและเพิ่ม LayoutManager
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) var myFruitRecyclerView: RecyclerView = findViewById(R.id.fruitRecyclerView) var fruitLinearLayout = LinearLayoutManager(this) myFruitRecyclerView.layoutManager =fruitLinearLayout }
เชื่อมต่ออะแดปเตอร์ของเรากับรายการต่างๆ และตั้งค่าบน RecyclerView
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) var myFruitRecyclerView: RecyclerView = findViewById(R.id.fruitRecyclerView) var fruitLinearLayout = LinearLayoutManager(this) myFruitRecyclerView.layoutManager =fruitLinearLayout var fruitListAdapter = FruitListAdapter(fruitNames) myFruitRecyclerView.adapter =fruitListAdapter }
นอกจากนี้เรายังได้สร้างอินสแตนซ์ FruitListAdapter และป้อนอาร์เรย์ของชื่อผลไม้ให้กับมันด้วย
โดยพื้นฐานแล้วเราทุกคนทำเสร็จแล้ว
ไฟล์ MainActivity.kt ที่สมบูรณ์มีลักษณะดังนี้
package com.guru99.learnrecycler import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.TextView import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView class MainActivity : AppCompatActivity() { var fruitNames:Array<String> = arrayOf<String>("Banana", "Mango", "Passion fruit", "Orange", "Grape") override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) var myFruitRecyclerView: RecyclerView = findViewById(R.id.fruitRecyclerView) var fruitLinearLayout = LinearLayoutManager(this) myFruitRecyclerView.layoutManager =fruitLinearLayout var fruitListAdapter = FruitListAdapter(fruitNames) myFruitRecyclerView.adapter =fruitListAdapter } class FruitViewHolder(fruitView: View): RecyclerView.ViewHolder(fruitView) class FruitListAdapter(var fruitArray: Array<String>) : RecyclerView.Adapter<FruitViewHolder>() { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FruitViewHolder { var fruitView = LayoutInflater.from(parent.context).inflate(R.layout.one_fruit_view, parent, false) var fruitViewHolder = FruitViewHolder(fruitView) return fruitViewHolder } override fun getItemCount(): Int { return fruitArray.size } override fun onBindViewHolder(holder: FruitViewHolder, position: Int) { var ourFruitTextView = holder.itemView.findViewById<TextView>(R.id.fruitName) var aFruitName = fruitArray.get(position) ourFruitTextView.setText(aFruitName) } } }