Android自定义View-画一个Chrome浏览器Logo

自定义View--画一个Chrome浏览器的Logo

如题,用自定义View来绘制一个Chrome浏览器的Logo,就是这个

logo

其实这个就是最终完成的作品,看上去还不错吧。这篇文章就手把手教你怎么用自定义View实现Chrome浏览器的Logo的绘制,其实还是很简单的,不过通过自己实现一遍,可以加深自己对自定义View流程的理解,以及对Path,Paint,Canvas的理解。

一 准备工作

先在网上下载一张谷歌浏览器的图标备用,然后用PS或者其他的颜色吸管工具,把Logo的颜色吸取到。为了简单,我们就把Logo按照四种颜色来处理。这里直接给大家列出色值,分别是红(0xFFCC5A4C),黄(0xFFF8CC5F),绿(0xFF4C9E66),蓝(0xFF5A8CEE)。

在绘制Logo之前,我们观察一下整个Logo就会发现,整个Logo其实是由一个蓝色小圆和一个三个相同的红黄绿三部分组成的圆环构成的。其实可以理解为三个圆,我大概测量了一下,蓝色小圆的半径大概是最大圆的4/10,中间的小圆是最大圆的半径的二分之一,我们姑且按照这样来计算。

logo-axes

外围圆环的三个部分其实形状都是一样的,只是颜色不一样而已。因此我们只需要绘制出其中一个,其他两个都可以通过旋转Canvas来绘制得到。下面我们就来分析一下怎么来绘制。

以上面的红色块为例,这一块可以看作是由弧AB,线段BC,弧CD,线段DA组成的封闭图形。显然这个图形是不规则的,而且不太容易通过组合来绘制得到,这个时候我们就需要强大的Path出场了,Path对这些不规则形状的绘制就是手到擒来。

明确了绘制方法,我们就需要精确计算A、B、C、D的坐标。首先是三个相同部分均分的圆,所以弧AB和弧CD毫无疑问应该是120°。假设O点坐标为 ( O x , O y ) (O_x, O_y) (Ox,Oy),OC的长度为R,OA=OB=R/2,所以我们可以得出结论∠BOC=60°,所以A点坐标就是 A x = O x − R ∗ 3 / 4 A_x = O_x-R*\sqrt{3}/4 Ax=OxR3 /4 , A y = O y + R / 4 A_y = O_y+R/4 Ay=Oy+R/4
B坐标为, B x = O x B_x = O_x Bx=Ox , B y = O y − R / 2 B_y = O_y-R/2 By=OyR/2;
C坐标为, C x = O x + R ∗ 3 / 2 C_x = O_x+R*\sqrt{3}/2 Cx=Ox+R3 /2 , C y = O y − R / 2 C_y = O_y-R/2 Cy=OyR/2;
D坐标为, D x = O x − R ∗ 3 / 2 D_x = O_x-R*\sqrt{3}/2 Dx=OxR3 /2 , D y = O y − R / 2 D_y = O_y-R/2 Dy=OyR/2;

一切准备就绪,下面开始自定义View的部分。

二 View的绘制

首先新建一个ChromeLogoView,继承自andorid.view.View,然后初始化三个构造函数。

public class ChromeLogoView extends View {

    public ChromeLogoView(Context context) {
        this(context, null);
    }

    public ChromeLogoView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

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

    private void init() {
    }
}

