AndroidUI系列 - 自定义View手绘小黄人

2024-02-29 07:32

本文主要是介绍AndroidUI系列 - 自定义View手绘小黄人,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

总是想尝试各种自定义控件,来熟悉谷歌提供的一些自定绘图的方法,那就画一个小黄人吧。我在git上找到一个小换人的源码。它是按照比例计算的,有一定的公式,我觉得太麻烦了。就用自己的理解画了一个写死大小的小黄人。先给大家看看效果。
git上的小黄人是这样的。
这里写图片描述

我用代码画出来的是这样的。
这里写图片描述

其实有很多地方是不一样的,我会把两套代码都贴出来,有兴趣的朋友可以自己去研究研究。画完之后对Path和canvas有了很多新的认识。以后自定义什么复杂控件,这些都是扎实的基础。

那么在来整理一下,思路。从哪开始画,需要使用一些什么方法来进行绘制。
这里写图片描述

具体方法都有注释,想要研究的就要自己去花时间看代码了。
git上的代码。

import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.CornerPathEffect;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RectF;
import android.os.Build;
import android.util.AttributeSet;
import android.view.View;class MinionView extends View{private static final int DEFAULT_SIZE = 200; //View默认大小private int mWidthUnspecified;private int mHeightUnspecified;private Paint mPaint;private float mBodyWidth;private float mBodyHeight;private static final float BODY_SCALE = 0.6f;//身体主干占整个view的比重private static final float BODY_WIDTH_HEIGHT_SCALE = 0.6f; //        身体的比例设定为 w:h = 3:5private float mStrokeWidth = 4;//描边宽度private float mOffset;//计算时,部分需要 考虑找边偏移private float mRadius;//身体上下半圆的半径private int mColorClothes = Color.rgb(32, 116, 160);//衣服的颜色private int mColorBody = Color.rgb(249, 217, 70);//衣服的颜色private int mColorStroke = Color.BLACK;private RectF mBodyRect;private float mHandsHeight;//计算出吊带的高度时,可以用来做手的高度private float mFootHeight;//脚的高度,用来画脚部阴影时用public MinionView(Context context) {super(context);}public MinionView(Context context, AttributeSet attrs) {super(context, attrs);}public MinionView(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);}@TargetApi(Build.VERSION_CODES.LOLLIPOP)public MinionView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {super(context, attrs, defStyleAttr, defStyleRes);}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {setMeasuredDimension(measure(widthMeasureSpec, true), measure(heightMeasureSpec, false));}/*** @param origin* @param isWidth 是否在测量宽* @return*/private int measure(int origin, boolean isWidth) {int result;int specMode = MeasureSpec.getMode(origin);int specSize = MeasureSpec.getSize(origin);switch (specMode) {case MeasureSpec.EXACTLY:case MeasureSpec.AT_MOST:result = specSize;if (isWidth) {mWidthUnspecified = result;} else {mHeightUnspecified = result;}break;case MeasureSpec.UNSPECIFIED:default:if (isWidth) {//宽或高未指定的情况下,可以由另一端推算出来 - -如果两边都没指定就用默认值result = (int) (mHeightUnspecified * BODY_WIDTH_HEIGHT_SCALE);} else {result = (int) (mWidthUnspecified / BODY_WIDTH_HEIGHT_SCALE);}if (result == 0) {result = DEFAULT_SIZE;}break;}return result;}@Overrideprotected void onDraw(Canvas canvas) {initParams();initPaint();drawFeetShadow(canvas);//脚下的阴影drawFeet(canvas);//脚drawHands(canvas);//手drawBody(canvas);//身体drawClothes(canvas);//衣服drawEyesMouth(canvas);//眼睛,嘴巴drawBodyStroke(canvas);//最后画身体的描边,可以摭住一些过渡的棱角}private void initParams() {mBodyWidth = Math.min(getWidth(), getHeight() * BODY_WIDTH_HEIGHT_SCALE) * BODY_SCALE;mBodyHeight = Math.min(getWidth(), getHeight() * BODY_WIDTH_HEIGHT_SCALE) / BODY_WIDTH_HEIGHT_SCALE * BODY_SCALE;mStrokeWidth = Math.max(mBodyWidth / 50, mStrokeWidth);mOffset = mStrokeWidth / 2;mBodyRect = new RectF();mBodyRect.left = (getWidth() - mBodyWidth) / 2;mBodyRect.top = (getHeight() - mBodyHeight) / 2;mBodyRect.right = mBodyRect.left + mBodyWidth;mBodyRect.bottom = mBodyRect.top + mBodyHeight;mRadius = mBodyWidth / 2;mFootHeight = mRadius * 0.4333f;mHandsHeight =  (getHeight() + mBodyHeight) / 2   + mOffset - mRadius * 1.65f ;}private void drawBody(Canvas canvas) {initPaint();mPaint.setColor(mColorBody);mPaint.setStyle(Paint.Style.FILL);canvas.drawRoundRect(mBodyRect, mRadius, mRadius, mPaint);}private void drawBodyStroke(Canvas canvas) {initPaint();mPaint.setColor(mColorStroke);mPaint.setStrokeWidth(mStrokeWidth);mPaint.setStyle(Paint.Style.STROKE);canvas.drawRoundRect(mBodyRect, mRadius, mRadius, mPaint);}private void drawClothes(Canvas canvas) {initPaint();RectF rect = new RectF();rect.left = (getWidth() - mBodyWidth) / 2 + mOffset;rect.top = (getHeight() + mBodyHeight) / 2 - mRadius * 2 + mOffset;rect.right = rect.left + mBodyWidth - mOffset * 2;rect.bottom = rect.top + mRadius * 2 - mOffset * 2;mPaint.setColor(mColorClothes);mPaint.setStyle(Paint.Style.FILL);mPaint.setStrokeWidth(mStrokeWidth);canvas.drawArc(rect, 0, 180, true, mPaint);int h = (int) (mRadius * 0.5);int w = (int) (mRadius * 0.3);rect.left += w;rect.top = rect.top + mRadius - h;rect.right -= w;rect.bottom = rect.top + h;canvas.drawRect(rect, mPaint);//画横线initPaint();mPaint.setColor(mColorStroke);mPaint.setStyle(Paint.Style.FILL);mPaint.setStrokeWidth(mStrokeWidth);float[] pts = new float[20];//5条线pts[0] = rect.left - w;pts[1] = rect.top + h;pts[2] = pts[0] + w;pts[3] = pts[1];pts[4] = pts[2];pts[5] = pts[3] + mOffset;pts[6] = pts[4];pts[7] = pts[3] - h;pts[8] = pts[6] - mOffset;pts[9] = pts[7];pts[10] = pts[8] + (mRadius - w) * 2;pts[11] = pts[9];pts[12] = pts[10];pts[13] = pts[11] - mOffset;pts[14] = pts[12];pts[15] = pts[13] + h;pts[16] = pts[14] - mOffset;pts[17] = pts[15];pts[18] = pts[16] + w;pts[19] = pts[17];canvas.drawLines(pts, mPaint);//画左吊带initPaint();mPaint.setColor(mColorClothes);mPaint.setStrokeWidth(mStrokeWidth);mPaint.setStyle(Paint.Style.FILL);Path path = new Path();path.moveTo(rect.left - w - mOffset, mHandsHeight);path.lineTo(rect.left + h / 4, rect.top + h / 2);final float smallW = w / 2 * (float) Math.sin(Math.PI / 4);path.lineTo(rect.left + h / 4 + smallW, rect.top + h / 2 - smallW);final float smallW2 = w / (float) Math.sin(Math.PI / 4) / 2;path.lineTo(rect.left - w - mOffset, mHandsHeight - smallW2);canvas.drawPath(path, mPaint);initPaint();mPaint.setColor(mColorStroke);mPaint.setStrokeWidth(mStrokeWidth);mPaint.setStyle(Paint.Style.STROKE);canvas.drawPath(path, mPaint);initPaint();mPaint.setStyle(Paint.Style.FILL_AND_STROKE);canvas.drawCircle(rect.left + h / 5, rect.top + h / 4, mStrokeWidth*0.7f, mPaint);//画右吊带initPaint();mPaint.setColor(mColorClothes);mPaint.setStrokeWidth(mStrokeWidth);mPaint.setStyle(Paint.Style.FILL);path.reset();path.moveTo(rect.left - w + 2 * mRadius - mOffset, mHandsHeight);path.lineTo(rect.right - h / 4, rect.top + h / 2);path.lineTo(rect.right - h / 4 - smallW, rect.top + h / 2 - smallW);path.lineTo(rect.left - w + 2 * mRadius - mOffset, mHandsHeight - smallW2);canvas.drawPath(path, mPaint);initPaint();mPaint.setColor(mColorStroke);mPaint.setStrokeWidth(mStrokeWidth);mPaint.setStyle(Paint.Style.STROKE);canvas.drawPath(path, mPaint);initPaint();mPaint.setStyle(Paint.Style.FILL_AND_STROKE);canvas.drawCircle(rect.right - h / 5, rect.top + h / 4, mStrokeWidth*0.7f, mPaint);//中间口袋initPaint();mPaint.setColor(mColorStroke);mPaint.setStrokeWidth(mStrokeWidth);mPaint.setStyle(Paint.Style.STROKE);path.reset();float radiusBigPokect = w / 2.0f;path.moveTo(rect.left + 1.5f * w, rect.bottom - h / 4);path.lineTo(rect.right - 1.5f * w, rect.bottom - h / 4);path.lineTo(rect.right - 1.5f * w, rect.bottom + h / 4);path.addArc(rect.right - 1.5f * w - radiusBigPokect * 2, rect.bottom + h / 4 - radiusBigPokect,rect.right - 1.5f * w, rect.bottom + h / 4 + radiusBigPokect, 0, 90);path.lineTo(rect.left + 1.5f * w + radiusBigPokect, rect.bottom + h / 4 + radiusBigPokect);path.addArc(rect.left + 1.5f * w, rect.bottom + h / 4 - radiusBigPokect,rect.left + 1.5f * w + 2 * radiusBigPokect, rect.bottom + h / 4 + radiusBigPokect, 90, 90);path.lineTo(rect.left + 1.5f * w, rect.bottom - h / 4 - mOffset);canvas.drawPath(path, mPaint);//        下边一竖,分开裤子canvas.drawLine(mBodyRect.left + mBodyWidth / 2, mBodyRect.bottom - h * 0.8f, mBodyRect.left + mBodyWidth / 2, mBodyRect.bottom, mPaint);
//      左边的小口袋float radiusSamllPokect = w * 1.2f;canvas.drawArc(mBodyRect.left - radiusSamllPokect, mBodyRect.bottom - mRadius - radiusSamllPokect,mBodyRect.left + radiusSamllPokect, mBodyRect.bottom - mRadius + radiusSamllPokect, 80, -60, false, mPaint);
//      右边小口袋canvas.drawArc(mBodyRect.right - radiusSamllPokect, mBodyRect.bottom - mRadius - radiusSamllPokect,mBodyRect.right + radiusSamllPokect, mBodyRect.bottom - mRadius + radiusSamllPokect, 100, 60, false, mPaint);
//        canvas.drawArc(left + w/5,);}private void drawEyesMouth(Canvas canvas) {float eyesOffset = mRadius * 0.1f;//眼睛中心处于上半圆直径 往上的高度偏移mPaint.setStrokeWidth(mStrokeWidth * 5);
//        计算眼镜带弧行的半径 分两段,以便眼睛中间有隔开的效果float radiusGlassesRibbon = (float) (mRadius / Math.sin(Math.PI / 20));RectF rect = new RectF();rect.left = mBodyRect.left + mRadius - radiusGlassesRibbon;rect.top = mBodyRect.top + mRadius - (float) (mRadius / Math.tan(Math.PI / 20)) - radiusGlassesRibbon - eyesOffset;rect.right = rect.left + radiusGlassesRibbon * 2;rect.bottom = rect.top + radiusGlassesRibbon * 2;canvas.drawArc(rect, 81, 3, false, mPaint);canvas.drawArc(rect, 99, -3, false, mPaint);//眼睛半径float radiusEyes = mRadius / 3;initPaint();mPaint.setColor(Color.WHITE);mPaint.setStrokeWidth(mStrokeWidth);mPaint.setStyle(Paint.Style.FILL);canvas.drawCircle(mBodyRect.left + mBodyWidth / 2 - radiusEyes - mOffset, mBodyRect.top + mRadius - eyesOffset, radiusEyes, mPaint);canvas.drawCircle(mBodyRect.left + mBodyWidth / 2 + radiusEyes + mOffset, mBodyRect.top + mRadius - eyesOffset, radiusEyes, mPaint);mPaint.setColor(mColorStroke);mPaint.setStyle(Paint.Style.STROKE);canvas.drawCircle(mBodyRect.left + mBodyWidth / 2 - radiusEyes - mOffset, mBodyRect.top + mRadius - eyesOffset, radiusEyes, mPaint);canvas.drawCircle(mBodyRect.left + mBodyWidth / 2 + radiusEyes + mOffset, mBodyRect.top + mRadius - eyesOffset, radiusEyes, mPaint);final float radiusEyeballBlack = radiusEyes / 3;mPaint.setStyle(Paint.Style.FILL);canvas.drawCircle(mBodyRect.left + mBodyWidth / 2 - radiusEyes - mOffset, mBodyRect.top + mRadius - eyesOffset, radiusEyeballBlack, mPaint);canvas.drawCircle(mBodyRect.left + mBodyWidth / 2 + radiusEyes + mOffset, mBodyRect.top + mRadius - eyesOffset, radiusEyeballBlack, mPaint);mPaint.setColor(Color.WHITE);final float radiusEyeballWhite = radiusEyeballBlack / 2;canvas.drawCircle(mBodyRect.left + mBodyWidth / 2 - radiusEyes + radiusEyeballWhite - mOffset * 2,mBodyRect.top + mRadius - radiusEyeballWhite + mOffset - eyesOffset,radiusEyeballWhite, mPaint);canvas.drawCircle(mBodyRect.left + mBodyWidth / 2 + radiusEyes + radiusEyeballWhite,mBodyRect.top + mRadius - radiusEyeballWhite + mOffset - eyesOffset,radiusEyeballWhite, mPaint);//        画嘴巴,因为位置和眼睛有相对关系,所以写在一块mPaint.setColor(mColorStroke);mPaint.setStyle(Paint.Style.STROKE);mPaint.setStrokeWidth(mStrokeWidth);float radiusMonth = mRadius;rect.left = mBodyRect.left;rect.top = mBodyRect.top - radiusMonth / 2.5f;rect.right = rect.left + radiusMonth * 2;rect.bottom = rect.top + radiusMonth * 2;canvas.drawArc(rect, 95, -20, false, mPaint);}private void drawFeet(Canvas canvas) {mPaint.setStrokeWidth(mStrokeWidth);mPaint.setColor(mColorStroke);mPaint.setStyle(Paint.Style.FILL_AND_STROKE);float radiusFoot = mRadius / 3 * 0.4f;float leftFootStartX = mBodyRect.left + mRadius - mOffset * 2;float leftFootStartY = mBodyRect.bottom - mOffset;float footWidthA = mRadius * 0.5f;//脚宽度大-到半圆结束float footWidthB = footWidthA / 3;//脚宽度-比较细的部分//      左脚Path path = new Path();path.moveTo(leftFootStartX, leftFootStartY);path.lineTo(leftFootStartX, leftFootStartY + mFootHeight);path.lineTo(leftFootStartX - footWidthA + radiusFoot, leftFootStartY + mFootHeight);RectF rectF = new RectF();rectF.left = leftFootStartX - footWidthA;rectF.top = leftFootStartY + mFootHeight - radiusFoot * 2;rectF.right = rectF.left + radiusFoot * 2;rectF.bottom = rectF.top + radiusFoot * 2;path.addArc(rectF, 90, 180);path.lineTo(rectF.left + radiusFoot + footWidthB, rectF.top);path.lineTo(rectF.left + radiusFoot + footWidthB, leftFootStartY);path.lineTo(leftFootStartX, leftFootStartY);canvas.drawPath(path, mPaint);//      右脚float rightFootStartX = mBodyRect.left + mRadius + mOffset * 2;float rightFootStartY = leftFootStartY;path.reset();path.moveTo(rightFootStartX, rightFootStartY);path.lineTo(rightFootStartX, rightFootStartY + mFootHeight);path.lineTo(rightFootStartX + footWidthA - radiusFoot, rightFootStartY + mFootHeight);rectF.left = rightFootStartX + footWidthA - radiusFoot * 2;rectF.top = rightFootStartY + mFootHeight - radiusFoot * 2;rectF.right = rectF.left + radiusFoot * 2;rectF.bottom = rectF.top + radiusFoot * 2;path.addArc(rectF, 90, -180);path.lineTo(rectF.right - radiusFoot - footWidthB, rectF.top);path.lineTo(rectF.right - radiusFoot - footWidthB, rightFootStartY);path.lineTo(rightFootStartX, rightFootStartY);canvas.drawPath(path, mPaint);}private void drawFeetShadow(Canvas canvas) {mPaint.setColor(getResources().getColor(android.R.color.darker_gray));canvas.drawOval(mBodyRect.left + mBodyWidth * 0.15f, mBodyRect.bottom - mOffset + mFootHeight,mBodyRect.right - mBodyWidth * 0.15f, mBodyRect.bottom - mOffset + mFootHeight + mStrokeWidth * 1.3f, mPaint);}private void drawHands(Canvas canvas) {mPaint.setStrokeWidth(mStrokeWidth);mPaint.setStyle(Paint.Style.FILL_AND_STROKE);mPaint.setColor(mColorBody);//        左手Path path = new Path();float hypotenuse = mBodyRect.bottom - mRadius - mHandsHeight;float radiusHand = hypotenuse / 6;mPaint.setPathEffect(new CornerPathEffect(radiusHand));path.moveTo(mBodyRect.left, mHandsHeight);path.lineTo(mBodyRect.left - hypotenuse / 2, mHandsHeight + hypotenuse / 2);path.lineTo(mBodyRect.left + mOffset, mBodyRect.bottom - mRadius + mOffset);path.lineTo(mBodyRect.left  , mHandsHeight);//增加兼容性,path没闭合在一起机子上会使手的下面的点没办法与裤子重合canvas.drawPath(path, mPaint);mPaint.setStrokeWidth(mStrokeWidth);mPaint.setStyle(Paint.Style.STROKE);mPaint.setColor(mColorStroke);canvas.drawPath(path, mPaint);//        右手path.reset();mPaint.setStyle(Paint.Style.FILL);mPaint.setColor(mColorBody);path.moveTo(mBodyRect.right, mHandsHeight);path.lineTo(mBodyRect.right + hypotenuse / 2, mHandsHeight + hypotenuse / 2);path.lineTo(mBodyRect.right  - mOffset, mBodyRect.bottom - mRadius + mOffset);path.lineTo(mBodyRect.right, mHandsHeight);canvas.drawPath(path, mPaint);mPaint.setStrokeWidth(mStrokeWidth);mPaint.setStyle(Paint.Style.STROKE);mPaint.setColor(mColorStroke);canvas.drawPath(path, mPaint);//        一个慢动作  - -||| 拐点内侧path.reset();mPaint.setStyle(Paint.Style.FILL);path.moveTo(mBodyRect.left, mHandsHeight + hypotenuse / 2 - mStrokeWidth);path.lineTo(mBodyRect.left - mStrokeWidth * 2, mHandsHeight + hypotenuse / 2 + mStrokeWidth * 2);path.lineTo(mBodyRect.left, mHandsHeight + hypotenuse / 2 + mStrokeWidth);path.lineTo(mBodyRect.left, mHandsHeight + hypotenuse / 2 - mStrokeWidth);canvas.drawPath(path, mPaint);path.reset();path.moveTo(mBodyRect.right, mHandsHeight + hypotenuse / 2 - mStrokeWidth);path.lineTo(mBodyRect.right + mStrokeWidth * 2, mHandsHeight + hypotenuse / 2 + mStrokeWidth * 2);path.lineTo(mBodyRect.right, mHandsHeight + hypotenuse / 2 + mStrokeWidth);path.lineTo(mBodyRect.right, mHandsHeight + hypotenuse / 2 - mStrokeWidth);canvas.drawPath(path, mPaint);}private void initPaint() {if (mPaint == null) {mPaint = new Paint();} else {mPaint.reset();}mPaint.setAntiAlias(true);//边缘无锯齿}/*public void randomBodyColor() {Random random = new Random();mColorBody = Color.rgb(random.nextInt(255), random.nextInt(255), random.nextInt(255));invalidate();}*/
}

