版權聲明:本文為博主原創文章,未經博主允許不得轉載。
無論是在移動端的App,還是在前端的網頁,我們經常會看到下面這種標簽的列表效果:
標簽從左到右擺放,一行顯示不下時自動換行。這樣的效果用Android源生的控件很不好實現,所以往往需要我們自己去自定義控件。我在開發中就遇到過幾次要實現這樣的標簽列表效果,所以就自己寫了個控件,放到我的GitHub,方便以后使用。有興趣的同學也歡迎訪問我的GitHub、查看源碼實現和使用該控件。下面我將為大家介紹該控件的具體實現和使用。
要實現這樣一個標簽列表其實並不難,列表中的item可以直接用TextView來實現,我們只需要關心列表控件的大小和標簽的擺放就可以了。也就是說我們需要做的只要兩件事:測量布局(onMeasure)和擺放標簽(onLayout)。這是自定義ViewGroup的基本步驟,相信對自定義View有所了解的同學都不會陌生。下面我們就來看看具體的代碼實現。
控件的測量:
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int count = getChildCount(); int maxWidth = MeasureSpec.getSize(widthMeasureSpec) - getPaddingLeft() - getPaddingRight(); int contentHeight = 0; //記錄內容的高度 int lineWidth = 0; //記錄行的寬度 int maxLineWidth = 0; //記錄最寬的行寬 int maxItemHeight = 0; //記錄一行中item高度最大的高度 boolean begin = true; //是否是行的開頭 //循環測量item並計算控件的內容寬高 for (int i = 0; i < count; i++) { View view = getChildAt(i); measureChild(view, widthMeasureSpec, heightMeasureSpec); //當前行顯示不下item時換行。 if (maxWidth < lineWidth + view.getMeasuredWidth()) { contentHeight += mLineMargin; contentHeight += maxItemHeight; maxItemHeight = 0; maxLineWidth = Math.max(maxLineWidth, lineWidth); lineWidth = 0; begin = true; } maxItemHeight = Math.max(maxItemHeight, view.getMeasuredHeight()); if(!begin) { lineWidth += mWordMargin; }else { begin = false; } lineWidth += view.getMeasuredWidth(); } contentHeight += maxItemHeight; maxLineWidth = Math.max(maxLineWidth, lineWidth); //測量控件的最終寬高 setMeasuredDimension(measureWidth(widthMeasureSpec,maxLineWidth), measureHeight(heightMeasureSpec, contentHeight)); } //測量控件的寬 private int measureWidth(int measureSpec, int contentWidth) { int result = 0; int specMode = MeasureSpec.getMode(measureSpec); int specSize = MeasureSpec.getSize(measureSpec); if (specMode == MeasureSpec.EXACTLY) { result = specSize; } else { result = contentWidth + getPaddingLeft() + getPaddingRight(); if (specMode == MeasureSpec.AT_MOST) { result = Math.min(result, specSize); } } //這一句是為了支持minWidth屬性。 result = Math.max(result, getSuggestedMinimumWidth()); return result; } //測量控件的高 private int measureHeight(int measureSpec, int contentHeight) { int result = 0; int specMode = MeasureSpec.getMode(measureSpec); int specSize = MeasureSpec.getSize(measureSpec); if (specMode == MeasureSpec.EXACTLY) { result = specSize; } else { result = contentHeight + getPaddingTop() + getPaddingBottom(); if (specMode == MeasureSpec.AT_MOST) { result = Math.min(result, specSize); } } //這一句是為了支持minHeight屬性。 result = Math.max(result, getSuggestedMinimumHeight()); return result; }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
標簽的擺放:
@Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { int x = getPaddingLeft(); int y = getPaddingTop(); int contentWidth = right - left; int maxItemHeight = 0; int count = getChildCount(); //循環擺放item for (int i = 0; i < count; i++) { View view = getChildAt(i); //當前行顯示不下item時換行。 if (contentWidth < x + view.getMeasuredWidth()) { x = getPaddingLeft(); y += mLineMargin; y += maxItemHeight; maxItemHeight = 0; } view.layout(x, y, x + view.getMeasuredWidth(), y + view.getMeasuredHeight()); x += view.getMeasuredWidth(); x += mWordMargin; maxItemHeight = Math.max(maxItemHeight, view.getMeasuredHeight()); } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
onMeasure和onLayout的實現代碼基本是一樣的,不同的只是一個是測量寬高,一個是擺放位置而已。實現起來非常的簡單。
控件的使用就更加的簡單了,只需要三步:
1、編寫布局:
<com.donkingliang.labels.LabelsView xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/labels" android:layout_width="match_parent" android:layout_height="wrap_content" app:labelBackground="@drawable/pink_frame_bg" //標簽的背景 app:labelTextColor="#fb435b" //標簽的字體顏色 app:labelTextSize="14sp" //標簽的字體大小 app:labelTextPaddingBottom="5dp" //標簽的上下左右邊距 app:labelTextPaddingLeft="10dp" app:labelTextPaddingRight="10dp" app:labelTextPaddingTop="5dp" app:lineMargin="10dp" //行與行的距離 app:wordMargin="10dp" /> //標簽與標簽的距離
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
2、設置標簽:
ArrayList<String> list = new ArrayList<>(); list.add("Android"); list.add("IOS"); list.add("前端"); list.add("后台"); list.add("微信開發"); list.add("Java"); list.add("JavaScript"); list.add("C++"); list.add("PHP"); list.add("Python"); labelsView.setLabels(list);
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
3、設置點擊監聽:(如果需要的話)
labels.setOnLabelClickListener(new LabelsView.OnLabelClickListener() { @Override public void onLabelClick(TextView label, int position) { //label就是被點擊的標簽,position就是標簽的位置。 } });
- 1
- 2
- 3
- 4
- 5
- 6
- 1
- 2
- 3
- 4
- 5
- 6
效果圖:
使用前不要忘了引入依賴:
allprojects {
repositories {
... maven { url 'https://jitpack.io' } } } dependencies { compile 'com.github.donkingliang:LabelsView:1.0.0' }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
最后給出該控件在GitHub中的地址,歡迎大家訪問和使用。
https://github.com/donkingliang/LabelsView
文章已同步到我的簡書