然后定义几个常量备用,分别是各个部分的颜色值和圆的比例,而且我们应该还需要一个变量mRadius来记录当前最大圆的半径,这个半径就取当前View宽高的较小的那一边的一半(保证能画一个完整的圆)。

    private static final int DEFAULT_WIDTH = 100;
    private static final int DEFAULT_HEIGHT = 100;
    private static final float SQRT_3 = (float) Math.sqrt(3);

    /**
     * the smaller blue circle inside, the radius ratio
     */
    private final static float INNER_RADIUS_RATIO = 400f / 1024f;

    @ColorInt
    private final static int COLOR_YELLOW = 0xFFF8CC5F;
    @ColorInt
    private final static int COLOR_RED = 0xFFCC5A4C;
    @ColorInt
    private final static int COLOR_GREEN = 0xFF4C9E66;
    @ColorInt
    private final static int COLOR_BLUE = 0xFF5A8CEE;
    private int[] colors = new int[]{COLOR_RED, COLOR_GREEN, COLOR_YELLOW};

    /**
     * view width
     */
    private int mWidth = DEFAULT_WIDTH;

    /**
     * view height
     */
    
    private int mHeight = DEFAULT_HEIGHT;

    /**
     * logo radius
     */
    private int mRadius = DEFAULT_WIDTH >> 1;

除此之外,我们还需要A、B、C、D、O四个点的坐标信息,以及画笔Paint和一个Path。

    /**
     * logo center point O.
     */
    private PointF pointO;
    private PointF pointA, pointB, pointC, pointD;
    private Path itemPath;
    private Paint itemPaint;

然后我们在init函数中初始化所有变量。

    private void init() {
        pointO = new PointF();
        pointA = new PointF();
        pointB = new PointF();
        pointC = new PointF();
        pointD = new PointF();
        
        itemPath = new Path();
        itemPaint = new Paint();
        itemPaint.setColor(colors[0]);
        itemPaint.setAntiAlias(true);
        itemPaint.setStyle(Paint.Style.FILL_AND_STROKE);
    }

我们需要在View的宽、高度改变的时候重置mRadius,所以需要重写onSizeChanged方法。

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        setMeasuredDimension(
            getDefaultSize(DEFAULT_WIDTH, widthMeasureSpec),
            getDefaultSize(DEFAULT_HEIGHT, heightMeasureSpec)
        );
    }


    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mWidth = w;
        mHeight = h;
        mRadius = Math.min(mWidth, mHeight) >> 1;
    }

接下来就是重点的绘制了。我们需要重写View的onDraw方法。

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

        //set ponits'postion
        float innerRadius = (mRadius >> 1);
        pointO.set(mWidth >> 1, mHeight >> 1);
        pointA.set(pointO.x - (mRadius >> 2) * SQRT_3, pointO.y + (mRadius >> 2));
        pointC.set(pointO.x + innerRadius * SQRT_3, pointO.y - innerRadius);

        //绘制其中一块
        itemPath.reset();
        //设置Path的起点坐标
        itemPath.moveTo(pointA.x, pointA.y);

        //绘制弧AB
        itemPath.arcTo(pointO.x - innerRadius, pointO.y - innerRadius,
                pointO.x + innerRadius, pointO.y + innerRadius,
                150f, 120f, true);

        //绘制线段BC
        itemPath.lineTo(pointC.x, pointC.y);

        //绘制弧CD
        itemPath.arcTo(pointO.x - mRadius, pointO.y - mRadius,
                pointO.x + mRadius, pointO.y + mRadius,
                -30f, -120f, true);

        //绘制线段DA
        itemPath.lineTo(pointA.x, pointA.y);

        //闭合Path,到这里我们算是把整个单块的路径给描绘出来了。
        itemPath.close();

        //绘制单个 1/3的图形(红色区域)
        itemPaint.setColor(colors[0]);
        canvas.drawPath(itemPath, itemPaint);

        //逆时针旋转120度,绘制绿色区域
        canvas.rotate(-120f, pointO.x, pointO.y);
        itemPaint.setColor(colors[1]);
        canvas.drawPath(itemPath, itemPaint);

        //逆时针继续旋转120度,绘制最后一块(黄色区域)
        canvas.rotate(-120f, pointO.x, pointO.y);
        itemPaint.setColor(colors[2]);
        canvas.drawPath(itemPath, itemPaint);

        //绘制中心的蓝色小圆
        itemPaint.setColor(COLOR_BLUE);
        canvas.drawCircle(pointO.x, pointO.y, mRadius * INNER_RADIUS_RATIO, itemPaint);
    }

