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

简介: 本篇来看看自定义ViewGroup

接上篇,
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();
    }}
相关文章
|
6月前
|
Android开发 UED 计算机视觉
Android自定义view之线条等待动画(灵感来源:金铲铲之战)
本文介绍了一款受游戏“金铲铲之战”启发的Android自定义View——线条等待动画的实现过程。通过将布局分为10份,利用`onSizeChanged`测量最小长度,并借助画笔绘制动态线条,实现渐变伸缩效果。动画逻辑通过四个变量控制线条的增长与回退,最终形成流畅的等待动画。代码中详细展示了画笔初始化、线条绘制及动画更新的核心步骤,并提供完整源码供参考。此动画适用于加载场景,提升用户体验。
518 5
Android自定义view之线条等待动画(灵感来源:金铲铲之战)
|
6月前
|
Android开发
Android自定义view之利用PathEffect实现动态效果
本文介绍如何在Android自定义View中利用`PathEffect`实现动态效果。通过改变偏移量,结合`PathEffect`的子类(如`CornerPathEffect`、`DashPathEffect`、`PathDashPathEffect`等)实现路径绘制的动态变化。文章详细解析了各子类的功能与参数,并通过案例代码展示了如何使用`ComposePathEffect`组合效果,以及通过修改偏移量实现动画。最终效果为一个菱形图案沿路径运动,源码附于文末供参考。
115 0
|
6月前
|
XML Java Android开发
Android自定义view之网易云推荐歌单界面
本文详细介绍了如何通过自定义View实现网易云音乐推荐歌单界面的效果。首先,作者自定义了一个圆角图片控件`MellowImageView`,用于绘制圆角矩形图片。接着,通过将布局放入`HorizontalScrollView`中,实现了左右滑动功能,并使用`ViewFlipper`添加图片切换动画效果。文章提供了完整的代码示例,包括XML布局、动画文件和Java代码,最终展示了实现效果。此教程适合想了解自定义View和动画效果的开发者。
322 65
Android自定义view之网易云推荐歌单界面
|
6月前
|
XML 前端开发 Android开发
一篇文章带你走近Android自定义view
这是一篇关于Android自定义View的全面教程,涵盖从基础到进阶的知识点。文章首先讲解了自定义View的必要性及简单实现(如通过三个构造函数解决焦点问题),接着深入探讨Canvas绘图、自定义属性设置、动画实现等内容。还提供了具体案例,如跑马灯、折线图、太极图等。此外,文章详细解析了View绘制流程(measure、layout、draw)和事件分发机制。最后延伸至SurfaceView、GLSurfaceView、SVG动画等高级主题,并附带GitHub案例供实践。适合希望深入理解Android自定义View的开发者学习参考。
668 84
|
6月前
|
前端开发 Android开发 UED
讲讲Android为自定义view提供的SurfaceView
本文详细介绍了Android中自定义View时使用SurfaceView的必要性和实现方式。首先分析了在复杂绘制逻辑和高频界面更新场景下,传统View可能引发卡顿的问题,进而引出SurfaceView作为解决方案。文章通过Android官方Demo展示了SurfaceView的基本用法,包括实现`SurfaceHolder.Callback2`接口、与Activity生命周期绑定、子线程中使用`lockCanvas()`和`unlockCanvasAndPost()`方法完成绘图操作。
188 3
|
Android开发
Android 自定义View 测量控件宽高、自定义viewgroup测量
Android 自定义View 测量控件宽高、自定义viewgroup测量
656 0
|
XML Android开发 数据格式
Android 中自定义ViewGroup实现流式布局的效果
Android 中自定义ViewGroup实现流式布局的效果
196 0
|
XML 前端开发 Android开发
Android自定义View-入门(明白自定义View和自定义ViewGroup)
为什么要自定义View? 主要是Andorid系统内置的View 无法实现我们的 需求,我们需要针对我们的业务需求定制我们想要的 View.
178 0
Android自定义View-入门(明白自定义View和自定义ViewGroup)
|
XML 前端开发 Android开发
android自定义View&自定义ViewGroup(上)
自定义View&自定义ViewGroup
210 0
|
XML Android开发 数据格式
Android自定义控件(十一)——自定义ViewGroup实现LinearLayout
Android自定义控件(十一)——自定义ViewGroup实现LinearLayout
639 0
下一篇
oss云网关配置