一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務(wù)器之家:專注于服務(wù)器技術(shù)及軟件下載分享
分類導(dǎo)航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術(shù)|正則表達(dá)式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務(wù)器之家 - 編程語言 - Android - 一篇文章弄懂Android自定義viewgroup的相關(guān)難點(diǎn)

一篇文章弄懂Android自定義viewgroup的相關(guān)難點(diǎn)

2022-03-02 15:46DK_BurNIng Android

這篇文章主要給大家介紹了關(guān)于如何通過一篇文章弄懂Android中自定義viewgroup的一些相關(guān)難點(diǎn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

本文的目的

目的在于教會(huì)大家到底如何自定義viewgroup,自定義布局和自定義測(cè)量到底如何寫。很多網(wǎng)上隨便搜搜的概念和流程圖
這里不再過多描述了,建議大家看本文之前,先看看基本的自定義viewgroup流程,心中有個(gè)大概即可。本文注重于實(shí)踐
viewgroup 的測(cè)量布局流程基本梳理

稍微回顧下,基本的viewgroup繪制和布局流程中的重點(diǎn):

1.view 在onMeasure()方法中進(jìn)行自我測(cè)量和保存,也就是說對(duì)于view(不是viewgroup噢)來說一定在onMeasure方法中計(jì)算出自己的尺寸并且保存下來

2.viewgroup實(shí)際上最終也是循環(huán)從上大小來調(diào)用子view的measure方法,注意子view的measure其實(shí)最終調(diào)用的是子view的onMeasure 方法。所以我們理解這個(gè)過程為:

viewgroup循環(huán)遍歷調(diào)用所有子view的onmeasure方法,利用onmeasure方法計(jì)算出來的大小,來確定這些子view最終可以占用的大小和所處的布局的位置。

3.measure方法是一個(gè)final方法,可以理解為做測(cè)量工作準(zhǔn)備工作的,既然是final方法所以我們無法重寫它,不需要過多
關(guān)注他,因?yàn)閙easure最終要調(diào)用onmeasure ,這個(gè)onmeasure我們是可以重寫的。要關(guān)注這個(gè)。layout和onlayout是一樣的關(guān)系。

4.父view調(diào)用子view的layout方法的時(shí)候會(huì)把之前measure階段確定的位置和大小都傳遞給子view。

5.對(duì)于自定義view/viewgroup來說 我們幾乎只需要關(guān)注下面三種需求:

  • 對(duì)于已有的android自帶的view,我們只需要重寫他的onMeasure方法即可。修改一下這個(gè)尺寸即可完成需求。
  • 對(duì)于android系統(tǒng)沒有的,屬于我們自定義的view,比上面那個(gè)要復(fù)雜一點(diǎn),要完全重寫onMeasure方法。
  • 第三種最復(fù)雜,需要重寫onmeasure和onlayout2個(gè)方法,來完成一個(gè)復(fù)雜viewgroup的測(cè)量和布局。

6.onMeasure方法的特殊說明:

一篇文章弄懂Android自定義viewgroup的相關(guān)難點(diǎn)

7.如何理解父view對(duì)子view的限制?

onMeasure的兩個(gè)參數(shù)既然是父view對(duì)子view的限制,那么這個(gè)限制的值到底是哪來的呢?

實(shí)際上,父view對(duì)子view的限制絕大多數(shù)就來自于我們開發(fā)者所設(shè)置的layout開頭的這些屬性

比方說我們給一個(gè)imageview設(shè)置了他的layout_width和layout_height 這2個(gè)屬性,那這2個(gè)屬性其實(shí)就是我們開發(fā)者
所期望的寬高屬性,但是要注意了,

設(shè)置的這2個(gè)屬性是給父view看的,實(shí)際上對(duì)于絕大多數(shù)的layout開頭的屬性這些屬性都是設(shè)置給父view看的

為什么要給父view看?因?yàn)楦竩iew要知道這些屬性以后才知道要對(duì)子view的測(cè)量加以什么限制?

到底是不限制(UNSPECIFIED)?還是限制個(gè)最大值(AT_MOST),讓子view不超過這個(gè)值?還是直接限制死,我讓你是多少就得是多少(EXACTLY)。

自定義一個(gè)BannerImageView 修改onMeasure方法

所謂bannerImageview,就是很多電商其實(shí)都會(huì)放廣告圖,這個(gè)廣告圖的寬高比都是可變的,我們?cè)谌粘i_發(fā)過程中
也會(huì)經(jīng)常接觸到這種需求:imageview的寬高比 在高保真中都標(biāo)注出來,但是考慮到很多手機(jī)的屏幕寬度或者高度都不確定

所以我們通常都要手動(dòng)來計(jì)算出這個(gè)imageview高度或者寬度,然后動(dòng)態(tài)改變width或者h(yuǎn)eight的值。這種方法可用但是很麻煩

