亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

如何在Android中自定義viewgroup

發布時間:2021-06-09 17:12:57 來源:億速云 閱讀:257 作者:Leah 欄目:移動開發

如何在Android中自定義viewgroup?相信很多沒有經驗的人對此束手無策,為此本文總結了問題出現的原因和解決方法,通過這篇文章希望你能解決這個問題。

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

2.viewgroup實際上最終也是循環從上大小來調用子view的measure方法,注意子view的measure其實最終調用的是子view的onMeasure 方法。所以我們理解這個過程為:

viewgroup循環遍歷調用所有子view的onmeasure方法,利用onmeasure方法計算出來的大小,來確定這些子view最終可以占用的大小和所處的布局的位置。

3.measure方法是一個final方法,可以理解為做測量工作準備工作的,既然是final方法所以我們無法重寫它,不需要過多
關注他,因為measure最終要調用onmeasure ,這個onmeasure我們是可以重寫的。要關注這個。layout和onlayout是一樣的關系。

4.父view調用子view的layout方法的時候會把之前measure階段確定的位置和大小都傳遞給子view。

5.對于自定義view/viewgroup來說 我們幾乎只需要關注下面三種需求:

  • 對于已有的android自帶的view,我們只需要重寫他的onMeasure方法即可。修改一下這個尺寸即可完成需求。

  • 對于android系統沒有的,屬于我們自定義的view,比上面那個要復雜一點,要完全重寫onMeasure方法。

  • 第三種最復雜,需要重寫onmeasure和onlayout2個方法,來完成一個復雜viewgroup的測量和布局。

6.onMeasure方法的特殊說明:

如何在Android中自定義viewgroup

7.如何理解父view對子view的限制?

onMeasure的兩個參數既然是父view對子view的限制,那么這個限制的值到底是哪來的呢?

實際上,父view對子view的限制絕大多數就來自于我們開發者所設置的layout開頭的這些屬性

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

設置的這2個屬性是給父view看的,實際上對于絕大多數的layout開頭的屬性這些屬性都是設置給父view看的

為什么要給父view看?因為父view要知道這些屬性以后才知道要對子view的測量加以什么限制?

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

自定義一個BannerImageView 修改onMeasure方法

所謂bannerImageview,就是很多電商其實都會放廣告圖,這個廣告圖的寬高比都是可變的,我們在日常開發過程中
也會經常接觸到這種需求:imageview的寬高比 在高保真中都標注出來,但是考慮到很多手機的屏幕寬度或者高度都不確定

所以我們通常都要手動來計算出這個imageview高度或者寬度,然后動態改變width或者height的值。這種方法可用但是很麻煩

這里給出一個自定義的imageview,通過設置一個ratio的屬性即可動態的設置iv的高度。很是方便

如何在Android中自定義viewgroup

看下效果

如何在Android中自定義viewgroup

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

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) {
 //人家自己的測量還是要自己走一遍的,因為這個方法內部會調用setMeasuredDimension方法來保存測量結果了
 //只有保存了以后 我們才能取得這個測量結果 否則你下面是取不到的
 super.onMeasure(widthMeasureSpec, heightMeasureSpec);

 //取測量結果
 int mWidth = getMeasuredWidth();

 int mHeight = (int) (mWidth * ratio);

 //保存了以后,父view就可以拿到這個測量的寬高了。不保存是拿不到的噢。
 setMeasuredDimension(mWidth, mHeight);
 }
}

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

首先明確一個結論:

對于完全自定義的view,完全自己寫的onMeasure方法來說,你保存的寬高必須要符合父view的限制,否則會發生bug,
保存父view對子view的限制的方法也很簡單直接調用resolveSize方法即可。

如何在Android中自定義viewgroup

如何在Android中自定義viewgroup

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

  • 先算自己想要的寬高,比如你畫了個圓,那么寬高就肯定是半徑的兩倍大小, 要是圓下面還有字,

  • 那么高度肯定除了半徑的兩倍還要有字體的大小。對吧。很簡單。這個純看你自定義view是啥樣的

  • 算完自己想要的寬高以后 直接拿resolveSize 方法處理一下 即可。

  • 最后setMeasuredDimension 保存。

范例:

public class LoadingView extends View {

 //圓形的半徑
 int radius;

 //圓形外部矩形rect的起點
 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的時候一定會出現奇怪的bug的。
 //因為不用這個 你就完全沒有父view的感受了 最后強調一遍
 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);
 }
}

布局文件:

<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

自定義一個viewgroup

這個其實也就是稍微復雜了一點,但是還是有跡可循的,只是稍微需要一點額外的耐心。

