29 ธันวาคม 2557

[Android Code] Let's Fragment - ใส่ Indicator ให้กับ View Pager [ภาคสอง]



        จากเดิมที่เจ้าของบล็อกพูดถึงเรื่อง Indicator สำหรับ View Pager โดยใช้ไลบรารีของ Jake Wharton ไปแล้ว คราวนี้เจ้าของบล็อกขอพูดถึงไลบรารีที่น่าสนใจสำหรับการทำ Indicator อีกหนึ่งตัวที่มีชื่อว่า Circle Indicator ของ Ongakuer [GitHub] ที่สามารถ Custom Indicator เพิ่มเติมได้

        โดยไลบรารีตัวนี้มีจุดเด่นตรงที่สามารถกำหนด Animator ให้กับ Indicator ได้ เมื่อ View Pager เกิดการเปลี่ยน Fragment


        จะเห็นว่า Indicator จะมี Transition เมื่อเปลี่ยน Fragment โดย Indicator ของตำแหน่งเก่าก็จะหดเล็กลง ส่วน Indicator ของปัจจุบันก็จะขยายใหญ่ขึ้น ซึ่งตรงนี้จะใช้ Object Animator ในการกำหนด Transition ดังกล่าว ถ้าผู้ที่หลงเข้ามาอ่านคนใดยังไม่รู้จักก็ไปศึกษาก่อนได้ที่ [Android Code] มาทำความรู้จักกับ Object Animator กันดีกว่า~!

        โดยวีดีโอตัวอย่างที่เห็นนี้เป็น Animator พื้นฐานที่ไลราบรีมีไว้ให้ใช้งานทันที และสามารถสร้างเพื่อใช้งานได้ตามใจชอบด้วยเช่นกัน (เจ๋งตรงนี้แหละ)

        ก่อนอื่นเลยให้ Import เจ้า Circle Indicator เข้ามายังโปรเจคที่จะเรียกใช้งานซะ

        วิธีการ Import Library บน Eclipse ADT
        วิธีการ Import Librayr บน Android Studio

        ถ้าใช้ Gradle สามารถดาวน์โหลดผ่าน Remote Dependencies ได้เลย

compile 'com.nineoldandroids:library:2.4.+'
compile 'me.relex:circleindicator:1.0.0@aar'


        สำหรับการใช้งานเบื้องต้นนั้นจะเหมือนกับ ViewPagerIndicator เป๊ะๆ แต่ว่ามีคำสั่งเพิ่มเติมเข้ามาอีกหน่อย ถ้างั้นขอยกตัวอย่างการใช้งานพื้นฐานก่อนนะ

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="${relativePackage}.${activityClass}" >

    <android.support.v4.view.ViewPager
        android:id="@+id/pager"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical" />

    <me.relex.circleindicator.CircleIndicator
        android:id="@+id/indicator"
        android:layout_width="match_parent"
        android:layout_height="30dp" />

</RelativeLayout>

        สำหรับ CircleIndicator จะต้องมีการกำหนดความสูง ซึ่งแนะนำให้กำหนดประมาณ 30-40dp

        ส่วนการทำงานของโค๊ดก็จะเหมือนกัน แต่จะใช้คลาส CircleIndicator แทน

ViewPager pager = (ViewPager) findViewById(R.id.pager);
pager.setAdapter(adapter);

CircleIndicator indicator = (CircleIndicator) findViewById(R.id.indicator);
indicator.setViewPager(pager);

        ก็จะได้ Indicator ออกมาเหมือนกับวีดีโอตัวอย่างในตอนแรกนั่นเอง


        และสำหรับการ Custom Indicator ให้ทำใน Layout XML นั้นๆเลย โดยจะมี Attribute เฉพาะดังนี้

        • app:ci_width ความกว้างของ Indicator แต่ละตัว
        • app:ci_height ความสูงของ Indicator แต่ละตัว
        • app:ci_margin ระยะ Margin ระหว่าง Indicator แต่ละตัว
        • app:ci_animator Animator ที่จะให้กระทำเมื่อเปลี่ยน Indicator
        • app:ci_drawable ภาพของ Indicator

        ดังนั้นการกำหนดใน Layout XML ก็จะออกมาเป็นแบบนี้

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="${relativePackage}.${activityClass}" >

    <android.support.v4.view.ViewPager
        android:id="@+id/pager"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical" />

    <me.relex.circleindicator.CircleIndicator
        android:id="@+id/indicator"
        android:layout_width="match_parent"
        android:layout_height="30dp"
        app:ci_width="20dp"
        app:ci_height="4dp"
        app:ci_margin="6dp" />