這里給出一個(gè)自定義的imageview,通過設(shè)置一個(gè)ratio的屬性即可動(dòng)態(tài)的設(shè)置iv的高度。很是方便

一篇文章弄懂Android自定義viewgroup的相關(guān)難點(diǎn)

看下效果

一篇文章弄懂Android自定義viewgroup的相關(guān)難點(diǎn)

最后看下代碼,重要的部分都寫在注釋里了,不再過多講了。

?
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
public class BannerImageView extends ImageView {
 
 //寬高比
 float ratio;
 
 public BannerImageView(Context context) {
 super(context);
 }
 
 public BannerImageView(Context context, AttributeSet attrs) {
 super(context, attrs);
 
 TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.BannerImageView);
 ratio = typedArray.getFloat(R.styleable.BannerImageView_ratio, 1.0f);
 typedArray.recycle();
 }
 
 public BannerImageView(Context context, AttributeSet attrs, int defStyleAttr) {
 super(context, attrs, defStyleAttr);
 }
 
 @Override
 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
 //人家自己的測(cè)量還是要自己走一遍的,因?yàn)檫@個(gè)方法內(nèi)部會(huì)調(diào)用setMeasuredDimension方法來保存測(cè)量結(jié)果了
 //只有保存了以后 我們才能取得這個(gè)測(cè)量結(jié)果 否則你下面是取不到的
 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
 
 //取測(cè)量結(jié)果
 int mWidth = getMeasuredWidth();
 
 int mHeight = (int) (mWidth * ratio);
 
 //保存了以后,父view就可以拿到這個(gè)測(cè)量的寬高了。不保存是拿不到的噢。
 setMeasuredDimension(mWidth, mHeight);
 }
}

自定義view,完全自己寫onMeasure方法

首先明確一個(gè)結(jié)論:

對(duì)于完全自定義的view,完全自己寫的onMeasure方法來說,你保存的寬高必須要符合父view的限制,否則會(huì)發(fā)生bug,
保存父view對(duì)子view的限制的方法也很簡(jiǎn)單直接調(diào)用resolveSize方法即可。

一篇文章弄懂Android自定義viewgroup的相關(guān)難點(diǎn)

一篇文章弄懂Android自定義viewgroup的相關(guān)難點(diǎn)

所以對(duì)于完全自定義的view onMeasure方法也不難寫了,

  • 先算自己想要的寬高,比如你畫了個(gè)圓,那么寬高就肯定是半徑的兩倍大小, 要是圓下面還有字,
  • 那么高度肯定除了半徑的兩倍還要有字體的大小。對(duì)吧。很簡(jiǎn)單。這個(gè)純看你自定義view是啥樣的
  • 算完自己想要的寬高以后 直接拿resolveSize 方法處理一下 即可。
  • 最后setMeasuredDimension 保存。

范例:

?
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
public class LoadingView extends View {
 
 //圓形的半徑
 int radius;
 
 //圓形外部矩形rect的起點(diǎn)
 int left = 10, top = 30;
 
 
 Paint mPaint = new Paint();
 
 public LoadingView(Context context) {
 super(context);
 }
 
 public LoadingView(Context context, AttributeSet attrs) {
 super(context, attrs);
 TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.LoadingView);
 radius = typedArray.getInt(R.styleable.LoadingView_radius, 0);
 }
 
 public LoadingView(Context context, AttributeSet attrs, int defStyleAttr) {
 super(context, attrs, defStyleAttr);
 }
 
 @Override
 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
 
 
 int width = left + radius * 2;
 int height = top + radius * 2;
 
 //一定要用resolveSize方法來格式化一下你的view寬高噢,否則遇到某些layout的時(shí)候一定會(huì)出現(xiàn)奇怪的bug的。
 //因?yàn)椴挥眠@個(gè) 你就完全沒有父view的感受了 最后強(qiáng)調(diào)一遍
 width = resolveSize(width, widthMeasureSpec);
 height = resolveSize(height, heightMeasureSpec);
 
 setMeasuredDimension(width, height);
 }
 
 @Override
 protected void onDraw(Canvas canvas) {
 super.onDraw(canvas);
 
 RectF oval = new RectF(left, top,
  left + radius * 2, top + radius * 2);
 mPaint.setColor(Color.BLUE);
 canvas.drawRect(oval, mPaint);
 //先畫圓弧
 mPaint.setColor(Color.RED);
 mPaint.setStyle(Paint.Style.STROKE);
 mPaint.setStrokeWidth(2);
 canvas.drawArc(oval, -90, 360, false, mPaint);
 }
}

