標籤:bst rri base mod 字型 屬性 epo 寬高 計算
著作權聲明:本文為博主原創文章,未經博主允許不得轉載。
無論是在移動端的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就是標籤的位置。 } });
:
使用前不要忘了引入依賴:
allprojects { repositories { ... maven { url ‘https://jitpack.io‘ } } }dependencies { compile ‘com.github.donkingliang:LabelsView:1.0.0‘ }
最後給出該控制項在GitHub中的地址,歡迎大家訪問和使用。
https://github.com/donkingliang/LabelsView
文章已同步到我的簡書
Android自訂標籤清單控制項LabelsView解析