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 ...
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:
Để tạo một custom layout manager, bạn cần thực hiện theo các bước sau:
- Tạo một class extend từ ViewGroup
- 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ó.
- 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ó.
- 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 awidth 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 awidthMeasureSpec, 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, awidthMeasureSpec, 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 awidth maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight()); maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth()); // Report our final dimensions. setMeasuredDimension(resolveSizeAndState(maxWidth, awidthMeasureSpec, 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_awidth="match_parent" android:layout_height="match_parent"> <com.javatechig.taglayout.TagLayout android:id="@+id/tagLayout" android:layout_awidth="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_awidth="wrap_content" android:layout_height="wrap_content"> <TextView android:id="@+id/tagTextView" android:layout_awidth="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); } } }