</RelativeLayout>

        จะเห็นว่าเจ้าของบล็อกกำหนด Indicator กว้าง 20dp สูง 4dp และอยู่ห่างกัน 6dp เมื่อลองทดสอบแอพฯดูก็จะได้แบบนี้


        ลองใส่ภาพเข้าไปแทน โดยที่เก็บไฟล์ภาพไว้ที่ drawable ตามปกติ


        ภาพตัวอย่างขอใส่พื้นหลังสีเทานะ เพราะวงกลมตรงกลางเป็นสีขาว เดี๋ยวมันจะกลืนไปกับพื้นหลังของบทความนี้ แต่ของจริงพื้นหลังโปร่งใสนะ

        โดยภาพนี้ตั้งชื่อไฟล์ว่า ic_indicator.png แล้วเก็บไว้ใน drawable



       กลับมาที่ Layout XML ก็ต้องเพิ่มใหม่เป็นดังนี้

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="${relativePackage}.${activityClass}" >

    <android.support.v4.view.ViewPager
        android:id="@+id/pager"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical" />

    <me.relex.circleindicator.CircleIndicator
        android:id="@+id/indicator"
        android:layout_width="match_parent"
        android:layout_height="40dp"
        app:ci_width="20dp"
        app:ci_height="20dp"
        app:ci_margin="10dp"
        app:ci_drawable="@drawable/ic_indicator" />

</RelativeLayout>

        พอลองทดสอบดูก็จะได้ Indicator เป็นรูปที่ทำไว้นั่นเอง


        อ๊ะๆ ความสนุกยังไม่จบลงเพียงเท่านี้ เพราะยังเหลือ Animator ให้เล่นอีกนะ

        เจ้าของบล็อกลองสร้าง Animator ขึ้นมาดังนี้


anim_indicator.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:ordering="together">

    <objectAnimator
        android:propertyName="alpha"
        android:duration="@android:integer/config_shortAnimTime"
        android:valueType="floatType"
        android:valueFrom="0.2"
        android:valueTo="1.0"/>

    <objectAnimator
        android:propertyName="scaleX"
        android:duration="@android:integer/config_shortAnimTime"
        android:valueType="floatType"
        android:valueFrom="1"
        android:valueTo="1.5"/>

    <objectAnimator
        android:propertyName="scaleY"
        android:duration="@android:integer/config_shortAnimTime"
        android:valueType="floatType"
        android:valueFrom="1"
        android:valueTo="1.5"/>


    <objectAnimator
        android:propertyName="rotation"
        android:duration="@android:integer/config_shortAnimTime"
        android:valueType="floatType"
        android:valueFrom="0"
        android:valueTo="90"/>


</set>

        จะเห็นว่ามีการกำหนด Object Animator ทั้งหมด 4 ชุดด้วยกัน

        • Alpha ให้ปรับความโปร่งใสของวัตถุจาก 0.2 ไปเป็น 1.0
        • Scale X ปรับความกว้างของวัตถุจาก 1 เท่าไปเป็น 1.5 เท่า
        • Scale Y ปรับความสูงของวัตถุจาก 1 เท่าไปเป็น 1.5 เท่า
        • Rotation หมุนวัตถุจาก 0 องศาไปที่ 90 องศา

        แล้วเอามากำหนดใน Layout XML ให้กับ Circle Indicator ดังนี้

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="${relativePackage}.${activityClass}" >

    <android.support.v4.view.ViewPager
        android:id="@+id/pager"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical" />

    <me.relex.circleindicator.CircleIndicator
        android:id="@+id/indicator"
        android:layout_width="match_parent"
        android:layout_height="40dp"
        app:ci_width="20dp"
        app:ci_height="20dp"
        app:ci_margin="10dp"
        app:ci_drawable="@drawable/ic_indicator"
        app:ci_animator="@animator/anim_indicator" />