代码中用到的知识点就是,Path的用法以及Canvas的旋转,注释中我也写的很详细了。关于Path的用法,大家可以自己去查看一下相关API,还是挺简单的,我就不多讲了。值得说一下的就是Path.arcTo函数的参数。

public void arcTo(float left, float top, float right, float bottom, 
                  float startAngle, float sweepAngle, boolean forceMoveTo)

这个arcTo函数其实绘制一段弧线,包括椭圆和圆的都可以,取决于left、top、right、bottom参数,其实就是代表椭圆(圆)的外接矩形的左上角坐标,和右下角坐标。startAngle是开始绘制的弧度,这个弧度是与x轴的夹角,顺时针方向sweepAngle代表绘制的弧线的度数,也是顺时针方向,如果这个参数是负值(-100f),就是逆时针绘制100度。forceMoveTo,这个参数就比较有意思了,如果设置为true,则path的绘制点就相应的移动到了弧线的末;如果设置为false,则path的绘制点还停留在弧线的额起始点。我们这里明显是要连续绘制的,所以要把这个参数设置成true。如果要设置成false,则还要手动去移动path的绘制点,反而麻烦了许多。因此这里我们的B点和D点其实都不要计算了。

可以给大家看看仅仅绘制一块时的样子,只需要把下面旋转绘制的代码注释掉就可以了。

single-part

三 源代码


import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PointF;
import android.util.AttributeSet;
import android.view.View;

import androidx.annotation.ColorInt;

/**
 *  @author newcoderzZ
 */
public class ChromeLogoView extends View {

    private static final int DEFAULT_WIDTH = 100;
    private static final int DEFAULT_HEIGHT = 100;
    private static final float SQRT_3 = (float) Math.sqrt(3);

    /**
     * the smaller blue circle inside, the radius ratio
     */
    private final static float INNER_RADIUS_RATIO = 400f / 1024f;

    @ColorInt
    private final static int COLOR_YELLOW = 0xFFF8CC5F;
    @ColorInt
    private final static int COLOR_RED = 0xFFCC5A4C;
    @ColorInt
    private final static int COLOR_GREEN = 0xFF4C9E66;
    @ColorInt
    private final static int COLOR_BLUE = 0xFF5A8CEE;
    private int[] colors = new int[]{COLOR_RED, COLOR_GREEN, COLOR_YELLOW};

    /**
     * view width
     */
    private int mWidth = DEFAULT_WIDTH;

    /**
     * view height
     */
    
    private int mHeight = DEFAULT_HEIGHT;

    /**
     * logo radius
     */
    private int mRadius = DEFAULT_WIDTH >> 1;

    /**
     * logo center point O.
     */
    private PointF pointO;
    private PointF pointA, pointC;
    private Path itemPath;
    private Paint itemPaint;

    public ChromeLogoView(Context context) {
        this(context, null);
    }

    public ChromeLogoView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

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

    private void init() {
        pointO = new PointF();
        pointA = new PointF();
        //pointB = new PointF();
        pointC = new PointF();
        //pointD = new PointF();
        
        itemPath = new Path();
        itemPaint = new Paint();
        itemPaint.setColor(colors[0]);
        itemPaint.setAntiAlias(true);
        itemPaint.setStyle(Paint.Style.FILL_AND_STROKE);
    }

