+3

Custom View extend ViewGroup

1. Giới thiệu về ViewGroup

ViewGroup trong android là một view có thể chứa view khác trong đó. Một ViewGroup có thể chứa một hoặc nhiều child view. Tất cả những Layout Manager cơ bản được cung cấp sẵn như LinearLayout, Relative Layout, Frame Layout đều là những sub class được kế thừa từ View Group, các child view ở bên trong nó sẽ xuất hiện theo từng định dạng đã được định sẵn với từng loại. Ví dụ trong Linear Layout các child view sẽ xuất hiện liền kề liên tiếp với nhau theo chiều dọc hoặc chiều ngang.

Trong một số trường hợp, tùy vào yêu cầu cụ thể, các Layout manager cơ bản là không đủ, bạn cần tạo một Custom View extend từ ViewGroup class để custom một layout manager của riêng mình.

Trong bài này sẽ hướng dẫn tạo một TagLayout, có thể dùng để show một list các tag như hình phía dưới dưới:

Create-Custom-Layout-in-Android-by-Extending-ViewGroup-Class-300x533.png

Để tạo một custom layout manager, bạn cần thực hiện theo các bước sau:

  1. Tạo một class extend từ ViewGroup
  2. Override onLayout() method. Method này sẽ được dùng để thiết lập vị trí cho các child view nằm trong nó.
  3. Override onMeasure() method. Method này sẽ được sử dụng để tính toán kích thước của view group và các child view ở trong nó.
  4. Hai method onLayout() và onMeasure() sẽ chứa các tính toán logic để sắp xếp các child view vào trong view group

Bạn có thể lấy được width và height của child view thông qua 2 method getMeasuredWidth() và getMeasuredHeight().

2. Tạo Custom View TagLayout

public class TagLayout extends ViewGroup {
    int deviceWidth;

    public TagLayout(Context context) {
        this(context, null, 0);
    }

    public TagLayout(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

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

    private void init(Context context) {
        final Display display = ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
        Point deviceDisplay = new Point();
        display.getSize(deviceDisplay);
        deviceWidth = deviceDisplay.x;
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        final int count = getChildCount();
        int curWidth, curHeight, curLeft, curTop, maxHeight;

        //get the available size of child view
        final int childLeft = this.getPaddingLeft();
        final int childTop = this.getPaddingTop();
        final int childRight = this.getMeasuredWidth() - this.getPaddingRight();
        final int childBottom = this.getMeasuredHeight() - this.getPaddingBottom();
        final int childWidth = childRight - childLeft;
        final int childHeight = childBottom - childTop;

        maxHeight = 0;
        curLeft = childLeft;
        curTop = childTop;

        for (int i = 0; i < count; i++) {
            View child = getChildAt(i);

            if (child.getVisibility() == GONE)
                return;

            //Get the maximum size of the child
            child.measure(MeasureSpec.makeMeasureSpec(childWidth, MeasureSpec.AT_MOST), MeasureSpec.makeMeasureSpec(childHeight, MeasureSpec.AT_MOST));
            curWidth = child.getMeasuredWidth();
            curHeight = child.getMeasuredHeight();
            //wrap is reach to the end
            if (curLeft + curWidth >= childRight) {
                curLeft = childLeft;
                curTop += maxHeight;
                maxHeight = 0;
            }
            //do the layout
            child.layout(curLeft, curTop, curLeft + curWidth, curTop + curHeight);
            //store the max height
            if (maxHeight < curHeight)
                maxHeight = curHeight;
            curLeft += curWidth;
        }
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int count = getChildCount();
        // Measurement will ultimately be computing these values.
        int maxHeight = 0;
        int maxWidth = 0;
        int childState = 0;
        int mLeftWidth = 0;
        int rowCount = 0;

        // Iterate through all children, measuring them and computing our dimensions
        // from their size.
        for (int i = 0; i < count; i++) {
            final View child = getChildAt(i);

            if (child.getVisibility() == GONE)
                continue;

            // Measure the child.
            measureChild(child, widthMeasureSpec, heightMeasureSpec);
            maxWidth += Math.max(maxWidth, child.getMeasuredWidth());
            mLeftWidth += child.getMeasuredWidth();

            if ((mLeftWidth / deviceWidth) > rowCount) {
                maxHeight += child.getMeasuredHeight();
                rowCount++;
            } else {
                maxHeight = Math.max(maxHeight, child.getMeasuredHeight());
            }
            childState = combineMeasuredStates(childState, child.getMeasuredState());
        }

        // Check against our minimum height and width
        maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
        maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());

        // Report our final dimensions.
        setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
                resolveSizeAndState(maxHeight, heightMeasureSpec, childState << MEASURED_HEIGHT_STATE_SHIFT));
    }
}

3. Sử dụng TagLayout vừa custom vào chương trình

  • Đầu tiên add một TagLayout vào file xml của một layout
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.javatechig.taglayout.TagLayout
        android:id="@+id/tagLayout"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="#ffff00"/>

</RelativeLayout>

4. Cài đặt layout cho child view nằm bên trong TagLayout

  • Tạo một layout, nó sẽ được sử dụng làm child view cho TagLayout, trong trường hợp này là các tag item.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content">

    <TextView
        android:id="@+id/tagTextView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="5dp"
        android:background="#a000"
        android:padding="10dp"
        android:textColor="#fff" />

</LinearLayout>

5. Add các child view vào trong TagLayout

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        TagLayout tagLayout = (TagLayout) findViewById(R.id.tagLayout);
        LayoutInflater layoutInflater = getLayoutInflater();
        String tag;
        for (int i = 0; i <= 20; i++) {
            tag = "#tag" + i;
            View tagView = layoutInflater.inflate(R.layout.tag_layout, null, false);

            TextView tagTextView = (TextView) tagView.findViewById(R.id.tagTextView);
            tagTextView.setText(tag);
            tagLayout.addView(tagView);
        }
    }
}

Nguồn tham khảo

http://javatechig.com/android/how-to-create-custom-layout-in-android-by-extending-viewgroup-class


All Rights Reserved

Viblo
Let's register a Viblo Account to get more interesting posts.