自定義一個viewgroup 需要注意的點如下:

  • 一定是先重寫onMeasure確定子view的寬高和自己的寬高以后 才可以繼續寫onlayout 對這些子view進行布局噢~~

  • viewgroup 的onMeasure其實就是遍歷自己的view 對自己的每一個子view進行measure,絕大多數時候對子view的measure都可以直接用 measureChild()這個方法來替代,簡化我們的寫法,如果你的viewgroup很復雜的話無法就是自己寫一遍measureChild 而不是調用measureChild 罷了。

  • 計算出viewgroup自己的尺寸并且保存,保存的方法還是哪個setMeasuredDimension 不要忘記了

  • 逼不得已要重寫measureChild方法的時候,其實也不難無非就是對父view的測量和子view的測量 做一個取舍關系而已,你看懂了基礎的measureChild方法,以后就肯定會寫自己的復雜的measureChild方法了。

下面是一個極簡的例子,一個很簡單的flowlayout的實現,沒有對margin paddding做處理,也假設了每一個tag的高度
是固定的,可以說是極為簡單了,但是麻雀雖小 五臟俱全,足夠你們好好理解自定義viewgroup的關鍵點了。

/**
 * 寫一個簡單的flowlayout 從左到右的簡單layout,如果寬度不夠放 就直接另起一行layout
 * 這個類似的開源控件有很多,有很多寫的出色的,我這里只僅僅實現一個初級的flowlayout
 * 也是最簡單的,目的是為了理解自定義viewgroup的關鍵核心點。
 * <p>
 * 比方說這里并沒有對padding或者margin做特殊處理,你們自己寫viewgroup的時候 記得把這些屬性的處理都加上
 * 否則一旦有人用了這些屬性 發現沒有生效就比較難看了。。。。。。
 */
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的算法 其實就是 不夠放剩下一行 那另外放一行 這個過程一定要自己寫一遍才能體會,
  * 個人有個人的寫法,說不定你的寫法比開源的項目還要好
  * 其實也沒什么夸張的,無法就是前面onMeasure結束以后 你可以拿到所有子view和自己的 測量寬高 然后就算唄
  *
  * @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);
   //取得這個子view要求的寬度和高度
   int childWidth = childView.getMeasuredWidth();
   int childHeight = childView.getMeasuredHeight();

   //如果寬度不夠了 就另外啟動一行
   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 對SimpleFlowLayout 的測量限制 這一步很重要噢。
  //你只有知道自己的寬高 才能限制你子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;  //當前行高

  for (int i = 0; i < getChildCount(); i++) {
   View childView = getChildAt(i);
   LayoutParams lp = childView.getLayoutParams();

   //先測量子view
   measureChild(childView, widthMeasureSpec, heightMeasureSpec);
   //然后計算一下寬度里面 還有多少是可用的 也就是剩余可用寬度
   remaining = widthSize - usedWidth;

   //如果一行不夠放了,也就是說這個子view測量的寬度 大于 這一行 剩下的寬度的時候 我們就要另外啟一行了
   if (childView.getMeasuredWidth() > remaining) {
    //另外啟動一行的時候,使用過的寬度 當然要設置為0
    usedWidth = 0;
    //另外啟動一行了 我們的總高度也要加一下,不然高度就不對了
    totalHeight = totalHeight + lineHeight;
   }

   //已使用 width 進行 累加
   usedWidth = usedWidth + childView.getMeasuredWidth();
   //當前 view 的高度
   lineHeight = childView.getMeasuredHeight();
  }

  //如果SimpleFlowLayout 的高度 為wrap cotent的時候 才用我們疊加的高度,否則,我們當然用父view對如果SimpleFlowLayout 限制的高度
  if (heightMode == MeasureSpec.AT_MOST) {
   heightSize = totalHeight;
  }
  setMeasuredDimension(widthSize, heightSize);
 }
}

最后看下效果

如何在Android中自定義viewgroup

看完上述內容,你們掌握如何在Android中自定義viewgroup的方法了嗎?如果還想學到更多技能或想了解更多相關內容,歡迎關注億速云行業資訊頻道,感謝各位的閱讀!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

太保市| 长沙市| 普宁市| 永年县| 侯马市| 兰州市| 永福县| 九台市| 陆川县| 喀喇| 大安市| 金昌市| 五原县| 东至县| 荔浦县| 甘谷县| 娱乐| 霍林郭勒市| 兰坪| 贺兰县| 五台县| 辉南县| 通州区| 镇坪县| 三台县| 广昌县| 沙河市| 弥勒县| 通山县| 四子王旗| 牙克石市| 上思县| 门源| 墨江| 金门县| 昌乐县| 会宁县| 体育| 绩溪县| 陕西省| 秦皇岛市|