android自定義View&自定義ViewGroup(下)

_小馬快跑_發表於2017-12-15

接上篇, android自定義View&自定義ViewGroup(上) 上篇主要是自定義View,本篇來看看自定義ViewGroup。

先來複習一下一般自定義ViewGroup中需要複寫的方法:

void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
void onSizeChanged(int w, int h, int oldw, int oldh)
void onLayout(boolean changed, int left, int top, int right, int bottom)
void onDraw(Canvas canvas)
複製程式碼

:ViewGroup的onLayout方法是必須重寫的,而onDraw方法預設是不會呼叫的,如果想執行onDraw方法,可以通過下面兩種方法: 1.設定透明背景: 在建構函式中:setBackgroundColor(Color.TRANSPARENT); 或者在xml中:android:background="@color/transparent" 2.或者可以在建構函式中新增setWillNotDraw(false); 自定義ViewGroup中如果需要獲得自定義屬性,方法和上篇自定義View中獲取方法是一樣的,請參照上篇。

###ViewGroup常用重寫方法:

  • #####onMeasure measureChildren方法觸發所有子View的onMeasure方法測量自己並把測量結果回傳給ViewGroup(ViewGroup傳遞給子View建議寬高和測量模式,如果子View的寬高是wrap_content,那麼只有子View測量出自己的寬和高),當所有子View測量完畢後,再呼叫setMeasuredDimension將ViewGroup自身的寬和高傳給它的父View。
  • #####onSizeChanged 在onMeasure()後執行,只有大小發生了變化才會執行onSizeChange
  • #####onLayout 排列所有子View的位置 通過getChildCount()獲取所有子view,getChildAt獲取childview呼叫各自的layout(int, int, int, int)方法來排列自己。
  • #####onDraw 上面已經提到,自定義ViewGroup預設不會觸發onDraw方法,需要設定背景色或者setWillNotDraw(false)來手動觸發。

自定義ViewGroup例子,先上圖:

fiveRings.png
###程式碼已上傳到github:自定義ViewGroup(五環圖)

思路:首先是5個自定義圓環,在自定義ViewGroup的onLayout中排列他們,在onDraw中寫文字。 核心程式碼:

public class CustomFiveRings extends ViewGroup {
    private Context mContext;
    private TextPaint mPaint;
    private int startX;//圓環起始X軸
    private int startY;//圓環起始Y軸
    private int mWidth;//ViewGroup的寬
    private int mHeight;//ViewGroup的高
    public CustomFiveRings(Context context) {
        this(context, null);
    }
    public CustomFiveRings(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }
    public CustomFiveRings(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        setBackgroundColor(Color.TRANSPARENT);
        mContext = context;
        mPaint = new TextPaint();
        mPaint.setAntiAlias(true);
        mPaint.setDither(true);
        mPaint.setTextSize(50);
        mPaint.setColor(Color.BLACK);
    }
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        //觸發所有子View的onMeasure函式去測量寬高
        measureChildren(widthMeasureSpec, heightMeasureSpec);
        //MeasureSpec封裝了父View傳遞給子View的佈局要求
        int wMode = MeasureSpec.getMode(widthMeasureSpec);
        int wSize = MeasureSpec.getSize(widthMeasureSpec);
        int hMode = MeasureSpec.getMode(heightMeasureSpec);
        int hSize = MeasureSpec.getSize(heightMeasureSpec);
        switch (wMode) {
            case MeasureSpec.EXACTLY:
                mWidth = wSize;
                break;
            case MeasureSpec.AT_MOST:
                //這裡應該先計算所有子view的寬度,暫時先寫死
                mWidth = wSize;
                break;
            case MeasureSpec.UNSPECIFIED:
                break;
        }
        switch (hMode) {
            case MeasureSpec.EXACTLY:
                mHeight = hSize;
                break;
            case MeasureSpec.AT_MOST:
                //這裡應該先計算所有子view的高度,暫時先寫死
                mHeight = hSize;
                // mHeight = getCircleHeight() / 2 * 3;
                break;
            case MeasureSpec.UNSPECIFIED:
                break;
        }
        setMeasuredDimension(mWidth, mHeight);
    }
    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
    }
    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        int childNum = getChildCount();
        startX = startY = 0;
        int gap = 10;//同一行圓圈之間的間隔
        int screenWidth = DpUtil.getScreenSizeWidth(mContext);
        int firstTotalWidth = 0;//第一行子View的總寬度
        int secondTotalWidth = 0;//第二行子View的總寬度
        for (int i = 0; i < childNum; i++) {
            View childView = getChildAt(i);
            int childWidth = childView.getMeasuredWidth();
            if (i <= 2) {
                //前三個總寬度
                firstTotalWidth += childWidth;
            } else {
                //後兩個總寬度
                secondTotalWidth += childWidth;
            }
        }
        int leftFMargin = (screenWidth - firstTotalWidth - gap * 2) / 2;
        int leftSMargin = (screenWidth - secondTotalWidth - gap) / 2;
        for (int i = 0; i < childNum; i++) {
            View childView = getChildAt(i);
            int childWidth = childView.getMeasuredWidth();
            int childHeight = childView.getMeasuredHeight();
            if (i <= 2) {
                //排列前三個圓圈
                if (i == 0) {
                    childView.layout(leftFMargin + startX, startY, leftFMargin + startX + childWidth, startY + childHeight);
                    startX += childWidth;
                } else {
                    childView.layout(leftFMargin + startX + gap, startY, leftFMargin + startX + gap + childWidth, startY + childHeight);                    startX += (childWidth + gap);
                }
                if (i == 2) {
                    startX = 0;
                    startY += childHeight / 2;
                }
            } else {
                //排列後兩個圓圈
                if (i == 3) {
                    childView.layout(leftSMargin + startX, startY, leftSMargin + startX + childWidth, startY + childHeight);
                    startX += childWidth;
                } else {
                    childView.layout(leftSMargin + startX + gap, startY, leftSMargin + startX + gap + childWidth, startY + childHeight);                    startX += (childWidth + gap);
                }
            }
        }
    }
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        int screenWidth = DpUtil.getScreenSizeWidth(mContext);
        String upStr = "同一個世界,同一個夢想";
        String downStr = "One World,One Dream";
        canvas.drawText(upStr, (screenWidth - mPaint.measureText(upStr)) / 2, getCircleHeight() / 2 * 3 + 60, mPaint);
        canvas.drawText(downStr, (screenWidth - mPaint.measureText(downStr)) / 2, getCircleHeight() / 2 * 3 + 120, mPaint);
    } 
   /**
     * 獲得圓環高度
     *
     * @return 圓環高度
     */
    private int getCircleHeight() {
        //5個圓環大小是一樣的,這裡就直接取第一個了
        View childView = getChildAt(0);
        return childView.getMeasuredHeight();
    }}
複製程式碼

相關文章