12/08/2018, 13:19

TẠO CUSTOM COMPOUND VIEWS TRONG ANDROID

I. MỞ ĐẦU Đã khi nào bạn lập trình một ứng dụng Android mà ở đó bạn thường sử dụng một nhóm các views giống nhau tại nhiều layout khác nhau trong ứng dụng chưa? Ví dụ như cụm view chọn ngày mà bạn tự custom chẳng hạn. Tất nhiên bạn có thể làm bằng cách là tạo một layout gồm các view cần thiết và ...

I. MỞ ĐẦU

Đã khi nào bạn lập trình một ứng dụng Android mà ở đó bạn thường sử dụng một nhóm các views giống nhau tại nhiều layout khác nhau trong ứng dụng chưa? Ví dụ như cụm view chọn ngày mà bạn tự custom chẳng hạn. Tất nhiên bạn có thể làm bằng cách là tạo một layout gồm các view cần thiết và chỗ nào sử dụng thì include vào nhưng như vậy vẫn phải lặp lại đoạn code Java xử lý và nó không chuyên nghiệp tiện lợi cho lắm.

Trong Android có một cách giải quyết vấn đề này đó là tạo ra một custom view đóng gói toàn bộ layout cũng như logic của các views đó. Sau đó bạn chỉ cần sử dụng customview như một view bình thường của android thay vì lặp lại code trong project. Custom view đó được gọi là Conpound View. Trong bài này tôi với bạn cùng đi tìm hiểu cách tạo ra custom compound views và sử dụng lại chúng một cách dễ dàng. :like a boss:

II. KẾT QUẢ MONG MUỐN

Tôi và bạn cùng thử tạo một compound view bao gồm 5 nút [previous, play, pause, stop, next] thường thấy trong các úng dụng nghe nhạc. Tại sao lại có 5 nút thì bởi vì tôi kiếm được cái file PSD nó có 5 nút chứ thực ra chỉ cần 4 nút thôi, play với pause có thể gộp làm một mà. (facepalm)

Mong muốn thì nó là như thế này, trăm nghe không bằng một thấy, cho cái hình cho nó dễ hình dung. Nhìn khá là đẹp và hoành tráng đấy chứ nhỉ. Hình này tôi export từ file PSD ra đấy.

player.png

Nhưng mà đó là mong muốn thôi đó nhé chứ còn thực tế sản phẩm nó ra như thế nào thì hiện tại tôi cũng chưa biết, cũng có thể không chạy =)) Mong là nó được cỡ 50% như trên là tốt rồi :boss:.

III. BẮT ĐẦU

OK, bước đầu tiên cứ tạo một cái project trống rỗng trên Android Studio đã, làm gì thì tính sau. Tạo MainActivity.java sử dụng activity_main.xml làm layout. Bước này chắc là dễ nhất rồi, nói vậy thôi chứ hồi đầu học android tôi mất cả ngày mới tạo được project đấy (hihi).

Giao diện trên tôi kiếm đại lấy một file PSD trên freebiesbug.com tại đây. Bạn có thể tải về và tập cắt các nút bằng Photoshop để luyện cho tăng trình cắt dán. Nếu ai không thích tự cắt thì tôi có gửi kèm cả bộ resources sẵn rồi đó quăng vào drawable và code như một vị thần thôi không phải lo cắt dán làm gì cho mệt.

IV. TẠO LAYOUT CHO COMPOUND VIEW

Giả sử tôi và các bạn đều có những icon cần thiết từ file PSD trên kia rồi nhá, giờ đến đoạn viết code cho layout. Để tạo được CustomView như trên thì trước hết tôi tạo một file layout tên là player_widget.xml và bố trí các nút sao cho nhìn nó giống với thiết kế đã. Hiện tại thì hãy quên code java đi đừng lo lắng về nó vội.

  • Bước 1: Copy tất cả các file .png vừa cắt từ PSD vào thư mục drawable.

  • Bước 2: Với mỗi nút trên player tạo một file drawable selector. Vì mỗi nút có 3 trạng thái là normal, pressed và selected nên tôi sẽ tạo các seletor với 3 trạng thái cho mỗi nút. Sau đó sử dụng bên layout thì các nút sẽ tự động thay đổi hình nền theo trạng thái đã định nghĩa. Ví dụ dưới đây là file ic_stop_selector.xml

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@drawable/ic_stop"
          android:state_pressed="false"
          android:state_selected="false"/>
    <item android:drawable="@drawable/ic_stop_pressed"
          android:state_pressed="true"/>
    <item android:drawable="@drawable/ic_stop_pressed"
          android:state_selected="true"/>