那么在看看我的代码与他的代码的区别。

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.View;/*** Created by ShuWen on 2017/6/27.*/public class MinionViewNew extends View {private static final int DEFAULT_SIZE = 200; //View默认大小private int mWidthUnspecified;private int mHeightUnspecified;private Paint mPaint;private float mBodyWidth;private float mBodyHeight;private static final float BODY_SCALE = 0.6f;//身体主干占整个view的比重private static final float BODY_WIDTH_HEIGHT_SCALE = 0.6f; //        身体的比例设定为 w:h = 3:5private float mStrokeWidth = 10;//描边宽度private float mOffset;//计算时,部分需要 考虑找边偏移private float mRadius;//身体上下半圆的半径private int mColorClothes = Color.rgb(32, 116, 160);//衣服的颜色private int mColorBody = Color.rgb(249, 217, 70);//衣服的颜色private int mColorStroke = Color.BLACK;private RectF mBodyRect;private float mHandsHeight;//计算出吊带的高度时,可以用来做手的高度private float mFootHeight;//脚的高度,用来画脚部阴影时用public MinionViewNew(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);}public MinionViewNew(Context context, AttributeSet attrs) {super(context, attrs);}public MinionViewNew(Context context) {super(context);}@Overrideprotected void onDraw(Canvas canvas) {initParams();initPaint();drawFeetShadow(canvas);drawFeet(canvas);//脚drawBody(canvas);//身体drawEyesMouth(canvas);//眼睛,嘴巴drawHands(canvas);//手drawClothes(canvas);//衣服}private void drawClothes(Canvas canvas) {initPaint();//肚子颜色mPaint.setColor(mColorClothes);mPaint.setStyle(Paint.Style.FILL);mPaint.setStrokeWidth(mStrokeWidth);RectF rectF = new RectF();rectF.left = getWidth()/2 - 246;  //xrectF.top = getHeight()/2 - 110; //yrectF.right = rectF.left + 492;rectF.bottom = rectF.top + 505;canvas.drawArc(rectF,0,180,true,mPaint);Path path = new Path();path.moveTo(getWidth()/2-250,getHeight()/2+144);path.lineTo(getWidth()/2-150,getHeight()/2+144);path.lineTo(getWidth()/2-150,getHeight()/2+50);path.lineTo(getWidth()/2+150,getHeight()/2+50);path.lineTo(getWidth()/2+150,getHeight()/2+144);path.lineTo(getWidth()/2+250,getHeight()/2+144);mPaint.setColor(mColorClothes);mPaint.setStyle(Paint.Style.FILL);canvas.drawPath(path,mPaint);mPaint.setColor(Color.BLACK);mPaint.setStrokeWidth(8);mPaint.setStyle(Paint.Style.STROKE);canvas.drawPath(path,mPaint);//左吊带path.moveTo(getWidth()/2 - 250,getHeight()/2 - 50);path.lineTo(getWidth()/2 - 110,getHeight()/2 + 70);path.lineTo(getWidth()/2 - 130,getHeight()/2 + 100);path.lineTo(getWidth()/2 - 250,getHeight()/2 - 10);mPaint.setColor(mColorClothes);mPaint.setStyle(Paint.Style.FILL);canvas.drawPath(path,mPaint);mPaint.setColor(Color.BLACK);mPaint.setStyle(Paint.Style.STROKE);canvas.drawPath(path,mPaint);//右吊带path.moveTo(getWidth()/2 + 250,getHeight()/2 - 50);path.lineTo(getWidth()/2 + 110,getHeight()/2 + 70);path.lineTo(getWidth()/2 + 130,getHeight()/2 + 100);path.lineTo(getWidth()/2 + 250,getHeight()/2 - 10);mPaint.setColor(mColorClothes);mPaint.setStyle(Paint.Style.FILL);canvas.drawPath(path,mPaint);mPaint.setColor(Color.BLACK);mPaint.setStyle(Paint.Style.STROKE);canvas.drawPath(path,mPaint);//画衣服口袋path.moveTo(getWidth()/2-80,getHeight()/2+144);path.lineTo(getWidth()/2+80,getHeight()/2+144);rectF.left = getWidth()/2-80;rectF.top = getHeight()/2+44;rectF.right = rectF.left + 160;rectF.bottom = rectF.top + 200;path.addArc(rectF,0,180);canvas.drawPath(path,mPaint);//裤子线path.moveTo(getWidth()/2,getHeight()/2+314);path.lineTo(getWidth()/2,getHeight()/2+394);canvas.drawPath(path,mPaint);//裤子口袋path.moveTo(getWidth()/2-218,getHeight()/2+254);path.lineTo(getWidth()/2-170,getHeight()/2+184);canvas.drawPath(path,mPaint);path.moveTo(getWidth()/2+218,getHeight()/2+254);path.lineTo(getWidth()/2+170,getHeight()/2+184);canvas.drawPath(path,mPaint);}private void drawEyesMouth(Canvas canvas) {//眼眶initPaint();mPaint.setColor(Color.WHITE);mPaint.setStyle(Paint.Style.FILL_AND_STROKE);canvas.drawCircle(getWidth() / 2 - 75,getHeight() / 2 - 180,70,mPaint); //左眼眶canvas.drawCircle(getWidth() / 2 + 75,getHeight() / 2 - 180,70,mPaint); //右眼眶mPaint.setStrokeWidth(mStrokeWidth);mPaint.setStyle(Paint.Style.STROKE);mPaint.setColor(Color.BLACK);canvas.drawCircle(getWidth() / 2 - 75,getHeight() / 2 - 180,70,mPaint);canvas.drawCircle(getWidth() / 2 + 75,getHeight() / 2 - 180,70,mPaint);//眼珠子initPaint();mPaint.setColor(Color.BLACK);mPaint.setStyle(Paint.Style.FILL_AND_STROKE);canvas.drawCircle(getWidth() / 2 - 75,getHeight() / 2 - 180,40,mPaint);canvas.drawCircle(getWidth() / 2 + 75,getHeight() / 2 - 180,40,mPaint);//眼睛initPaint();mPaint.setColor(Color.WHITE);mPaint.setStyle(Paint.Style.FILL_AND_STROKE);canvas.drawCircle(getWidth() / 2 - 60,getHeight() / 2 - 195,20,mPaint);canvas.drawCircle(getWidth() / 2 + 90,getHeight() / 2 - 195,20,mPaint);//画嘴巴initPaint();mPaint.setColor(Color.BLACK);mPaint.setStyle(Paint.Style.FILL_AND_STROKE);mPaint.setStrokeWidth(mStrokeWidth);RectF rectF = new RectF();rectF.left = getWidth() / 2 - 70;rectF.top = getHeight() / 2 - 100;rectF.right = rectF.left + 140;rectF.bottom = rectF.top + 80;canvas.drawArc(rectF, 105, -60, false, mPaint);//画眼带initPaint();mPaint.setColor(Color.BLACK);mPaint.setStyle(Paint.Style.FILL_AND_STROKE);mPaint.setStrokeWidth(50);//左眼带rectF.left = getWidth() / 2 - 300;rectF.top = getHeight() / 2 - 280;rectF.right = rectF.left + 300;rectF.bottom = rectF.top + 100;canvas.drawArc(rectF, 88, 40, false, mPaint);//右眼带rectF.left = getWidth() / 2 ;rectF.top = getHeight() / 2 - 280;rectF.right = rectF.left + 300;rectF.bottom = rectF.top + 100;canvas.drawArc(rectF, 92, -42, false, mPaint);}private void drawBody(Canvas canvas) {initPaint();mPaint.setColor(mColorBody);mPaint.setStyle(Paint.Style.FILL);RectF rectF = new RectF();rectF.left = getWidth() / 2 - 250;  //xrectF.top = getHeight() / 2 - 400; //yrectF.right = rectF.left + 500;rectF.bottom = rectF.top + 800;mRadius = 250;canvas.drawRoundRect(rectF, mRadius, mRadius, mPaint);initPaint();mPaint.setColor(mColorStroke);mPaint.setStrokeWidth(mStrokeWidth);mPaint.setStyle(Paint.Style.STROKE);canvas.drawRoundRect(rectF, mRadius, mRadius, mPaint);}private void drawHands(Canvas canvas) {initPaint();//左手Path path = new Path();path.moveTo(getWidth()/2 - 250,getHeight()/2 - 10);path.lineTo(getWidth() / 2 - 350,getHeight() / 2 + 60);path.lineTo(getWidth()/2-250,getHeight()/2+130);mPaint.setColor(mColorBody);mPaint.setStyle(Paint.Style.FILL);canvas.drawPath(path,mPaint);mPaint.setColor(mColorStroke);mPaint.setStrokeWidth(10);mPaint.setStyle(Paint.Style.STROKE);canvas.drawPath(path,mPaint);path.moveTo(getWidth()/2 - 250,getHeight()/2 + 60);path.lineTo(getWidth() / 2 - 265,getHeight() / 2 + 70);canvas.drawPath(path,mPaint);//右手path.moveTo(getWidth()/2 + 250,getHeight()/2 - 10);path.lineTo(getWidth() / 2 + 350,getHeight() / 2 + 60);path.lineTo(getWidth()/2+250,getHeight()/2+130);mPaint.setColor(mColorBody);mPaint.setStyle(Paint.Style.FILL);canvas.drawPath(path,mPaint);mPaint.setColor(mColorStroke);mPaint.setStrokeWidth(8);mPaint.setStyle(Paint.Style.STROKE);canvas.drawPath(path,mPaint);path.moveTo(getWidth()/2 + 250,getHeight()/2 + 60);path.lineTo(getWidth() / 2 + 265,getHeight() / 2 + 70);canvas.drawPath(path,mPaint);}private void test(Canvas canvas){mPaint.setStrokeWidth(mStrokeWidth);mPaint.setColor(mColorStroke);mPaint.setStyle(Paint.Style.STROKE);RectF rectF = new RectF(200,200,600,400);canvas.drawArc(rectF,90,90,false,mPaint);}private void drawFeet(Canvas canvas) {mPaint.setStrokeWidth(mStrokeWidth);mPaint.setColor(mColorStroke);mPaint.setStyle(Paint.Style.FILL_AND_STROKE);//画左脚Path path = new Path();float leftFootStartX = getWidth() / 2 - 10;float leftFootstartY = mBodyRect.bottom -10;path.moveTo(leftFootStartX,leftFootstartY);path.lineTo(leftFootStartX - 100,leftFootstartY);RectF rectF = new RectF();rectF.left = leftFootStartX - 115;rectF.top = leftFootstartY - 50;rectF.right = rectF.left + 30;rectF.bottom = rectF.top + 50;path.addArc(rectF,90,180);path.lineTo(leftFootStartX - 50,leftFootstartY - 50);path.lineTo(leftFootStartX - 50,leftFootstartY - 100);path.lineTo(leftFootStartX ,leftFootstartY - 100);path.lineTo(leftFootStartX ,leftFootstartY);canvas.drawPath(path, mPaint);//画右脚path.reset();float rightFootStartX = getWidth() / 2 + 10;float rightFootstartY = mBodyRect.bottom -10;path.moveTo(rightFootStartX,rightFootstartY);path.lineTo(rightFootStartX + 100,rightFootstartY);RectF rectF1 = new RectF();rectF1.left = rightFootStartX + 85;rectF1.top = rightFootstartY - 50;rectF1.right = rectF1.left + 30;rectF1.bottom = rectF1.top + 50;path.addArc(rectF1,90,-180);path.lineTo(rightFootStartX + 50,rightFootstartY - 50);path.lineTo(rightFootStartX + 50,rightFootstartY - 100);path.lineTo(rightFootStartX ,rightFootstartY - 100);path.lineTo(rightFootStartX ,rightFootstartY);canvas.drawPath(path, mPaint);//        float radiusFoot = mRadius / 3 * 0.4f;
//        float leftFootStartX = mBodyRect.left + mRadius - mOffset * 2;
//        float leftFootStartY = mBodyRect.bottom - mOffset;
//        float footWidthA = mRadius * 0.5f;//脚宽度大-到半圆结束
//        float footWidthB = footWidthA / 3;//脚宽度-比较细的部分
//
//        //      左脚
//        Path path = new Path();
//        path.moveTo(leftFootStartX, leftFootStartY);
//        path.lineTo(leftFootStartX, leftFootStartY + mFootHeight);
//        path.lineTo(leftFootStartX - footWidthA + radiusFoot, leftFootStartY + mFootHeight);
//
//        RectF rectF = new RectF();
//        rectF.left = leftFootStartX - footWidthA;
//        rectF.top = leftFootStartY + mFootHeight - radiusFoot * 2;
//        rectF.right = rectF.left + radiusFoot * 2;
//        rectF.bottom = rectF.top + radiusFoot * 2;
//        path.addArc(rectF, 90, 180);
//        path.lineTo(rectF.left + radiusFoot + footWidthB, rectF.top);
//        path.lineTo(rectF.left + radiusFoot + footWidthB, leftFootStartY);
//        path.lineTo(leftFootStartX, leftFootStartY);
//        canvas.drawPath(path, mPaint);
//右脚
//        float rightFootStartX = mBodyRect.left + mRadius + mOffset * 2;
//        float rightFootStartY = leftFootStartY;
//        path.reset();
//        path.moveTo(rightFootStartX, rightFootStartY);
//        path.lineTo(rightFootStartX, rightFootStartY + mFootHeight);
//        path.lineTo(rightFootStartX + footWidthA - radiusFoot, rightFootStartY + mFootHeight);
//
//        rectF.left = rightFootStartX + footWidthA - radiusFoot * 2;
//        rectF.top = rightFootStartY + mFootHeight - radiusFoot * 2;
//        rectF.right = rectF.left + radiusFoot * 2;
//        rectF.bottom = rectF.top + radiusFoot * 2;
//        path.addArc(rectF, 90, -180);
//        path.lineTo(rectF.right - radiusFoot - footWidthB, rectF.top);
//        path.lineTo(rectF.right - radiusFoot - footWidthB, rightFootStartY);
//        path.lineTo(rightFootStartX, rightFootStartY);
//        canvas.drawPath(path, mPaint);}private void drawFeetShadow(Canvas canvas) {mPaint.setColor(getResources().getColor(android.R.color.darker_gray));canvas.drawOval(getWidth() / 2 - 200,mBodyRect.bottom - 10,getWidth() / 2 + 200,mBodyRect.bottom + 10,mPaint );}private void initParams() {
//        mBodyWidth = Math.min(getWidth(), getHeight() * BODY_WIDTH_HEIGHT_SCALE) * BODY_SCALE;
//        mBodyHeight = Math.min(getWidth(), getHeight() * BODY_WIDTH_HEIGHT_SCALE) / BODY_WIDTH_HEIGHT_SCALE * BODY_SCALE;
//
//        mStrokeWidth = Math.max(mBodyWidth / 50, mStrokeWidth);
//        mOffset = mStrokeWidth / 2;int widthCenter = getWidth() / 2;int heightCenter = getHeight() / 2;mBodyRect = new RectF();mBodyRect.left = widthCenter - 400;mBodyRect.top = heightCenter - 500;mBodyRect.right = mBodyRect.left + 800;mBodyRect.bottom = mBodyRect.top + 1000;//        mRadius = mBodyWidth / 2;
//        mFootHeight = mRadius * 0.4333f;//        mHandsHeight =  (getHeight() + mBodyHeight) / 2   + mOffset - mRadius * 1.65f ;}private void initPaint(){if (mPaint == null){mPaint = new Paint();}else {mPaint.reset();}mPaint.setAntiAlias(true);}
}