布局文件:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<LinearLayout
  android:layout_width="200dp"
  android:layout_height="200dp"
  android:background="#000000"
  android:orientation="horizontal">
 
  <com.example.a16040657.customviewtest.LoadingView
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
   android:src="@mipmap/dly"
   app:radius="200"></com.example.a16040657.customviewtest.LoadingView>
 
  <com.example.a16040657.customviewtest.LoadingView
   android:layout_marginLeft="10dp"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
   android:src="@mipmap/dly"
   app:radius="200"></com.example.a16040657.customviewtest.LoadingView>
 </LinearLayout>

最后效果:

一篇文章弄懂Android自定義viewgroup的相關(guān)難點(diǎn)

自定義一個(gè)viewgroup

這個(gè)其實(shí)也就是稍微復(fù)雜了一點(diǎn),但是還是有跡可循的,只是稍微需要一點(diǎn)額外的耐心。

自定義一個(gè)viewgroup 需要注意的點(diǎn)如下:

  • 一定是先重寫onMeasure確定子view的寬高和自己的寬高以后 才可以繼續(xù)寫onlayout 對(duì)這些子view進(jìn)行布局噢~~
  • viewgroup 的onMeasure其實(shí)就是遍歷自己的view 對(duì)自己的每一個(gè)子view進(jìn)行measure,絕大多數(shù)時(shí)候?qū)ψ觱iew的measure都可以直接用 measureChild()這個(gè)方法來替代,簡(jiǎn)化我們的寫法,如果你的viewgroup很復(fù)雜的話無法就是自己寫一遍measureChild 而不是調(diào)用measureChild 罷了。
  • 計(jì)算出viewgroup自己的尺寸并且保存,保存的方法還是哪個(gè)setMeasuredDimension 不要忘記了
  • 逼不得已要重寫measureChild方法的時(shí)候,其實(shí)也不難無非就是對(duì)父view的測(cè)量和子view的測(cè)量 做一個(gè)取舍關(guān)系而已,你看懂了基礎(chǔ)的measureChild方法,以后就肯定會(huì)寫自己的復(fù)雜的measureChild方法了。

下面是一個(gè)極簡(jiǎn)的例子,一個(gè)很簡(jiǎn)單的flowlayout的實(shí)現(xiàn),沒有對(duì)margin paddding做處理,也假設(shè)了每一個(gè)tag的高度
是固定的,可以說是極為簡(jiǎn)單了,但是麻雀雖小 五臟俱全,足夠你們好好理解自定義viewgroup的關(guān)鍵點(diǎn)了。

?
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
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
/**
 * 寫一個(gè)簡(jiǎn)單的flowlayout 從左到右的簡(jiǎn)單layout,如果寬度不夠放 就直接另起一行l(wèi)ayout
 * 這個(gè)類似的開源控件有很多,有很多寫的出色的,我這里只僅僅實(shí)現(xiàn)一個(gè)初級(jí)的flowlayout
 * 也是最簡(jiǎn)單的,目的是為了理解自定義viewgroup的關(guān)鍵核心點(diǎn)。
 * <p>
 * 比方說這里并沒有對(duì)padding或者margin做特殊處理,你們自己寫viewgroup的時(shí)候 記得把這些屬性的處理都加上
 * 否則一旦有人用了這些屬性 發(fā)現(xiàn)沒有生效就比較難看了。。。。。。
 */
public class SimpleFlowLayout extends ViewGroup {
 public SimpleFlowLayout(Context context) {
  super(context);
 }
 
 public SimpleFlowLayout(Context context, AttributeSet attrs) {
  super(context, attrs);
 }
 
 public SimpleFlowLayout(Context context, AttributeSet attrs, int defStyleAttr) {
  super(context, attrs, defStyleAttr);
 }
 
 /**
  * layout的算法 其實(shí)就是 不夠放剩下一行 那另外放一行 這個(gè)過程一定要自己寫一遍才能體會(huì),
  * 個(gè)人有個(gè)人的寫法,說不定你的寫法比開源的項(xiàng)目還要好
  * 其實(shí)也沒什么夸張的,無法就是前面onMeasure結(jié)束以后 你可以拿到所有子view和自己的 測(cè)量寬高 然后就算唄
  *
  * @param changed
  * @param l
  * @param t
  * @param r
  * @param b
  */
 
