การสร้าง Custom View

หลังจากจัดการกับปัญหา Hard Disk  ตัวเองแล้วก็คิดอยู่นานว่าจะเขียนเรื่องอะไรดี อยากจะเขียนหลายๆเรื่อง แต่คิดว่าคงยากที่จะอธิบาย ยิ่งอธิบายน่าจะยิ่งงง เลยมาตกที่ Custom View หรือคือการสร้าง View ของตัวเองขึ้นมาโดยอาจจะสร้างเป็น library เพื่อเก็บไว้ใช้ภายหลัง หรือว่าอยากได้ view ที่ไม่มีอยู่ใน view มาตรฐาน ในตัวอย่างนี้จะมาลองทำ Number Picker กัน โดยเจ้า Number Picker นี้มีอยู่ใน api level 11 (android 3) ขึ้นไป ในตอนที่ผมเริ่มเขียนมีเครื่อง test เป็น android 2.2 ต้องหาโหลด widget ของที่อื่นมาใช้ ซึ่งบางทีมันก็ไม่ได้ตรงกับที่เราต้องการเท่าไหร่นัก มาลองกันเลยครับ หน้าตาอาจจะธรรมดาไปหน่อยนะครับ



custom_number_picker.xml อันนี้จะเป็นหน้าตา view ของเราโดยผมสร้าง Button ขั้นมาสองตัวใช้เป็นปุ่ม ลบ บวก และมี textView ไว้แสดงผลหนึ่งตัว
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:orientation="horizontal"
    >
    <Button 
        android:id="@+id/btnMinus"
        android:layout_width="35dp"
        android:layout_height="wrap_content"
        android:text="-"
        android:textSize="18sp"
        />
    
    <TextView 
        android:id="@+id/tvNumber"
        android:layout_width="25dp"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:textSize="18sp"
        android:text="00"
        />
    
    <Button 
        android:id="@+id/btnPlus"
        android:layout_width="35dp"
        android:layout_height="wrap_content"
        android:text="+"
        android:textSize="18sp"
        />
    
</LinearLayout>
CustomNumberPicker.java ในไฟล์นี้จะเป็นการเขียนการทำงานของ view เราครับใน method init ทำการ inflater ไฟล์ layout ที่เราสร้างด้านบนเข้ามาจากนั้นทำการกำหนดค่าเริ่มต้นและ set event listener ให้กับปุ่ม โดยใน class นี้จะมีตัวแปรชื่อ currentNumber อยู่โดยเจ้าตัวนี้จะมีค่าเท่ากับ textview ที่เราใช้แสดงผล เมื่อทำการเพิ่มหรือลบก็จะทำการเปลี่ยนแปลงค่าตัวแปรนี้ด้วยจากนั้นถึงอัพเดทค่าใน TextView แล้วก็มี method getNumber เพื่ออ่านค่าปัจจุบันของ View เราด้วยครับ
package com.customview;

import android.content.Context;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.TextView;

public class CustomNumberPicker extends LinearLayout{

 private Context mCtx;
 private TextView tvNumber; 
 private int currentNumber;
 
 public CustomNumberPicker(Context context){
  super(context);
  mCtx = context;
  init();
 }
 
 public CustomNumberPicker(Context context, AttributeSet attrs) {
  super(context, attrs);
  mCtx = context;
  init();
 }
 
 private void init(){
  LayoutInflater inflater = (LayoutInflater) mCtx.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
  inflater.inflate(R.layout.custom_number_picker, this);
  tvNumber = (TextView) findViewById(R.id.tvNumber);
  ((Button) findViewById(R.id.btnPlus)).setOnClickListener(btnPlusOnClickListener);
  ((Button) findViewById(R.id.btnMinus)).setOnClickListener(btnMinusOnClickListener);
  
  currentNumber = 0;
  tvNumber.setText(Integer.toString(currentNumber));  
 }

 private View.OnClickListener btnMinusOnClickListener = new View.OnClickListener() {
  
  @Override
  public void onClick(View v) {
   currentNumber--;
   tvNumber.setText(Integer.toString(currentNumber));
  }
 };
 
 private View.OnClickListener btnPlusOnClickListener = new View.OnClickListener() {
  @Override
  public void onClick(View v) {
   currentNumber++;
   tvNumber.setText(Integer.toString(currentNumber));   
  }
 };
 
 public int getNumber(){
  return currentNumber;
 }
 
}
การทำ CustomView แบบง่ายๆก็มีแค่นี้แหละครับ ทีนี้เราลองไปดูการใช้งานบ้างครับ
main.xml ในไฟล์นี้จะเห็นว่ามี View com.customview.CustomNumberPicker ของเราอยู่
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    >
    <TextView 
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Select Number"
        android:gravity="center"
        android:textSize="20sp"
        android:textColor="#FF0000"
        android:layout_marginTop="20dp"
        android:layout_marginBottom="20dp"
        />
    <com.customview.CustomNumberPicker
        android:id="@+id/numberPicker"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        />
    <Button 
        android:id="@+id/btnSelect"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Select!"
        android:textSize="18sp"
        android:textColor="#0000FF"
        android:layout_gravity="center"
        android:layout_marginTop="20dp"
        />
</LinearLayout>
MainActivity.java ทีนี้เข้าดูใน code กันบ้าง เรียกใช้งานเหมือน View ปกติเลยครับ กำหนดตัวแปรเป็นชนิด CustomNumberPicker จากนั้นก็กำหนด reference เมื่อผู้ใช้คลิกที่ปุ่ม เราก็เรียกใช้ method getNumber ที่เราสร้างไว้เพื่อรับค่าไปใช้งานต่อ
package com.customview;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;

public class MainActivity extends Activity {

 private CustomNumberPicker numberPicker;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        numberPicker = (CustomNumberPicker) findViewById(R.id.numberPicker);
        ((Button) findViewById(R.id.btnSelect)).setOnClickListener(btnSelectOnClickListener);
    }
    
    private View.OnClickListener btnSelectOnClickListener = new View.OnClickListener() {
  
  @Override
  public void onClick(View v) {
   Toast.makeText(MainActivity.this, "you select " + Integer.toString(numberPicker.getNumber()), Toast.LENGTH_SHORT).show();
  }
 };
    
}
ก็ประมาณนี้แหละครับการสร้าง CustomView ในตัวอย่างนี้อาจจะมีรายละเอียดน้อยไป โดยเราสามารถที่จะสร้าง custom attributes ของเราขึ้นมาเองได้เช่น min_number, max_number, step เป็นต้น แม้แต่ในตอนที่ผู้ใช้คลิกปุ่มเพิ่มหรือลบเรายังสามารถนำ Interface มาประยุกต์เพื่อสร้าง callback ได้ ลองเอาไปประยุกต์ใช้กันดูครับ