美文网首页
Android自定义View之自定义布局

Android自定义View之自定义布局

作者: 翟小乙 | 来源:发表于2022-04-20 18:38 被阅读0次
自定义分为自定义View与自定义布局:
  • 需要掌握的技术:

    1. onMeasure 父子组件大小计算
    2. onLayout 组件排版
    3. onDraw
  • 知识点:

    1. getMeasureWidth()与getMeasureHeight() 在View没有展示完全获取
    2. getWidth()与getHeight()在View展示后才能获取
    3. MeasureSpec.EXACTLY 等三种类型区别
    4. 通过getChildMeasureSpec将子view的宽高转为MeasureSpec
  • 自定义布局用到前两个,我们以流式布局为例实现自定义布局:

实现:

思路:
1.onMeasure中计算子View 大小
2.右子View 大小计算出父View真是宽高
3.记录所有行的View 与高度
4.在Onlayout中排版

  • 创建 LiuShiLayout 继承 ViewGroup
package com.myself.alamdding;

import android.content.Context;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;

import java.util.ArrayList;
import java.util.List;

public class LiuShiLayout extends ViewGroup {

  int hWidth = 100;  // 每一行的宽度间隔
  int vHeight = 100; // 每一行的高度间隔
  private List<List<View>> listListLine = new ArrayList<>();// 记录所有行的信息
  private List<Integer> listHeight = new ArrayList<>();// 记录每一行的行高

   public LiuShiLayout(Context context) {
      super(context);
  }

  public LiuShiLayout(Context context, AttributeSet attrs) {
      super(context, attrs);
  }

  public LiuShiLayout(Context context, AttributeSet attrs, int defStyleAttr) {
      super(context, attrs, defStyleAttr);
  }

  public LiuShiLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
      super(context, attrs, defStyleAttr, defStyleRes);
  }
  private void initParams(){

       listListLine = new ArrayList<>();// 记录所有行的信息
        listHeight = new ArrayList<>();// 记录每一行的行高
  }
  @Override
  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
      initParams();
       // 计算自己宽高
       int lineWidth = 0; // 每一行的宽度
       int lineHeight = 0;// 每一行的高度
       int parentWith = 0;// 所有行一共宽度
       int parentHeight = 0;// 所有行一共高度
       List<View> lineViewArr = new ArrayList<>();// 每一行存储的view
       int selfWidth = MeasureSpec.getSize(widthMeasureSpec);// 容器宽度
       int selfHeight = MeasureSpec.getSize(heightMeasureSpec);// 容器高度
      // 计算子view 大小
      int paddingLeft =  getPaddingLeft();
      int paddingRight =  getPaddingRight();
      int paddingTop =  getPaddingTop();
      int paddingBottom =  getPaddingBottom();
      for (int i = 0; i < getChildCount(); i++) {
          View view = getChildAt(i);
          LayoutParams layoutParams = view.getLayoutParams();
          int childMeasureSpecWidth = getChildMeasureSpec(widthMeasureSpec, paddingLeft+paddingRight, layoutParams.width);
          int childMeasureSpecHeight = getChildMeasureSpec(heightMeasureSpec, paddingTop + paddingBottom, layoutParams.height);
          view.measure(childMeasureSpecWidth,childMeasureSpecHeight);

          // 获取子view 一行宽度和一行最大高度
          int childWidth = view.getMeasuredWidth();
          int childHeight = view.getMeasuredHeight();

          if((lineWidth + childWidth + hWidth)> selfWidth||lineHeight==0){// 换行
              listListLine.add(lineViewArr);// 记录每一行的view
              listHeight.add(parentHeight);
              // 换行之前统计一下宽高度
              parentWith = Math.max(parentWith, lineWidth + hWidth);
              parentHeight = parentHeight + lineHeight + vHeight;


              lineWidth = 0;
              lineHeight = 0;
              lineViewArr = new ArrayList<>();
          }

          lineViewArr.add(view);
          lineWidth = lineWidth + childWidth + hWidth;
          lineHeight = Math.max(lineHeight,childHeight);

      }
      // 计算自己大小->根据子view
      int widthMode = MeasureSpec.getMode(widthMeasureSpec);
      int heightMode = MeasureSpec.getMode(heightMeasureSpec);
          // 如果父亲传过来的是确切的size就用自己,不是就用计算的

      listListLine.add(lineViewArr);// 记录每一行的view
      listHeight.add(parentHeight);
      parentWith = Math.max(parentWith, lineWidth + hWidth);
      parentHeight = parentHeight + lineHeight + vHeight;

      int realWidth = (widthMode==MeasureSpec.EXACTLY)?selfWidth:parentWith;
      int realHeight = (heightMode==MeasureSpec.EXACTLY)?selfHeight:parentHeight;

      setMeasuredDimension(realWidth,realHeight);

  }

  @Override
  protected void onLayout(boolean b, int i, int i1, int i2, int i3) {
       int curLeft = getPaddingLeft();
       int curTop = getPaddingTop();
      for (int j = 0; j < listListLine.size(); j++) {
          int lineHeight = listHeight.get(j);
          List<View> viewList = listListLine.get(j);
          for (int k = 0; k < viewList.size(); k++) {
              View view = viewList.get(k);
              int left = curLeft;
              int top = curTop;
              int right = left + view.getMeasuredWidth();
              int bottom = top + view.getMeasuredHeight();
              view.layout(left,top,right,bottom);
              curLeft = right + hWidth;// 画完一个View,位置右移动画下一个
          }
          curLeft = getPaddingLeft();
          curTop = curTop + lineHeight + vHeight;
      }
  }

  @Override
  protected void onDraw(Canvas canvas) {
      super.onDraw(canvas);
  }
}

XML 实现:
 <com.myself.alamdding.LiuShiLayout
        android:layout_width="match_parent"
        android:background="@color/colorPrimaryDark"
        android:layout_marginTop="20dp"
        android:layout_height="wrap_content">
        <TextView
            android:layout_width="50dp"
            android:layout_height="wrap_content"
            android:text="北京"
            android:background="@color/colorPrimary"/>
        <TextView
            android:layout_width="50dp"
            android:layout_height="wrap_content"
            android:text="石家庄"
            android:background="@color/colorPrimary"/>
    </com.myself.alamdding.LiuShiLayout>
效果:
效果.png

相关文章

网友评论

      本文标题:Android自定义View之自定义布局

      本文链接:https://www.haomeiwen.com/subject/jlcwertx.html