     @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        setMeasuredDimension(
            getDefaultSize(DEFAULT_WIDTH, widthMeasureSpec),
            getDefaultSize(DEFAULT_HEIGHT, heightMeasureSpec)
        );
    }


    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mWidth = w;
        mHeight = h;
        mRadius = Math.min(mWidth, mHeight) >> 1;
    }

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

        //set ponits'postion
        float innerRadius = (mRadius >> 1);
        pointO.set(mWidth >> 1, mHeight >> 1);
        pointA.set(pointO.x - (mRadius >> 2) * SQRT_3, pointO.y + (mRadius >> 2));
        pointC.set(pointO.x + innerRadius * SQRT_3, pointO.y - innerRadius);

        //绘制其中一块
        itemPath.reset();
        //设置Path的起点坐标
        itemPath.moveTo(pointA.x, pointA.y);

        //绘制弧AB
        itemPath.arcTo(pointO.x - innerRadius, pointO.y - innerRadius,
                pointO.x + innerRadius, pointO.y + innerRadius,
                150f, 120f, true);

        //绘制线段BC
        itemPath.lineTo(pointC.x, pointC.y);

        //绘制弧CD
        itemPath.arcTo(pointO.x - mRadius, pointO.y - mRadius,
                pointO.x + mRadius, pointO.y + mRadius,
                -30f, -120f, true);

        //绘制线段DA
        itemPath.lineTo(pointA.x, pointA.y);

        //闭合Path,到这里我们算是把整个单块的路径给描绘出来了。
        itemPath.close();

        //绘制单个 1/3的图形(红色区域)
        itemPaint.setColor(colors[0]);
        canvas.drawPath(itemPath, itemPaint);

        //逆时针旋转120度,绘制绿色区域
        canvas.rotate(-120f, pointO.x, pointO.y);
        itemPaint.setColor(colors[1]);
        canvas.drawPath(itemPath, itemPaint);

        //逆时针继续旋转120度,绘制最后一块(黄色区域)
        canvas.rotate(-120f, pointO.x, pointO.y);
        itemPaint.setColor(colors[2]);
        canvas.drawPath(itemPath, itemPaint);

        //绘制中心的蓝色小圆
        itemPaint.setColor(COLOR_BLUE);
        canvas.drawCircle(pointO.x, pointO.y, mRadius * INNER_RADIUS_RATIO, itemPaint);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
自定义一个水波进度 View,你需要完成以下几个步骤: 1. 创建一个自定义 View 类,并在构造函数中初始化一些必要的属性,如颜色、线宽等。 2. 重写 onSizeChanged() 方法,在该方法中获取 View 的宽度和高度,并计算出进度条的半径、圆心等相关参数。 3. 重写 onDraw() 方法,在该方法中绘制水波纹效果。 4. 在自定义 View 中添加一个 setProgress() 方法,用于设置进度条的进度。 5. 在布局文件中引入自定义 View,设置 layout_width 和 layout_height 属性,并在代码中调用 setProgress() 方法设置进度条的进度。 下面是一个简单的自定义水波进度 View 的代码示例: ```java public class WaterWaveProgressView extends View { private Paint mPaint; private int mWidth, mHeight; private float mRadius; private float mProgress; public WaterWaveProgressView(Context context) { super(context); init(); } public WaterWaveProgressView(Context context, AttributeSet attrs) { super(context, attrs); init(); } public WaterWaveProgressView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } private void init() { mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mPaint.setColor(Color.BLUE); mPaint.setStrokeWidth(5); mPaint.setStyle(Paint.Style.STROKE); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); mWidth = w; mHeight = h; mRadius = Math.min(mWidth, mHeight) / 2 * 0.8f; } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); canvas.drawCircle(mWidth / 2, mHeight / 2, mRadius, mPaint); float angle = mProgress / 100 * 360; canvas.drawArc(mWidth / 2 - mRadius, mHeight / 2 - mRadius, mWidth / 2 + mRadius, mHeight / 2 + mRadius, -90, angle, false, mPaint); } public void setProgress(float progress) { mProgress = progress; invalidate(); } } ``` 通过调用 setProgress() 方法来更新进度条的进度,如下所示: ```java WaterWaveProgressView progressView = findViewById(R.id.progress_view); progressView.setProgress(50); // 设置进度为 50% ``` 在布局文件中引入自定义 View: ```xml <com.example.waterwaveprogressview.WaterWaveProgressView android:id="@+id/progress_view" android:layout_width="150dp" android:layout_height="150dp" /> ``` 这样就能够实现一个简单的水波进度 View 了。如果需要更加复杂的效果,可以在 onDraw() 方法中绘制多个水波纹,或者使用 Path 绘制波形等。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值