Android – Thành phần tùy chỉnh (Custom components)



Custom Component là kết quả của việc tự viết Component theo ý bạn dựa trên các Component có sẵn bằng cách Extend lớp con từ các lớp đã định nghĩa sẵn.

Android cung cấp khá nhiều các Widget xây dựng sẵn như Button, TextView, EditText, ListView, CheckBox, RadioButton, Gallery, Spinner, AutoCompleteTextView… Đó là những Widget bạn có thể sử dụng trong ứng dụng Android khi lập trình. Tuy nhiên, có những trường hợp chúng không đáp ứng được nhu cầu của người lập trình. Vì vậy, Android cho phép lập trình viên tạo các Component theo ý muốn, hay còn gọi là Custom Component dựa trên các Component đã xây dựng sẵn. Thông thường, việc này chỉ đơn giản bằng cách tạo lớp con của Widget hay Layout có sẵn, viết đè các phương thức của nó để điều chỉnh giao diện và tính năng theo ý mình.

Ví dụ tạo một Custom Component đơn giản

Bước Mô tả
1 Dùng IDE tạo một ứng dụng đặt tên là compoundview dưới package com.vogella.android.customview.compoundview.
2 Tạo file XML res/values/attrs.xml để xác định các thuộc tính mới và kiểu dữ liệu của chúng.
3 Tạo file src/ColorOptionsView.java và thêm code để định nghĩa một Custom Component mới. Custom Component này Extend lớp LinearLayout và sẽ có thêm một số tính năng mới. Bạn sẽ cung cấp thuộc tính parsing logic trong Constructor có tham số là AttributeSet.
4 Chỉnh sửa file res/layout/activity_main.xml để thêm code tạo Instance của một View có tên là Colour, trong Instance này sẽ có các thuộc tính mặc định và các thuộc tính mới.
5 Chạy ứng dụng trên Android Emulator và kiểm tra kết quả.

Tạo file chứa các thuộc tính mới attrs.xml trong thư mục res/values.

<?xml version="1.0" encoding="utf-8"?>
<resources>
   <declare-styleable name="Options">
      <attr name="titleText" format="string" localization="suggested" />
      <attr name="valueColor" format="color" />
   </declare-styleable>
</resources>

Thay đổi file Layout như sau:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:tools="http://schemas.android.com/tools"
   xmlns:custom="http://schemas.android.com/apk/res/com.vogella.android.view.compoundview"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   android:orientation="vertical"
   android:showDividers="middle"
   android:divider="?android:attr/listDivider"
   tools:context=".MainActivity" >
   
   <com.vogella.android.view.compoundview.ColorOptionsView
      android:id="@+id/view1"
      android:layout_width="match_parent"
      android:layout_height="?android:attr/listPreferredItemHeight"
      android:background="?android:selectableItemBackground"
      android:onClick="onClicked"
      custom:titleText="Background color"
      custom:valueColor="@android:color/holo_green_light"/>

   <com.vogella.android.view.compoundview.ColorOptionsView
      android:id="@+id/view2"
      android:layout_width="match_parent"
      android:layout_height="?android:attr/listPreferredItemHeight"
      android:background="?android:selectableItemBackground"
      android:onClick="onClicked"
      custom:titleText="Foreground color"
      custom:valueColor="@android:color/holo_orange_dark"/>

</LinearLayout>

Tạo file java đặt tên là ColorOptionsView cho Compound View mới.

package com.vogella.android.customview.compoundview;

import com.vogella.android.view.compoundview.R;

import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;

public class ColorOptionsView extends LinearLayout {
   private View mValue;
   private ImageView mImage;
   
   public ColorOptionsView(Context context, AttributeSet attrs) {
      super(context, attrs);
      
      TypedArray a = context.obtainStyledAttributes(attrs,R.styleable.ColorOptionsView, 0, 0);
      String titleText = a.getString(R.styleable.ColorOptionsView_titleText);
      
      int valueColor = a.getColor(R.styleable.ColorOptionsView_valueColor,android.R.color.holo_blue_light);
      a.recycle();
      
      setOrientation(LinearLayout.HORIZONTAL);
      setGravity(Gravity.CENTER_VERTICAL);
      
      LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
      inflater.inflate(R.layout.view_color_options, this, true);
      TextView title = (TextView) getChildAt(0);
      title.setText(titleText);
      
      mValue = getChildAt(1);
      mValue.setBackgroundColor(valueColor);
      mImage = (ImageView) getChildAt(2);
   }
   
   public ColorOptionsView(Context context) {
      this(context, null);
   }
   
   public void setValueColor(int color) {
      mValue.setBackgroundColor(color);
   }
   
   public void setImageVisible(boolean visible) {
      mImage.setVisibility(visible ? View.VISIBLE : View.GONE);
   }
} 

Thay đổi file Activity chính như sau:

package com.vogella.android.customview.compoundview;

import com.vogella.android.view.compoundview.R;

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