</RelativeLayout>


        เมื่อลองทดสอบดูก็จะเห็นว่า Indicator มี Animation ตามที่เจ้าของบล็อกสร้างขึ้นมา



        อ้าว!? กำหนดแต่ Animator ตอนที่ Indicator ไม่ถูกเลือก >> ถูกเลือก เท่านั้นนี่? แล้วตอนที่ Indicator ถูกเลือก >> ไม่ถูกเลือก ไปกำหนดตรงไหนหรือ?

        สำหรับกรณีดังกล่าวไลบรารีตัวนี้จะใช้วิธี Reverse Animator ให้โดยอัติโนมัติ ดังนั้นไม่ต้องมานั่งกำหนดเพิ่มเติมให้วุ่นวาย และก็หมายความว่าไม่สามารถกำหนดแตกต่างไปจาก Animator ตัวนั้นๆด้วยเช่นกัน (แต่ก็ไม่จำเป็นนั่นแหละ)


        และสำหรับภาพ Indicator ที่ใช้ อย่าลืมว่าสามารถเอา Shape มาทำได้เหมือนกันนะ ดังนั้นลองสร้าง Shape ขึ้นมาเลย


shape_round_corner.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">

    <solid android:color="#FFFFFF" />

    <corners android:radius="30dp" />

</shape>

        เป็น Shape สี่เหลี่ยมที่มีมุมมน 30dp และใช้สีขาว


        ขอแก้ไข Animator ให้เข้ากับ Shape ตัวนี้เสียหน่อย โดยให้ Indicator ปรับความโปร่งใสและหมุน 90 องศา

anim_indicator.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:ordering="together">

    <objectAnimator
        android:propertyName="alpha"
        android:duration="@android:integer/config_shortAnimTime"
        android:valueType="floatType"
        android:valueFrom="0.2"
        android:valueTo="1.0"/>

    <objectAnimator
        android:propertyName="rotation"
        android:duration="@android:integer/config_shortAnimTime"
        android:valueType="floatType"
        android:valueFrom="0"
        android:valueTo="90"/>

</set>


        ที่สำคัญอย่าลืมแก้ไข Drawable ที่อยู่ใน Layout XML ด้วย

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#f2f2f2"
    tools:context="${relativePackage}.${activityClass}" >

    <android.support.v4.view.ViewPager
        android:id="@+id/pager"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical" />

    <me.relex.circleindicator.CircleIndicator
        android:id="@+id/indicator"
        android:layout_width="match_parent"
        android:layout_height="40dp"
        app:ci_width="15dp"
        app:ci_height="5dp"
        app:ci_margin="5dp"
        app:ci_drawable="@drawable/shape_round_corner"
        app:ci_animator="@animator/anim_indicator"/>

</RelativeLayout>
 
        โดยเจ้าของบล็อกได้กำหนดให้ Indicator ยาว 15dp สูง 5dp และห่างกัน 5dp ด้วย จะได้ Indicator ที่มีลักษณะเป็นแท่งยาวปลายมน

         มาดูผลลัพธ์กันเลย~!


        ซึ่งเจ้าของบล็อกก็คิดว่า Shape เหมาะแก่การทำ Indicator มากกว่า Drawable เสียอีก เพราะ Indicator ที่ดีไม่จำเป็นต้องภาพสวยเวอร์ แต่มองแล้วรู้ว่าเป็น Indicator โดยที่ไม่ไปรบกวน Content ที่อยู่บน Fragment

        จงอย่าลืมหน้าที่ของ Indicator ว่ามันมีไว้ทำอะไร จำเป็นต้องสวยเพื่อดึงดูดสายตาหรือป่าว?

        ที่เหลือก็อยู่ที่ผู้ที่หลงเข้ามาอ่านแล้วล่ะว่าจะทำ Indicator ให้ออกมามีลักษณะแบบไหน แต่ถ้าเน้นใช้ง่ายและดูเหมาะสมก็คงเป็นแบบเริ่มต้นที่ไลบรารีเค้าทำไว้ให้แล้วนั่นแหละ




เหล่าพันธมิตรแอนดรอยด์

Devahoy Layer Net NuuNeoI The Cheese Factory Somkiat CC Mart Routine Artit-K Arnondora Kamonway Try to be android developer Oatrice Benz Nest Studios Kotchaphan@Medium Jirawatee@Medium Travispea@Medium