</selector>

  • Bước 3: Tạo layout, tôi thì thích sử dụng LinearLayout ở đây vì các nút giống nhau về kích thước nên tôi muốn lợi dụng layout_weight của LinearLayout để chia đều kích thước. Còn bạn muốn dùng RelativeLayout cũng được, không vấn đề gì.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_awidth="match_parent"
    android:layout_height="wrap_content"
    android:background="@drawable/player_bg"
    android:gravity="center"
    android:orientation="horizontal"
    android:paddingLeft="10dp"
    android:paddingRight="10dp">

    <ImageView
        android:id="@+id/btnPrev"
        style="@style/PlayerButtonsStyle"
        android:layout_awidth="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:src="@drawable/ic_prev_selector"/>

    <ImageView
        android:id="@+id/btnPlay"
        style="@style/PlayerButtonsStyle"
        android:layout_awidth="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:src="@drawable/ic_play_selector"/>

    <ImageView
        android:id="@+id/btnPause"
        style="@style/PlayerButtonsStyle"
        android:layout_awidth="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:src="@drawable/ic_pause_selector"/>

    <ImageView
        android:id="@+id/btnStop"
        style="@style/PlayerButtonsStyle"
        android:layout_awidth="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:src="@drawable/ic_stop_selector"/>

    <ImageView
        android:id="@+id/btnNext"
        style="@style/PlayerButtonsStyle"
        android:layout_awidth="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:src="@drawable/ic_next_selector"/>
</LinearLayout>

Bạn để ý các buttons sẽ có chung một số thuộc tính nên tôi để các thuộc tính đó trong styles.xml và đặt style cho nó:

<style name="PlayerButtonsStyle">
    <item name="android:scaleType">fitCenter</item>
    <item name="android:clickable">true</item>
</style>

Xong xuôi thử nhấn vào tab design cái xem hình thù thế nào?

preview.png

Đã bao giờ bạn tự thấy mình thật là pro chưa? :sexy: Vậy là tạm ổn cho phần layout rồi nhỉ. Chuyển qua viết code logic nhé.

V. VIẾT CODE JAVA XỬ LÝ LOGIC

Trên đây mới chỉ là layout cho Custom View nếu bây giờ bạn mang file layout đó đi và chỗ nào cần dùng thì quăng vào thẻ include sau đó viết logic bên Activity chẳng hạn nó cũng chạy đẹp và mượt rồi đó. Nhưng đó không phải “dã tâm” của tôi nên tôi và bạn cùng tiếp tục nhé. Vì là tôi làm layout bằng LinearLayout nên tôi tạo một class Java đặt tên là PlayerView chẳng hạn kế thừa từ LinearLayout. Thêm các Constructor cơ bản của một CustomView.

public class PlayerView extends LinearLayout {
    public PlayerView(Context context) {
        super(context);
    }

    public PlayerView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    public PlayerView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
}

Tiếp đến tôi tạo một method private cho việc inflate layout cũng như lấy các buttons từ layout đó ra. Method này được gọi trong các constructor để đảm bảo layout được inflate ngay khi view đc tạo. Trong class PlayerView tôi cũng khai báo các ImageView là các button của player.

private void initialize() {
    ViewGroup viewGroup = (ViewGroup) inflate(getContext(), R.layout.player_widget, this);
    playerLayout = (ViewGroup) viewGroup.getChildAt(0);
    btnPrev = (ImageView) playerLayout.findViewById(R.id.btnPrev);
    btnPrev.setOnClickListener(buttonsClick);
    btnPlay = (ImageView) playerLayout.findViewById(R.id.btnPlay);
    btnPlay.setOnClickListener(buttonsClick);
    btnPause = (ImageView) playerLayout.findViewById(R.id.btnPause);
    btnPause.setOnClickListener(buttonsClick);
    btnStop = (ImageView) playerLayout.findViewById(R.id.btnStop);
    btnStop.setOnClickListener(buttonsClick);
    btnNext = (ImageView) playerLayout.findViewById(R.id.btnNext);
    btnNext.setOnClickListener(buttonsClick);
}

OK, tạm thế đã, vậy là cũng xong phần giao diện layout các kiểu con đà điểu rồi đặt thử nó vào layout của activity xem hình thù nó dư lào. Trong file layout muốn sử dụng PlayerView thì chỉ cần khai báo view như sau:

<?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_awidth="match_parent"
    android:layout_height="match_parent"
    tools:context="com.framgia.lupx.customcompoundviews.MainActivity">

    <com.framgia.lupx.customcompoundviews.widget.PlayerView
        android:layout_awidth="match_parent"
        android:layout_height="wrap_content"/>
</RelativeLayout>

Chạy thử phát xem kết quả, click vào các nút xem thế nào:

device-2016-03-27-140917.png

Ái chà chà, cũng ra gì phết đấy nhỉ. Chạy phát lên luôn (hihi)

Bây giờ với mỗi button trên player tôi sẽ set OnClickListener và xử lý logic như bình thường mà các bạn hay làm. Click vào button nào thì set state selected cho button đó và clear trạng thái selected của button trước đó đi. Phần này bạn tham khảo trong code tại anonymous class buttonsClick.

Để khi sử dụng có thể tùy biến việc click vào các nút thì sẽ thực hiện hành động mình muốn thì tôi tạm thời tạo ra một listener tên là PlayerControlListener, thực ra nó chỉ là 1 interface với 5 method được gọi tương ứng khi 5 button được click thôi.

Kết quả cuối cùng :boss:

device-2016-03-27-153452.png

Trên đây chỉ là một hướng dẫn nho nhỏ và 1 ví dụ đơn giản cho việc tạo custom compound view mong rằng các bạn nào chưa biết thì có thêm kiến thức để khi cần có thể giải quyết vấn đề một cách nhanh chóng và tiện lợi nhất.

HẾT

PSD: music ui elements.zip

Resoources PNG: CustomCompoundViewResources.zip

Github: CustomCompoundViews

0