Day 17 - Swipe ListView
สวัสดีครับ บทความนี้เป็นบทความที่ 17 แล้วนะครับ ที่ผมจะมาเขียน ในซีรีย์ Learn 30 Android Libraries in 30 days
สำหรับบทความทั้งหมด อ่านได้จากด้านล่างครับ
- Day 1 : AndroidStaggered Grid
- Day 2 : Paralloid
- Day 3 : Retrofit
- Day 4 : SwipeRefreshLayout
- Day 5 : Android GraphView
- Day 6 : Holo Color Picker
- Day 7 : Android Async Http
- Day 8 : Crashlytics
- Day 9 : Butter Knife
- Day 10 : Android Annotations
- Day 11 : DateTimePicker
- Day 12 : Circular Progress Button
- Day 13 : ViewPager
- Day 14 : ViewPagerIndicator
- Day 15 : FadingActionBar
- Day 16 : AutofitTextView
- Day 17 : SwipeListView
- Day 18 : ShowcaseView
- Day 19 : GreenDAO
- Day 20 : AndroidViewAnimation
- Day 21 : ActiveAndroid
- Day 22 : Twitter4J
- Day 23 : ListViewAnimations
- Day 24 : AndEngine
- Day 25 : EazeGraph
- Day 26 : Cardslib
- Day 27 : AdapterKit
- Day 28 : WeatherLib
- Day 29 : FlatUI
- Day 30 : Android Firebase
สำหรับวันนี้ขอนำเสนอเรื่อง SwipeListView เป็น Library ที่เอาไว้ทำให้ ListView สามารถที่จะ swipe เลื่อนซ้าย ขวาได้ ดังตัวอย่าง วิดีโอและรูปข้างล่างเลยครับ
Link Youtube : https://www.youtube.com/watch?v=E0352OH488M
Installation
ขั้นตอนการติดตั้ง ง่ายๆครับ เพิ่ม dependencies ลงไปที่ไฟล์ build.gradle
(ไฟล์ภายใน Module)
dependencies {
compile ('com.fortysevendeg.swipelistview:swipelistview:1.0-SNAPSHOT@aar') {
transitive = true
}
}
ต่อจากนั้นก็เพิ่ม Maven Central ที่ไฟล์ build.gradle
ใน Root Project ดังนี้
repositories {
maven { url 'http://clinker.47deg.com/nexus/content/groups/public' }
}
ถ้ามี maven url หลายอัน ก็จะได้เป็นลักษณะนี้
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
....
}
allprojects {
repositories {
maven {
url {
'http://repo1.maven.org/maven2/'
"http://dl.bintray.com/populov/maven"
"http://clinker.47deg.com/nexus/content/groups/public"
}
}
jcenter()
}
}
กด Sync Project with Gradle File เป็นอันเรียบร้อย
Getting Started
การใช้งาน SwipeListView
มีตัวอย่าง Demo ให้ดูที่ Google Play ด้วยนะครับ ลองโหลดมาลองดูได้ ส่วน Sample Code ก็ตามนี้
สำหรับวิธีการใช้งาน ก็เหมือนการใช้ ListView
ธรรมดาเลยครับ โดย xml จะมีลักษณะประมาณนี้
<com.fortysevendeg.swipelistview.SwipeListView
xmlns:swipe="http://schemas.android.com/apk/res-auto"
android:id="@+id/example_lv_list"
android:listSelector="#00000000"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
swipe:swipeFrontView="@+id/front"
swipe:swipeBackView="@+id/back"
swipe:swipeActionLeft="[reveal | dismiss]"
swipe:swipeActionRight="[reveal | dismiss]"
swipe:swipeMode="[none | both | right | left]"
swipe:swipeCloseAllItemsWhenMoveList="[true | false]"
swipe:swipeOpenOnLongPress="[true | false]"
swipe:swipeAnimationTime="[miliseconds]"
swipe:swipeOffsetLeft="[dimension]"
swipe:swipeOffsetRight="[dimension]"
/>
ส่วนข้างล่างเป็นคำอธิบายแต่ละ attributes ครับ
swipeFrontView
: เลเอาท์สำหรับ Front View (ต้องมี)swipeBackView
: เลเอาท์สำหรับ Back View (ต้องมี)swipeActionLeft
: เอฟเฟคเวลา swipe ไปทางซ้าย มีให้เลือก reveal (default) กับ dismissswipeActionRight
: เอฟเฟคเวลา swipe ไปทางขวา มีให้เลือก reveal (default) กับ dismissswipeMode
: เลือก Gestures หรือไม่ มี ให้เลือก none, both, right, leftswipeCloseAllItemsWhenMoveList
: ให้ปิดที่ swipe ไว้ทั้งหมด เวลาเลือก listView. Default เป็น trueswipeOpenOnLongPress
เปิดเมื่อกด Long Press. Default เป็น trueswipeAnimationTime
: เวลาที่ใ้ห้แสดง animation หน่วยเป็น msswipeOffsetLeft
และswipeOffsetRight
: ระยะ offset เวลา swipe ไปซ้ายขวา จะให้มันเว้นระยะแค่ไหน
OK เมื่อรู้วิธีใช้และ attribute เบื้องต้นแล้ว ต่อไปก็มาสร้างโปรเจ็คกันนะครับ มันคล้ายๆกับ ListView ทั่วๆไป
Create Project
เป้าหมายโปรเจ็คคือ แสดง ListView เป็นชื่อเว็บไซต์ที่มีบทความสอนแอนดรอยส์ จากนั้นเมื่อ Swipe ก็จะมีปุ่มให้กดไปหน้าเว็บไซต์นั้นๆครับ สำหรับไฟล์และเลเอาท์ที่จะใช้ก็จะมีดังนี้
SwipeListViewActivity.java
: สำหรับ Activity หลักครับCustomAdapter
: มี Custom Adapter คลาสนึง เอาไว้แสดง หน้า front และ back ของ ListView แต่ละแถวactivity_swipe_listview.xml
: หน้าเลเอาท์หลัก มีSwipeListView
อยู่ภายในlist_item.xml
: สำหรับ Layout ในแต่ละ row ของSwipeListView
บทความที่เกี่ยวข้อง
เริ่มที่ activity_swipe_listview.xml
มีแค่ SwipeListView
ภายใน LinearLayout อันเดียวเท่านั้น
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:swipe="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.fortysevendeg.swipelistview.SwipeListView
android:id="@+id/list_view"
android:listSelector="#f30085ff"
android:layout_width="match_parent"
android:layout_height="wrap_content"
swipe:swipeFrontView="@+id/front"
swipe:swipeBackView="@+id/back"
swipe:swipeAnimationTime="300"
swipe:swipeActionLeft="dismiss"
swipe:swipeMode="both"
swipe:swipeCloseAllItemsWhenMoveList="true" />
</LinearLayout>
ต่อมาก็สร้างเลเอาท์ แต่ละแถวของ ListView ก็คือไฟล์ list_item.xml
โดยให้ FrontView อยู่ด้านบนของ BackView
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="16dp"
android:gravity="center"
android:background="#ffcdcbcb"
android:id="@+id/back"
android:tag="back" >
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/button_visit"
android:text="@string/visit_website"/>
</LinearLayout>
<RelativeLayout
android:layout_height="match_parent"
android:layout_width="match_parent"
android:padding="16dp"
android:background="#ff31415a"
android:id="@+id/front"
android:tag="front">
<ImageView
android:layout_width="64dp"
android:layout_height="64dp"
android:layout_centerVertical="true"
android:layout_marginRight="16dp"
android:id="@+id/website_logo"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_toRightOf="@id/website_logo"
android:layout_centerVertical="true"
android:textColor="#ffffff"
android:id="@+id/website_title"/>
</RelativeLayout>
</FrameLayout>
เลเอาท์ LinearLayout และ RelativeLayout จะซ้อนกันแบบนี้
จะเห็นว่า มี id ชื่อ @+id/back
และ @+id/front
ซึ่งเป็นเลเอาท์ สำหรับแสดงข้างหน้าก่อนการ swipe และ เลเอาท์ข้างหลัก เมื่อมีการ swipe ดังที่ประกาศ attribute
swipe:swipeFrontView="@+id/front"
swipe:swipeBackView="@+id/back"
ไว้ในไฟล์ activity_swipe_listview.xml
Custom Adapter
ต่อมาผมทำ Custom Adapter เพื่อจะจัดหน้าตาเลเอาท์แต่ละ row เอง ส่วนนี้ไม่ขอลงรายละเอียดมากนะครับ ไปอ่านเพิ่มเติมในบทความเกี่ยวกับ Custom ListView ดูครับ
ทำการตั้งชื่อว่า WebsiteAdapter.java
มีโค๊ดดังนี้
package com.devahoy.learn30androidlibraries.day17;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import com.devahoy.learn30androidlibraries.R;
import java.util.ArrayList;
public class WebsiteAdapter extends BaseAdapter {
private Context mContext;
private LayoutInflater mInflater;
private ArrayList<Website> mWebsites;
public WebsiteAdapter(Context context, ArrayList<Website> sites) {
mContext = context;
mInflater = LayoutInflater.from(context);
mWebsites = sites;
}
public int getCount() {
return mWebsites.size();
}
public Object getItem(int position) {
return mWebsites.get(position);
}
public long getItemId(int position) {
return position;
}
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
holder = new ViewHolder();
convertView = mInflater.inflate(R.layout.day17_list_item, parent, false);
holder.title = (TextView) convertView.findViewById(R.id.website_title);
holder.buttonVisit = (Button) convertView.findViewById(R.id.button_visit);
holder.logo = (ImageView) convertView.findViewById(R.id.website_logo);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
final Website website = mWebsites.get(position);
holder.title.setText(website.getTitle());
holder.logo.setImageResource(website.getImageResourceId());
holder.buttonVisit.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse(website.getUrl()));
mContext.startActivity(intent);
}
});
return convertView;
}
static class ViewHolder {
TextView title;
Button buttonVisit;
ImageView logo;
}
}
ตัวเว็บไซต์มี ชื่อ มี url และมีโลโ้ก้ ฉะนั้นผมเลยสร้างคลาส Website ขึ้นมาครับ ดังนี้
package com.devahoy.learn30androidlibraries.day17;
public class Website {
String title;
String url;
int imageResourceId;
public Website(String title, String url, int imgId) {
this.title = title;
this.url = url;
this.imageResourceId = imgId;
}
public String getTitle() {
return title;
}
public String getUrl() {
return url;
}
public int getImageResourceId() {
return imageResourceId;
}
}
สุดท้ายที่ไฟล์ Activity หลัก ผมตั้งชื่อว่า SwipeListViewActivity
package com.devahoy.learn30androidlibraries.day17;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import com.devahoy.learn30androidlibraries.R;
import com.fortysevendeg.swipelistview.BaseSwipeListViewListener;
import com.fortysevendeg.swipelistview.SwipeListView;
import java.util.ArrayList;
public class SwipeListViewActivity extends ActionBarActivity {
SwipeListView mSwipeListView;
private ArrayList<Website> mWebsites;
private WebsiteAdapter mAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.day17_activity_swipe_listview);
mSwipeListView = (SwipeListView) findViewById(R.id.list_view);
initSampleData();
mSwipeListView.setSwipeListViewListener(new BaseSwipeListViewListener() {
@Override
public void onDismiss(int[] reverseSortedPositions) {
for (int position : reverseSortedPositions) {
Website website = mWebsites.get(position);
mWebsites.remove(position);
mWebsites.add(website);
}
mAdapter.notifyDataSetChanged();
}
});
}
private void initSampleData() {
Website akexorcist = new Website("Sleeping For Less",
"http://www.akexorcist.com/",
R.drawable.akexorcist);
Website androidthai = new Website("ทุกๆเรื่อง ที่เกี่ยวกับ android โดย มาสเตอร์ อึ่ง",
"http://www.androidthai.in.th/",
R.drawable.androidthai);
Website android4health = new Website("android4health",
"http://android4health.wordpress.com/",
R.drawable.android4health);
Website martoutine = new Website("Mart Tanathip | Simple routine of me",
"http://www.martroutine.com",
R.drawable.martroutine);
Website nuuneoi = new Website("NuuNeoI : Personal Blog of a little full stack developer guy",
"http://nuuneoi.com",
R.drawable.nuuneoi);
Website devahoy = new Website("Devahoy",
"https://devahoy.com",
R.drawable.devahoy);
mWebsites = new ArrayList<Website>();
mWebsites.add(akexorcist);
mWebsites.add(androidthai);
mWebsites.add(android4health);
mWebsites.add(martoutine);
mWebsites.add(nuuneoi);
mWebsites.add(devahoy);
mAdapter = new WebsiteAdapter(this, mWebsites);
mSwipeListView.setAdapter(mAdapter);
}
}
โค๊ดด้านบน เป็นการสร้าง WebsiteAdapter
จาก ArrayList<Website>
ที่ผมจำลองขึ้นมา จากนั้นก็ setAdapter()
ให้กับ SwipeListView
ส่วน SwipeListView
ทำการ setSwipeListViewListener
ให้มันเพื่อส่ง callback กลับมา ในกรณีที่มีการ swipe จากนั้นผม override เมธอด แค่ตัวเดียวครับ onDismiss
เวลาที่ทำการ swipe ไปทางซ้าย ก็จะเป็นการเรียก dismiss (ที่คอนฟิคไว้ใน xml) ก็จะให้แถวๆนั้น ไปอยู่ท้ายสุดของ List ครับ ส่วนเมธอดใน Listenter มีอะไรบ้าง ก็ตามรูปครับ ลองนำไปประยุกต์ใช้ดูครับ
ส่วนใน initSampleData()
ผมทำการสร้าง ข้อมูลขึ้นมา โดยใส่ title, url และ ไฟล์รูปภาพนะครับ สำหรับใครอยากได้รูปภาพ ก็โหลดจาก Github เลยครับ ภาพแอบขโมยมาจากแถวๆนี้แหละครับ :)
เมื่อรัน ผลลัพธ์ก็ได้ดังรูป
Happy Coding :D
Source Code
References
- Authors
- Name
- Chai Phonbopit
- Website
- @Phonbopit