 @Override
 protected void onLayout(boolean changed, int l, int t, int r, int b) {
  int childTop = 0;
  int childLeft = 0;
  int childRight = 0;
  int childBottom = 0;
 
  //已使用 width
  int usedWidth = 0;
 
 
  //customlayout 自己可使用的寬度
  int layoutWidth = getMeasuredWidth();
  Log.v("wuyue", "layoutWidth==" + layoutWidth);
  for (int i = 0; i < getChildCount(); i++) {
   View childView = getChildAt(i);
   //取得這個(gè)子view要求的寬度和高度
   int childWidth = childView.getMeasuredWidth();
   int childHeight = childView.getMeasuredHeight();
 
   //如果寬度不夠了 就另外啟動(dòng)一行
   if (layoutWidth - usedWidth < childWidth) {
    childLeft = 0;
    usedWidth = 0;
    childTop += childHeight;
    childRight = childWidth;
    childBottom = childTop + childHeight;
    childView.layout(0, childTop, childRight, childBottom);
    usedWidth = usedWidth + childWidth;
    childLeft = childWidth;
    continue;
   }
   childRight = childLeft + childWidth;
   childBottom = childTop + childHeight;
   childView.layout(childLeft, childTop, childRight, childBottom);
   childLeft = childLeft + childWidth;
   usedWidth = usedWidth + childWidth;
 
  }
 }
 
 @Override
 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
 
  //先取出SimpleFlowLayout的父view 對(duì)SimpleFlowLayout 的測(cè)量限制 這一步很重要噢。
  //你只有知道自己的寬高 才能限制你子view的寬高
  int widthMode = MeasureSpec.getMode(widthMeasureSpec);
  int heightMode = MeasureSpec.getMode(heightMeasureSpec);
  int widthSize = MeasureSpec.getSize(widthMeasureSpec);
  int heightSize = MeasureSpec.getSize(heightMeasureSpec);
 
 
  int usedWidth = 0//已使用的寬度
  int remaining = 0//剩余可用寬度
  int totalHeight = 0; //總高度
  int lineHeight = 0//當(dāng)前行高
 
  for (int i = 0; i < getChildCount(); i++) {
   View childView = getChildAt(i);
   LayoutParams lp = childView.getLayoutParams();
 
   //先測(cè)量子view
   measureChild(childView, widthMeasureSpec, heightMeasureSpec);
   //然后計(jì)算一下寬度里面 還有多少是可用的 也就是剩余可用寬度
   remaining = widthSize - usedWidth;
 
   //如果一行不夠放了,也就是說這個(gè)子view測(cè)量的寬度 大于 這一行 剩下的寬度的時(shí)候 我們就要另外啟一行了
   if (childView.getMeasuredWidth() > remaining) {
    //另外啟動(dòng)一行的時(shí)候,使用過的寬度 當(dāng)然要設(shè)置為0
    usedWidth = 0;
    //另外啟動(dòng)一行了 我們的總高度也要加一下,不然高度就不對(duì)了
    totalHeight = totalHeight + lineHeight;
   }
 
   //已使用 width 進(jìn)行 累加
   usedWidth = usedWidth + childView.getMeasuredWidth();
   //當(dāng)前 view 的高度
   lineHeight = childView.getMeasuredHeight();
  }
 
  //如果SimpleFlowLayout 的高度 為wrap cotent的時(shí)候 才用我們疊加的高度,否則,我們當(dāng)然用父view對(duì)如果SimpleFlowLayout 限制的高度
  if (heightMode == MeasureSpec.AT_MOST) {
   heightSize = totalHeight;
  }
  setMeasuredDimension(widthSize, heightSize);
 }
}

最后看下效果

一篇文章弄懂Android自定義viewgroup的相關(guān)難點(diǎn)

總結(jié)

以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問大家可以留言交流,謝謝大家對(duì)服務(wù)器之家的支持。

原文鏈接:https://juejin.im/post/5b25bc136fb9a00e373bd0c8

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 国产欧美一区二区三区免费看 | 九九在线免费视频 | 99视频九九精品视频在线观看 | 精品久久久麻豆国产精品 | 白丝校花好湿好紧 | 国产清纯91天堂在线观看 | 97视频免费人人观看人人 | 国产视频一区在线观看 | 婷婷丁香色综合狠狠色 | 国产精品第一区揄拍 | 欧美精品久久一区二区三区 | 91嫩草国产在线观看免费 | 四虎在线观看 | 亚洲欧美久久婷婷爱综合一区天堂 | 日b视频免费 | 日本xxxxx高清免费观看 | 国产女主播福利在线 | 恩爱夫妇交换小说 | 国产国拍亚洲精品av | se在线播放 | 日韩毛片基地一区二区三区 | 久久无码人妻中文国产 | 日本一卡二卡3卡四卡网站精品 | 舔比小说 | 日韩人成免费网站大片 | 免费一级欧美大片在线观看 | 四虎精品免费视频 | 草莓秋葵菠萝蜜绿巨人污 | www.91在线 | 午夜小视频网站 | 国产精品亚洲综合第一区 | 免费亚洲视频 | 色噜噜视频影院 | 日本免费高清在线 | 91麻豆精品 | 日本高清中文字幕视频在线 | 天天操网 | 91精品国产美女福到在线不卡 | 国产精品自产拍在线观看2019 | 韩日一区二区三区 | 99在线观看视频免费 |