Day 2 - Paralloid
สวัสดีครับ บทความนี้เป็นบทความที่ 2 แล้วนะครับ ที่ผมจะมาเขียน ในซีรีย์ 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
Paralloid คืออะไร?
Paralloid เป็น Android Parallax Library ครับ แล้ว Parallax คืออะไร? บางคนอาจจะสงสัยใช่มั้ยครับ ถ้าใครไม่รู้ แนะนำให้อ่าน ที่นี่ประกอบครับ สร้างเว็บแนว Parallax Scrolling แบบง่ายๆ ด้วย jQuery Jarallax ถึงแม้จะเป็นการทำ Parallax บนเว็บไซต์ แต่ว่าหลักการมันคืออย่างเดียวกัน นั่นแหละ
Features
เจ้า Paralloid ที่จะใช้เนี้ย มันทำอะไรได้บ้างละ? หลักๆเลย คือ
- ทำ Parallax กับ View อื่นๆเวลาที่มีการ scroll
- สามารถทำ Parallax หลายๆ แบล็กกราวน์ได้
- Parallax ได้หลายทิศทาง
- รองรับทั้ง ListView, ScrollView และ HorizontalScrollView
ตัวอย่าง จาก Google Play ลองโหลดมาดูได้ครับ
Getting Started
สำหรับ Library ตัวนี้ ต้องบอกว่า มันติดตั้งยุ่งยากมากเลย เนื่องจาก ไม่มี repository อยู่ใน maven ทำให้เวลาติดตั้งจริงๆ ต้องก็อปไฟล์มาทั้งหมด แล้วตั้งค่าอยู่เยอะพอสมควร อีกทั้ง ใช้ได้แค่บน Android Studio เท่านั้น เนื่องจาก ทางผู้พัฒนาเขียนด้วย gradle script (ตัวนี้ผมใช้เวลาในการติดตั้ง อยู่เกือบ 2 ชม. แนะ error นุ้น นี่ นั่น เยอะแยะไปหมด หากใครลองเล่น แล้วติดปัญหา ก็ลองดูครับ เผื่อเจอปัญหาคล้ายๆกัน)
แนะนำว่าดูขั้นตอนการติดตั้งให้ละเอียดครับ พร้อมทั้งดู ต้นฉบับประกอบด้วย
Installation
ขั้นตอนการติดตั้ง เริ่มแรก ให้ clone repository ของเค้ามาเลยครับ (ใคร clone หรือไม่เคยใช้ git ก็กด ดาวน์โหลด zip ไฟล์) จากลิงค์นี้ครับ Paralloid-master.zip
แล้วก็อปไปวางไว้ในโฟลเดอร์ libs
ที่โปรเจ็คของเรา
cd path/to/Project/libs/
git clone https://github.com/chrisjenx/Paralloid.git paralloid
จะได้ File Tree ลักษณะแบบดังภาพข้างล่าง (โดย app
คือโมดูลหลักที่เราจะใช้เขียน)
ต่อมา เข้าไปที่ /libs/paralloid
ที่เพิ่ง clone มาเมื่อกี้ จะเห็น 3 โมดูลหลักๆ คือ paralloid
, paralloidexample
และ prralloidviews
ให้เปิดไฟล์ build.gradle
ของแต่ละโฟลเดอร์มาแก้ดังนี้
โดยทั้ง 3 ไฟล์ จะมีส่วนที่ต้องแก้เหมือนกันคือ
เปลี่ยนเวอร์ชันของ gradle plugin จาก
classpath 'com.android.tools.build:gradle:0.6.+'
เป็นเวอร์ชันล่าสุด
classpath 'com.android.tools.build:gradle:0.12.0'
เปลี่ยนชื่อ android plugin จาก
apply plugin: 'android-library'
เป็น
apply plugin: 'com.android.library'
เปลี่ยน Build Tool เวอร์ชัน จาก
buildToolsVersion "19.0.0"
เป็น เวอร์ชันล่าสุด
buildToolsVersion "20.0.0"
และเปลี่ยน Support Library เป็นเวอร์ชันที่ใหม่กว่า
dependencies {
compile "com.android.support:support-v4:18.0.+"
}
เป็น
dependencies {
compile "com.android.support:support-v4:20.0.+"
}
สำหรับแต่ละไฟล์ เมื่อแก้ไขแล้ว จะได้ดังนี้
Paralloid/build.gradle
ไฟล์ paralloid/build.gradle
นอกจากแก้ไขแบบด้านบนแล้ว ให้ลบบล็อก uploadArchives {}
ทิ้งไปด้วย
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:0.12.0'
}
}
apply plugin: 'com.android.library'
repositories {
mavenCentral()
}
android {
compileSdkVersion 19
buildToolsVersion "20.0.0"
defaultConfig {
minSdkVersion 7
targetSdkVersion 19
}
}
dependencies {
compile "com.android.support:support-v4:20.0.+"
}
Paralloidexample/build.gradle
ไฟล์ paralloidexample/build.gradle
แค่เปลี่ยนจาก
apply plugin: 'android'
เป็น
apply plugin: 'com.android.application'
สุดท้ายไฟล์จะได้ดังนี้
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:0.12.0'
}
}
apply plugin: 'com.android.application'
repositories {
mavenCentral()
}
android {
compileSdkVersion 18
buildToolsVersion "20.0.0"
defaultConfig {
minSdkVersion 14
targetSdkVersion 18
}
signingConfigs {
release {
// We can leave these in environment variables
storeFile file("keystore/paralloidkeystore.jks")
keyAlias "paralloid"
storePassword 'paralloid'
keyPassword 'paralloid'
}
}
buildTypes {
release {
signingConfig signingConfigs.release
}
}
}
dependencies {
compile 'com.android.support:appcompat-v7:20.0.+'
compile project(':paralloidviews')
}
Paraloidviews/build.gradle
ไฟล์ paraloidviews/build.gradle
ลบบล็อก uploadArchives {}
ทิ้งไปด้วย และก็เปลี่ยนจาก
compile project(':paralloid')
ให้เป็น
compile project(':libs:paralloid:paralloid')
แล้วก็เพิ่ม Support Library และลด minSDK ลงเหลือ 11 ก็จะเหลือแค่นี้
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:0.12.0'
}
}
apply plugin: 'com.android.library'
repositories {
mavenCentral()
}
android {
compileSdkVersion 19
buildToolsVersion "20.0.0"
defaultConfig {
minSdkVersion 11
targetSdkVersion 19
}
}
dependencies {
compile 'com.android.support:support-v4:20.0.0'
compile project(':libs:paralloid:paralloid')
}
ส่วนที่ต้องแก้เฉพาะที่ clone มาก็หมดแค่นี้ครับ ต่อมากลับมาไฟล์ Root Project ของเรา เลือกเปิด settings.gradle
ขึ้นมา ทำการ include library ที่เรา clone เมื่อกี้ลงไปด้วย
include ':app'
include ':libs:paralloid:paralloid'
include ':libs:paralloid:paralloidviews'
ต่อมา ไปที่ โมดูลหลัก (app) เลือกไฟล์ app/build.gradle
แล้วก็เพิ่ม compile project
เข้าไป แบบนี้
apply plugin: 'com.android.application'
android {
...
}
dependencies {
...
compile project(':libs:paralloid:paralloid')
compile project(':libs:paralloid:paralloidviews')
}
สุดท้าย Sync Project
ทดสอบรัน Sample ดูก่อน หากใครมีปัญหา error gradle failed ในลักษณะนี้
UNEXPECTED TOP-LEVEL EXCEPTION:
com.android.dex.DexException: Multiple dex files define Luk/co/chrisjenx/paralloid/BuildConfig;
at com.android.dx.merge.DexMerger.readSortableTypes(DexMerger.java:594)
at com.android.dx.merge.DexMerger.getSortedTypes(DexMerger.java:552)
at com.android.dx.merge.DexMerger.mergeClassDefs(DexMerger.java:533)
at com.android.dx.merge.DexMerger.mergeDexes(DexMerger.java:170)
at com.android.dx.merge.DexMerger.merge(DexMerger.java:188)
at com.android.dx.command.dexer.Main.mergeLibraryDexBuffers(Main.java:439)
at com.android.dx.command.dexer.Main.runMonoDex(Main.java:287)
at com.android.dx.command.dexer.Main.run(Main.java:230)
at com.android.dx.command.dexer.Main.main(Main.java:199)
at com.android.dx.command.Main.main(Main.java:103)
ให้เปิดไฟล์ app/build.gradle
ภายในบล็อก android
เพิ่มนี้ลงไป
dexOptions {
preDexLibraries = false
}
แล้ว Sync ใหม่ หรือ clean project -> restart อีกซักรอบ น่าจะหาย
เห็นมั้ยครับ แค่การ Install ก็ยุ่งยากมากแล้ว แต่ตอนนำไปใช้นี่ ไม่ยากเท่าไหร่นะครับ :D
Create Sample Project
ผมจะลองใช้ 2 แบบคือ Parallax แบบ ListView กับ Parallax แบบ Background นะครับ
เริ่มแรก ผมสร้างไฟล์ ParallaxListActivity
ขึ้นมา ได้หน้าตาแบบนี้
package com.devahoy.learn30androidlibraries.day2;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.widget.ListView;
import android.widget.SimpleAdapter;
import com.devahoy.learn30androidlibraries.R;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import uk.co.chrisjenx.paralloid.Parallaxor;
public class ParallaxListActivity extends ActionBarActivity {
private ListView mListView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.day2_parallax_list_view);
mListView = (ListView) findViewById(R.id.list_view);
List<Map<String, String>> maps = new ArrayList<Map<String, String>>(50);
Map<String, String> map;
for (int i = 0; i < 50; i++) {
map = new HashMap<String, String>();
map.put("text", "Example Text " + i);
maps.add(map);
}
SimpleAdapter adapter = new SimpleAdapter(this, maps,
android.R.layout.simple_list_item_1,
new String[]{"text"},
new int[]{android.R.id.text1});
mListView.setAdapter(adapter);
if (mListView instanceof Parallaxor) {
((Parallaxor) mListView).parallaxViewBackgroundBy(mListView, getResources().getDrawable(R.drawable.day2_example_rainbow), .25f);
}
}
}
ด้านบน ทำคล้ายๆกับ เราสร้าง ListView ทั่วๆไป คือมี data แล้วก็สร้าง SimpleAdapter
ด้วยข้อมูลที่เรา gen ขึ้น โดยใช้ layout แบบ simple_list_item_1
(TextView อันเดียว) จากนั้นก็ setAdapter()
ให้กับ ListView
สุดท้าย ความมหัศจรรย์ มันอยู่ตรง interface Parallaxor ครับ โดย implement เมธอด parallaxViewBackgroundBy()
ซึ่งส่ง parameters 3 ค่าคือ ListView, รูปภาพที่จะใช้เป็น background และก็อัตราส่วนที่จะให้มันเคลื่อนที่
parameter ตัวสุดท้าย หากค่า เป็น 1.0f คือ ถ้า 0.5f ก็จะเคลื่อนที่ ครึ่งหนึ่งของระยะทาง ของ View อย่างตัวอย่าง 0.25f ก็เคลื่อนที่ 1/4
สุดท้ายจะเห็นว่ามันมี error อยู่ตรง ไม่รู้จัก รูปภาพ ก็ไปก็อบเอาจาก parallaxexample
ละกันครับ หรือลิงค์นี้ และก็ไม่รู้จัก layout day2_parallax_list_view
ก็แน่นอนซิครับ ยังไม่ได้สร้าง
ก็ทำการสร้าง layout นี่เลยครับ ตามโค๊ดด้านล่าง
<uk.co.chrisjenx.paralloid.views.ParallaxListView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/list_view"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
ใช้ ParallaxListView
ก็คล้ายๆ ListView เพียงแต่ว่ามัน implement Parallaxor นี่แหละ
สุดท้ายลองรันดูครับ ผลลัพธ์จะได้ประมาณนี้
ทีนี้ยังไม่หนำใจ ผมลองสร้างมาอีกตัวอย่างหนึ่ง สมมติให้ชื่อว่า ParallaxBackgroundActivity
โค๊ดเป็นแบบนี้
package com.devahoy.learn30androidlibraries.day2;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import com.devahoy.learn30androidlibraries.R;
import uk.co.chrisjenx.paralloid.views.ParallaxScrollView;
public class ParallaxBackgroundActivity extends ActionBarActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.day2_parallax_background);
ParallaxScrollView scrollView = (ParallaxScrollView) findViewById(R.id.scroll_view);
scrollView.parallaxViewBackgroundBy(scrollView, getResources().getDrawable(R.drawable.day2_example_image), .2f);
}
}
คราวนี้ไม่ใช้ ListView แล้ว แต่จะใช้ ParallaxScrollView
ซึ่งมันเป็น ScrollView ชนิดนึง แล้วก็เรียกเมธอด parallaxViewBackgroundBy
ส่งค่า ParallaxScrollView, รูป background ที่จะใช้ แล้วก็อัตราการเคลื่อนที่เหมือนเดิม
ส่วนไฟล์ day2_parallax_background.xml
ก็สร้างดังนี้
<uk.co.chrisjenx.paralloid.views.ParallaxScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/scroll_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView style="@style/FixedHeightBox"/>
<TextView style="@style/FixedHeightBox"/>
<TextView style="@style/FixedHeightBox"/>
<TextView style="@style/FixedHeightBox"/>
<TextView style="@style/FixedHeightBox"/>
<TextView style="@style/FixedHeightBox"/>
<TextView style="@style/FixedHeightBox"/>
<TextView style="@style/FixedHeightBox"/>
</LinearLayout>
</uk.co.chrisjenx.paralloid.views.ParallaxScrollView>
ด้านบนเป็นการใช้ ParallaxScrollView
ครอบ TextView ทั้งหมดเลย ส่วน TextView ก็ setStyle()
จากไฟล์ res/values/styles.xml
ดังนี้
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
</style>
<style name="FixedHeightBox">
<item name="android:background">@android:color/transparent</item>
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:minHeight">200dp</item>
<item name="android:layout_margin">16dp</item>
<item name="android:gravity">left|center_vertical</item>
<item name="android:padding">16dp</item>
<item name="android:text">@string/lorem_ipsum</item>
<item name="android:textColor">#ffffff</item>
</style>
</resources>
ไฟล์ res/values/strings.xml
ใช้ lorem ipsum แบบนี้ ฮ่าๆ
<string name="lorem_ipsum">ศิลปวัฒนธรรมฮาลาลซาตานท็อปบูต ไทเฮาพลานุภาพอริยสงฆ์ ภควัทคีตาโบรกเกอร์คาสิโนอัลบัมตัวเอง สึนามิเสือโคร่งนินจา ออยล์ ชิฟฟอนยังไงโง่เขลาแคมปัสทอร์นาโด วาทกรรมมาร์เก็ตติ้งโอเพ่นเสือโคร่ง เรซิ่น โดมิโน สี่แยกสคริปต์ คอนแทคฮิปโปสตาร์ทบ็อกซ์แชมป์ แหม็บไชน่าเทควันโดไอซียู รีวิว มอยส์เจอไรเซอร์ยะเยือกอพาร์ทเมนท์ เพลซไกด์ไฮเอนด์เทอร์โบเอ๊าะ ซาร์ดีนบ็อกซ์
แอ็กชั่น แอพพริคอทบ๋อยโปรเจกเตอร์วาไรตี้ เฟิร์มบร็อกโคลีเช็งเม้งละอ่อนดีมานด์ โพลารอยด์เป่ายิ้งฉุบคอนเฟิร์มโปรดิวเซอร์สเตอริโอ ฮากกาเทคโนแรงดูด คอนโดมิเนียมซูเอี๋ยพาสตาพลานุภาพ ปิยมิตรผลักดัน กาญจนาภิเษกแชมเปี้ยนสตรอว์เบอร์รี รีโมตสไตล์ซันตาคลอสซีอีโอรูบิก พาสต้า แซวมินต์แบคโฮอีสต์อีสต์ เมคอัพเจ๊ปาสคาลบอกซ์ เธคเพาเวอร์วิว อินเตอร์แทกติค สหรัฐพาวเวอร์ชิฟฟอนฟีเวอร์ ป๋อหลอตุ๊กไมเกรน</string>
<string name="action_parallax">ParallaxBG</string>
สุดท้าย ผมเพิ่มปุ่ม menu โดยการแก้คลาส ParallaxListActivity
เพื่อเมื่อคลิกเมนู ก็ให้เปิด ParallaxBackgroundActivity
แบบนี้
public class ParallaxListActivity extends ActionBarActivity {
private ListView mListView;
@Override
protected void onCreate(Bundle savedInstanceState) {
...
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
Intent intent = new Intent(this, ParallaxBackgroundActivity.class);
startActivity(intent);
return super.onOptionsItemSelected(item);
}
}
ไฟล์ res/menu/main.xml
คือ
<menu 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"
tools:context=".MainActivity">
<item
android:id="@+id/action_settings"
android:title="@string/action_parallax"
app:showAsAction="always"/>
</menu>
สุดท้ายเพิ่ม Activity ในไฟล์ AndroidManifest.xml ด้วย
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.devahoy.learn30androidlibraries" >
<uses-permission android:name="android.permission.INTERNET" />
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name=".day2.ParallaxListActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".day2.ParallaxBackgroundActivity" />
</application>
</manifest>
เมื่อทดสอบ ผลลัพธ์ได้แบบนี้
สรุป
Library ตัวนี้น่าสนใจดีครับ ตรงที่มันเป็น Parallax นี่แหละ ปกติเคยทำแต่บนเว็บ ไม่เคยลองใช้ในแอพ Android เลย แล้วก็อุปสรรคในการใช้งานเจ้าตัวนี้คือ ความยากในการติดตั้งครับ กินเวลาหลายชม.ทีเดียว ใครสนใจ Parallax ลองโหลดไปใช้ และทดสอบดูกันนะครับ
- Authors
- Name
- Chai Phonbopit
- Website
- @Phonbopit