public class MainActivity extends Activity {
   @Override
   protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.activity_main);
   }
   
   @Override
   public boolean onCreateOptionsMenu(Menu menu) {
      // Inflate the menu; this adds items to the action bar if it is present.
      getMenuInflater().inflate(R.menu.activity_main, menu);
      return true;
   }
   
   public void onClicked(View view) {
      String text = view.getId() == R.id.view1 ? "Background" : "Foreground";
      Toast.makeText(this, text, Toast.LENGTH_SHORT).show();
   }
} 

Khi bạn chạy ứng dung, kết quả sẽ giống như hình bên dưới:

Custom

Tạo Instance dùng code bên trong lớp Activity

Tạo một Instance của Custom Widget cũng tương tự như cách mà bạn tạo một Instance của Widget đã xây dựng sẵn trong Activity. Hãy xem ví dụ sau:

@Override
protected void onCreate(Bundle savedInstanceState) {
   super.onCreate(savedInstanceState);
   setContentView(R.layout.activity_main);
   
   DateView dateView = new DateView(this);
   setContentView(dateView);
}

Tạo Instance dùng File Layout XML

Thông thường bạn dùng File Layout XML tạo một Instance của Widget có sẵn, ta cũng làm tương tự với Custom Widget. Ở đây com.example.compoundview là package mà bạn muốn dùng code liên quạn đến lớp DateViewDateView là một lớp java nơi mà bạn đặt các code cho Custom Component của mình.

<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"
   android:paddingBottom="@dimen/activity_vertical_margin"
   android:paddingLeft="@dimen/activity_horizontal_margin"
   android:paddingRight="@dimen/activity_horizontal_margin"
   android:paddingTop="@dimen/activity_vertical_margin"
   tools:context=".MainActivity" >
   
   <com.example.compoundview.DateView
      android:layout_width="match_parent"
      android:layout_height="wrap_content" 
      android:textColor="#fff"
      android:textSize="40sp"
      android:background="#000"/>
</RelativeLayout>

Custom Component với Custom Attribute

Trong ví dụ sau chúng ta sẽ tạo các Custom Attribute mới và sử dụng chúng trong Custom Componnet mới.

<com.example.compoundview.DateView
   android:layout_width="match_parent"
   android:layout_height="wrap_content" 
   android:textColor="#fff"
   android:textSize="40sp"
   custom:delimiter="-"
   custom:fancyText="true"/>

Bước 1

Tạo file attrs.xml trong thư mục res/values/, nội dung file này như bên dưới:

<?xml version="1.0" encoding="utf-8"?>
<resources>
   <declare-styleable name="DateView">
   <attr name="delimiter" format="string"/>
   <attr name="fancyText" format="boolean"/>
   </declare-styleable>
</resources>

Here the name=value là tên của thuộc tính trong file Layout XML và format=type là kiểu của giá trị của thuộc tính.

Bước 2

Ở bước này ta sẽ đọc các thuộc tính mới này từ file Layout XML và thiết lập chúng cho Component. Logic này được đặt trong Constructor trong đó AttributeSet được truyền vào, AttributeSet chứa các thuộc tính XML. Để đọc các giá trị trong file XML, ta tạo một biến kiểu TypedArray từ AttributeSet, sau đó dùng nó để đọc và thiết lập giá trị cho thuộc tính như đoạn code bên dưới:

TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.DateView);

final int N = a.getIndexCount();
for (int i = 0; i < N; ++i)
{
   int attr = a.getIndex(i);
   switch (attr)
   {
      case R.styleable.DateView_delimiter:
      String delimiter = a.getString(attr);
      
      //...do something with delimiter...
      break;
      
      case R.styleable.DateView_fancyText:
      boolean fancyText = a.getBoolean(attr, false);
      
      //...do something with fancyText...
      break;
   }
}
a.recycle();

Bước 3

Cuối cùng bạn dùng các thuộc tính mới trong XML như sau:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:tools="http://schemas.android.com/tools"
   xmlns:custom="http://schemas.android.com/apk/res/com.example.compoundview"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   android:paddingBottom="@dimen/activity_vertical_margin"
   android:paddingLeft="@dimen/activity_horizontal_margin"
   android:paddingRight="@dimen/activity_horizontal_margin"
   android:paddingTop="@dimen/activity_vertical_margin"
   tools:context=".MainActivity" >
   
   <com.example.compoundview.DateView
      android:layout_width="match_parent"
      android:layout_height="wrap_content" 
      android:textColor="#fff"
      android:textSize="40sp"
      custom:delimiter="-"
      custom:fancyText="true"/>

</RelativeLayout>

Ở đây phần quan trọng là xmlns:custom=”http://schemas.android.com/apk/res/com.example.compoundview”. Hãy chú ý http://schemas.android.com/apk/res/ luôn không thay đổi, phần cuối sẽ thay đổi tùy vào tên package của bạn. phần nằm sau xmlns: bạn có thể dùng bất cứ chuỗi nào bạn muốn, ở đây tôi dùng từ custom.

839 Total Views 2 Views Today