当然这会花很多时间,其实做完之后就会发现这些都是值得的。爱学习的爱技术的人都是值得尊重的。

这篇关于AndroidUI系列 - 自定义View手绘小黄人的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/758005

相关文章

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

科研绘图系列:R语言扩展物种堆积图(Extended Stacked Barplot)

介绍 R语言的扩展物种堆积图是一种数据可视化工具,它不仅展示了物种的堆积结果,还整合了不同样本分组之间的差异性分析结果。这种图形表示方法能够直观地比较不同物种在各个分组中的显著性差异,为研究者提供了一种有效的数据解读方式。 加载R包 knitr::opts_chunk$set(warning = F, message = F)library(tidyverse)library(phyl

【生成模型系列(初级)】嵌入(Embedding)方程——自然语言处理的数学灵魂【通俗理解】

【通俗理解】嵌入(Embedding)方程——自然语言处理的数学灵魂 关键词提炼 #嵌入方程 #自然语言处理 #词向量 #机器学习 #神经网络 #向量空间模型 #Siri #Google翻译 #AlexNet 第一节:嵌入方程的类比与核心概念【尽可能通俗】 嵌入方程可以被看作是自然语言处理中的“翻译机”,它将文本中的单词或短语转换成计算机能够理解的数学形式,即向量。 正如翻译机将一种语言

自定义类型:结构体(续)

目录 一. 结构体的内存对齐 1.1 为什么存在内存对齐? 1.2 修改默认对齐数 二. 结构体传参 三. 结构体实现位段 一. 结构体的内存对齐 在前面的文章里我们已经讲过一部分的内存对齐的知识,并举出了两个例子,我们再举出两个例子继续说明: struct S3{double a;int b;char c;};int mian(){printf("%zd\n",s

flume系列之:查看flume系统日志、查看统计flume日志类型、查看flume日志

遍历指定目录下多个文件查找指定内容 服务器系统日志会记录flume相关日志 cat /var/log/messages |grep -i oom 查找系统日志中关于flume的指定日志 import osdef search_string_in_files(directory, search_string):count = 0

Spring 源码解读:自定义实现Bean定义的注册与解析

引言 在Spring框架中,Bean的注册与解析是整个依赖注入流程的核心步骤。通过Bean定义,Spring容器知道如何创建、配置和管理每个Bean实例。本篇文章将通过实现一个简化版的Bean定义注册与解析机制,帮助你理解Spring框架背后的设计逻辑。我们还将对比Spring中的BeanDefinition和BeanDefinitionRegistry,以全面掌握Bean注册和解析的核心原理。

Oracle type (自定义类型的使用)

oracle - type   type定义: oracle中自定义数据类型 oracle中有基本的数据类型,如number,varchar2,date,numeric,float....但有时候我们需要特殊的格式, 如将name定义为(firstname,lastname)的形式,我们想把这个作为一个表的一列看待,这时候就要我们自己定义一个数据类型 格式 :create or repla

GPT系列之:GPT-1,GPT-2,GPT-3详细解读

一、GPT1 论文:Improving Language Understanding by Generative Pre-Training 链接:https://cdn.openai.com/research-covers/languageunsupervised/language_understanding_paper.pdf 启发点:生成loss和微调loss同时作用,让下游任务来适应预训

Java基础回顾系列-第七天-高级编程之IO

Java基础回顾系列-第七天-高级编程之IO 文件操作字节流与字符流OutputStream字节输出流FileOutputStream InputStream字节输入流FileInputStream Writer字符输出流FileWriter Reader字符输入流字节流与字符流的区别转换流InputStreamReaderOutputStreamWriter 文件复制 字符编码内存操作流(