日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > Android >内容正文

Android

绘制半圆_Android Canvas 绘制小黄人

發布時間:2023/12/10 Android 43 豆豆
生活随笔 收集整理的這篇文章主要介紹了 绘制半圆_Android Canvas 绘制小黄人 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
?

學習往往是枯燥的,如果能用一個有趣 Demo 來學習和練習技術,那對知識的掌握就會更牢固。我在學習 Canvas 繪制 API 的時候就是這樣做的。

?

截圖鎮樓

效果圖

我覺得這個繪制小黃人的自定義 View 就很有意思,也為我后來工作中的自定義 View 實現打下了良好的基礎。雖然這是 4 年半以前寫的文章,但是大部分關注我們的同學應該沒看過,今天咱們一起來拷古翻新一下代碼(程序員的事,怎么能叫炒冷飯呢,這明明是溫故而知新)。以后有機會還會分享項目實用自定義 View,敬請關注。

實現步驟

其實很簡單

  • 首先找到一張小黃人的圖
  • 然后調用 canvas.drawBitmap() 后畫到畫布上 好吧,一點都不好笑 ?- -。
  • 正文 ↓

    準備工作

    自定義MinionView extends View,定義以下成員變量,備用(可以先不看,后面的代碼看到莫名其妙出來的變量再上來看下)

    private?float?bodyWidth;
    private?float?bodyHeight;
    private?static?final?float?BODY_SCALE?=?0.6f;?//?身體主干占整個view的比重
    private?static?final?float?BODY_WIDTH_HEIGHT_SCALE?=?0.6f;?//?身體的比例設定為?w:h?=?3:5

    private?float?mStrokeWidth?=?4;?//?描邊寬度
    private?float?offset;?//?計算時,部分需要?考慮描邊偏移
    private?float?radius;?//?身體上下半圓的半徑
    private?int?colorClothes?=?Color.rgb(32,?116,?160);?//?衣服的顏色
    private?int?colorBody?=?Color.rgb(249,?217,?70);?//?身體的顏色
    private?int?colorStroke?=?Color.BLACK;
    private?RectF?bodyRect?=?new?RectF();
    private?float?handsHeight;//?計算出吊帶的高度時,可以用來做手的高度
    private?float?footHeight;?//?腳的高度,用來畫腳部陰影時用

    初始化參數

    重寫 onSizeChanged 方法,尺寸變化時初始化一下繪制的參數(會經常看到一些奇怪的數字,用做比例換算,別問我怎么來的,目測 + 一點點微調得來的- -。)

    private?void?initParams()?{
    ????bodyWidth?=?Math.min(getWidth(),?getHeight()?*?BODY_WIDTH_HEIGHT_SCALE)?*?BODY_SCALE;
    ????bodyHeight?=?Math.min(getWidth(),?getHeight()?*?BODY_WIDTH_HEIGHT_SCALE)?/?BODY_WIDTH_HEIGHT_SCALE?*?BODY_SCALE;

    ????mStrokeWidth?=?Math.max(bodyWidth?/?50,?mStrokeWidth);
    ????offset?=?mStrokeWidth?/?2;

    ????bodyRect.left?=?(getWidth()?-?bodyWidth)?/?2;
    ????bodyRect.top?=?(getHeight()?-?bodyHeight)?/?2;
    ????bodyRect.right?=?bodyRect.left?+?bodyWidth;
    ????bodyRect.bottom?=?bodyRect.top?+?bodyHeight;

    ????radius?=?bodyWidth?/?2;
    ????footHeight?=?radius?*?0.4333f;?

    ????handsHeight?=??(getHeight()?+?bodyHeight)?/?2???+?offset?-?radius?*?1.65f;
    }

    繪制參數好了,接下來就是一步步繪制幾何圖形了

    畫身體

    顯然身體是一個矩形加上,上下半圓,這邊只要用一個圓角矩形,然后圓角的弧度半徑用身體寬度的一半就可以達到這個效果了。把身體的矩形外存起來,后面經常要用到其相對位置進行對其它部位的定位,代碼如下:

    protected?void?onDraw(Canvas?canvas)?{
    ????...
    ????drawBody(canvas);???????//?身體
    ????drawBodyStroke(canvas);?//?最后畫身體的描邊,可以摭住一些過渡的棱角
    }

    private?void?drawBody(Canvas?canvas)?{
    ????mPaint.setColor(colorBody);
    ????mPaint.setStyle(Paint.Style.FILL);

    ????canvas.drawRoundRect(bodyRect,?radius,?radius,?mPaint);
    }

    private?void?drawBodyStroke(Canvas?canvas)?{
    ????mPaint.setColor(colorStroke);
    ????mPaint.setStrokeWidth(mStrokeWidth);
    ????mPaint.setStyle(Paint.Style.STROKE);
    ????canvas.drawRoundRect(bodyRect,?radius,?radius,?mPaint);
    }

    畫衣服

    這是穿上褲子的樣子
    • 首先畫 底下的半圓
    rect.left?=?(getWidth()?-?bodyWidth)?/?2?+?offset;
    rect.top?=?(getHeight()?+?bodyHeight)?/?2?-?radius?*?2?+?offset;
    rect.right?=?rect.left?+?bodyWidth?-?offset?*?2;
    rect.bottom?=?rect.top?+?radius?*?2?-?offset?*?2;

    mPaint.setColor(colorClothes);
    mPaint.setStyle(Paint.Style.FILL);
    mPaint.setStrokeWidth(mStrokeWidth);
    canvas.drawArc(rect,?0,?180,?true,?mPaint);
    • 再畫半圓上方的矩形, w 表示矩形離左邊身體的距離,h 矩形的高
    int?h?=?(int)?(radius?*?0.5);
    int?w?=?(int)?(radius?*?0.3);

    rect.left?+=?w;
    rect.top?=?rect.top?+?radius?-?h;
    rect.right?-=?w;
    rect.bottom?=?rect.top?+?h;

    canvas.drawRect(rect,?mPaint);
    • 上面的畫完之后,要在衣服上面描一層黑色的邊,用canvas.drawLines把線一條條畫出來吧,這邊要同時考慮畫筆的描邊寬度,否則會出現連接點有鋸齒的感覺。( 2020 注:這是當時最直接的想法,現在來看用 Path 來繪制,每個點用 rLineTo 去連接,代碼會簡單得多。)
    mPaint.setColor(colorStroke);
    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]?+?offset;
    pts[6]?=?pts[4];
    pts[7]?=?pts[3]?-?h;

    pts[8]?=?pts[6]?-?offset;
    pts[9]?=?pts[7];
    pts[10]?=?pts[8]?+?(radius?-?w)?*?2;
    pts[11]?=?pts[9];

    pts[12]?=?pts[10];
    pts[13]?=?pts[11]?-?offset;
    pts[14]?=?pts[12];
    pts[15]?=?pts[13]?+?h;

    pts[16]?=?pts[14]?-?offset;
    pts[17]?=?pts[15];
    pts[18]?=?pts[16]?+?w;
    pts[19]?=?pts[17];
    canvas.drawLines(pts,?mPaint);
    • 畫吊帶 就是一個直角梯形,把梯形的四個頂點計算出來,使用canvas.drawPath將其畫上去,然后紐扣用一個實心的小圓表示
    //?畫左吊帶
    path.reset();
    path.moveTo(rect.left?-?w?-?offset,?handsHeight);
    path.lineTo(rect.left?+?h?/?4f,?rect.top?+?h?/?2f);
    final?float?smallW?=?w?/?2f?*?(float)?Math.sin(Math.PI?/?4);
    path.lineTo(rect.left?+?h?/?4f?+?smallW,?rect.top?+?h?/?2f?-?smallW);
    final?float?smallW2?=?w?/?(float)?Math.sin(Math.PI?/?4)?/?2;
    path.lineTo(rect.left?-?w?-?offset,?handsHeight?-?smallW2);
    canvas.drawPath(path,?mPaint);

    mPaint.setColor(colorStroke);
    mPaint.setStrokeWidth(mStrokeWidth);
    mPaint.setStyle(Paint.Style.STROKE);
    canvas.drawPath(path,?mPaint);
    mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
    canvas.drawCircle(rect.left?+?h?/?5f,?rect.top?+?h?/?4f,?mStrokeWidth?*?0.7f,?mPaint);

    //?畫右吊帶,代碼差不多省略了,坐標對稱
    • 畫中間的口袋 是一個下面兩邊是圓角的圓角矩形,但是貌似不能直接畫這樣的圓角矩形,所以我就用土辦法,不就是一個多邊形嗎,用canvas.drawPath來畫,在圓角的地方添加圓弧過渡path.addArc
    path.reset();
    float?radiusBigPocket?=?w?/?2.0f;
    path.moveTo(rect.left?+?1.5f?*?w,?rect.bottom?-?h?/?4f);
    path.lineTo(rect.right?-?1.5f?*?w,?rect.bottom?-?h?/?4f);
    path.lineTo(rect.right?-?1.5f?*?w,?rect.bottom?+?h?/?4f);
    path.addArc(rect.right?-?1.5f?*?w?-?radiusBigPocket?*?2,?rect.bottom?+?h?/?4f?-?radiusBigPocket,
    ????????rect.right?-?1.5f?*?w,?rect.bottom?+?h?/?4f?+?radiusBigPocket,?0,?90);
    path.lineTo(rect.left?+?1.5f?*?w?+?radiusBigPocket,?rect.bottom?+?h?/?4f?+?radiusBigPocket);

    path.addArc(rect.left?+?1.5f?*?w,?rect.bottom?+?h?/?4f?-?radiusBigPocket,
    ????????rect.left?+?1.5f?*?w?+?2?*?radiusBigPocket,?rect.bottom?+?h?/?4f?+?radiusBigPocket,?90,?90);
    path.lineTo(rect.left?+?1.5f?*?w,?rect.bottom?-?h?/?4f?-?offset);
    canvas.drawPath(path,?mPaint);????
    • 左右兩個小口袋也直接用一個小弧來解決掉
    //?下邊一豎,分開褲子
    canvas.drawLine(bodyRect.left?+?bodyWidth?/?2,?bodyRect.bottom?-?h?*?0.8f,?bodyRect.left?+?bodyWidth?/?2,?bodyRect.bottom,?mPaint);
    //?左邊的小口袋
    float?radiusSmallPocket?=?w?*?1.2f;
    canvas.drawArc(bodyRect.left?-?radiusSmallPocket,?bodyRect.bottom?-?radius?-?radiusSmallPocket,
    ???????bodyRect.left?+?radiusSmallPocket,?bodyRect.bottom?-?radius?+?radiusSmallPocket,?80,?-60,?false,?mPaint);
    //?右邊小口袋
    canvas.drawArc(bodyRect.right?-?radiusSmallPocket,?bodyRect.bottom?-?radius?-?radiusSmallPocket,
    ????????bodyRect.right?+?radiusSmallPocket,?bodyRect.bottom?-?radius?+?radiusSmallPocket,?100,?60,?false,?mPaint);
    • 嗯,衣服畫完了。
    protected?void?onDraw(Canvas?canvas)?{
    ????...
    ????drawClothes(canvas);//衣服
    }

    private?void?drawClothes(Canvas?canvas)?{
    ????//就是上面那一堆代碼按順序合起來啦。。。。。
    }

    畫腳

    腳這部分比較簡單,從身體的下方,一個豎直的矩形下來,再加上一個左邊圓角的圓角矩形,還是通過畫Path來實現。

    private?void?drawFeet(Canvas?canvas)?{
    ????mPaint.setStrokeWidth(mStrokeWidth);
    ????mPaint.setColor(colorStroke);
    ????mPaint.setStyle(Paint.Style.FILL_AND_STROKE);

    ????float?radiusFoot?=?radius?/?3?*?0.4f;
    ????float?leftFootStartX?=?bodyRect.left?+?radius?-?offset?*?2;
    ????float?leftFootStartY?=?bodyRect.bottom?-?offset;
    ????float?footWidthA?=?radius?*?0.5f;//腳寬度大-到半圓結束
    ????float?footWidthB?=?footWidthA?/?3;//腳寬度-比較細的部分

    ????//?左腳
    ????path.reset();
    ????path.moveTo(leftFootStartX,?leftFootStartY);
    ????path.lineTo(leftFootStartX,?leftFootStartY?+?footHeight);
    ????path.lineTo(leftFootStartX?-?footWidthA?+?radiusFoot,?leftFootStartY?+?footHeight);

    ????rect.left?=?leftFootStartX?-?footWidthA;
    ????rect.top?=?leftFootStartY?+?footHeight?-?radiusFoot?*?2;
    ????rect.right?=?rect.left?+?radiusFoot?*?2;
    ????rect.bottom?=?rect.top?+?radiusFoot?*?2;
    ????path.addArc(rect,?90,?180);
    ????path.lineTo(rect.left?+?radiusFoot?+?footWidthB,?rect.top);
    ????path.lineTo(rect.left?+?radiusFoot?+?footWidthB,?leftFootStartY);
    ????path.lineTo(leftFootStartX,?leftFootStartY);
    ????canvas.drawPath(path,?mPaint);

    ??//?右腳與左腳實現一致,坐標對稱,代碼略
    }

    畫手

    這里是雙手放在后背的樣子

    手我用的是一個等腰直角三角形來實現,斜邊就是吊帶到褲子,從直角頂點作高到斜邊,通過小直角三角形的直角邊相等就可以算出頂點的坐標。這個時候還是有個圓角,剛開始我實現的時候是像上面那些通過path.addArc加上圓角,但是這邊計算好之后和原來的銜接一直有問題,在調了半天之后,偶然發現mPaint.setPathEffect(new CornerPathEffect(radiusHand));這個方法,可以使path的拐角用圓角來過渡,一下子就簡單到爆了,果然科學技術是第一生產力。

    private?void?drawHands(Canvas?canvas)?{
    ????...???????
    ????//?左手
    ????path.moveTo(bodyRect.left,?handsHeight);
    ????path.lineTo(bodyRect.left?-?hypotenuse?/?2,?handsHeight?+?hypotenuse?/?2);
    ????path.lineTo(bodyRect.left?+offset,?bodyRect.bottom?-?radius?+offset);
    ????path.lineTo(bodyRect.left,?handsHeight);
    ????canvas.drawPath(path,?mPaint);

    ????mPaint.setStrokeWidth(mStrokeWidth);
    ????mPaint.setStyle(Paint.Style.STROKE);
    ????mPaint.setColor(colorStroke);
    ????canvas.drawPath(path,?mPaint);

    ????//?右手略?...
    ????//?手臂內側拐點
    ????path.reset();
    ????mPaint.setStyle(Paint.Style.FILL);
    ????path.moveTo(bodyRect.left,?handsHeight?+?hypotenuse?/?2?-?mStrokeWidth);
    ????path.lineTo(bodyRect.left?-?mStrokeWidth?*?2,?handsHeight?+?hypotenuse?/?2?+?mStrokeWidth?*?2);
    ????path.lineTo(bodyRect.left,?handsHeight?+?hypotenuse?/?2?+?mStrokeWidth);
    ????canvas.drawPath(path,?mPaint);
    ????...
    ?}

    畫眼睛,嘴巴

    三個字,圓圓圓

    反正就是各種畫圓,或者弧形,嘴巴部分偷懶也就一條小弧一筆帶過了,哈哈

    private?void?drawEyesMouth(Canvas?canvas)?{
    ????//?眼睛中心處于上半圓直徑?往上的高度偏移
    ????float?eyesOffset?=?radius?*?0.1f;
    ????mPaint.setStrokeWidth(mStrokeWidth?*?5);

    ????//?計算眼鏡帶弧行的半徑?分兩段,以便眼睛中間有隔開的效果
    ????float?radiusGlassesRibbon?=?(float)?(radius?/?Math.sin(Math.PI?/?20));
    ????rect.left?=?bodyRect.left?+?radius?-?radiusGlassesRibbon;
    ????rect.top?=?bodyRect.top?+?radius?-?(float)?(radius?/?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?=?radius?/?3;
    ????mPaint.setColor(Color.WHITE);
    ????mPaint.setStrokeWidth(mStrokeWidth);
    ????mPaint.setStyle(Paint.Style.FILL);

    ????canvas.drawCircle(bodyRect.left?+?bodyWidth?/?2?-?radiusEyes?-?offset,?bodyRect.top?+?radius?-?eyesOffset,?radiusEyes,?mPaint);
    ????canvas.drawCircle(bodyRect.left?+?bodyWidth?/?2?+?radiusEyes?+?offset,?bodyRect.top?+?radius?-?eyesOffset,?radiusEyes,?mPaint);

    ????mPaint.setColor(colorStroke);
    ????mPaint.setStyle(Paint.Style.STROKE);
    ????canvas.drawCircle(bodyRect.left?+?bodyWidth?/?2?-?radiusEyes?-?offset,?bodyRect.top?+?radius?-?eyesOffset,?radiusEyes,?mPaint);
    ????canvas.drawCircle(bodyRect.left?+?bodyWidth?/?2?+?radiusEyes?+?offset,?bodyRect.top?+?radius?-?eyesOffset,?radiusEyes,?mPaint);

    ????final?float?radiusEyeballBlack?=?radiusEyes?/?3;
    ????mPaint.setStyle(Paint.Style.FILL);
    ????canvas.drawCircle(bodyRect.left?+?bodyWidth?/?2?-?radiusEyes?-?offset,?bodyRect.top?+?radius?-?eyesOffset,?radiusEyeballBlack,?mPaint);
    ????canvas.drawCircle(bodyRect.left?+?bodyWidth?/?2?+?radiusEyes?+?offset,?bodyRect.top?+?radius?-?eyesOffset,?radiusEyeballBlack,?mPaint);

    ????mPaint.setColor(Color.WHITE);
    ????final?float?radiusEyeballWhite?=?radiusEyeballBlack?/?2;
    ????canvas.drawCircle(bodyRect.left?+?bodyWidth?/?2?-?radiusEyes?+?radiusEyeballWhite?-?offset?*?2,
    ????????????bodyRect.top?+?radius?-?radiusEyeballWhite?+?offset?-?eyesOffset,
    ????????????radiusEyeballWhite,?mPaint);
    ????canvas.drawCircle(bodyRect.left?+?bodyWidth?/?2?+?radiusEyes?+?radiusEyeballWhite,
    ????????????bodyRect.top?+?radius?-?radiusEyeballWhite?+?offset?-?eyesOffset,
    ????????????radiusEyeballWhite,?mPaint);

    ????//?畫嘴巴,因為位置和眼睛有相對關系,所以寫在一塊
    ????mPaint.setColor(colorStroke);
    ????mPaint.setStyle(Paint.Style.STROKE);
    ????mPaint.setStrokeWidth(mStrokeWidth);
    ????float?radiusMonth?=?radius;
    ????rect.left?=?bodyRect.left;
    ????rect.top?=?bodyRect.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?drawFeetShadow(Canvas?canvas)?{
    ????mPaint.setColor(getResources().getColor(android.R.color.darker_gray));
    ????canvas.drawOval(bodyRect.left?+?bodyWidth?*?0.15f,
    ????????????bodyRect.bottom?-?offset?+?footHeight,
    ????????????bodyRect.right?-?bodyWidth?*?0.15f,
    ????????????bodyRect.bottom?-?offset?+?footHeight?+?mStrokeWidth?*?1.3f,?mPaint);
    }

    重寫 onDraw 方法

    按層級依次調用上述的各種方法,畫完收工。

    @Override
    protected?void?onDraw(Canvas?canvas)?{
    ????drawFeetShadow(canvas);?//?腳下的陰影
    ????drawFeet(canvas);???????//?腳
    ????drawHands(canvas);??????//?手
    ????drawBody(canvas);???????//?身體
    ????drawClothes(canvas);????//?衣服
    ????drawEyesMouth(canvas);??//?眼睛,嘴巴
    ????drawBodyStroke(canvas);?//?最后畫身體的描邊,可以摭住一些過渡的棱角
    }

    少了點什么?

    畫完了,好像少了點什么。。。。。對了,頭發。好吧,我畫的是程序猿,哪來的頭發 - -

    ?

    至此,正常畫風的小黃人已經畫完了,但是吧,好不容易畫好,好像沒啥意思,腦洞大開一下吧。電影中的小黃人中病毒后是會變成紫色的,那我們用代碼畫,換個顏色還不是分分鐘,不但要紫色,還要各種顏色。

    ?

    三行代碼搞定腦洞

    public?void?randomBodyColor()?{
    ????Random?random?=?new?Random();
    ????colorBody?=?Color.rgb(random.nextInt(255),?random.nextInt(255),?random.nextInt(255));
    ????invalidate();
    }

    然后效果就變成了這樣。

    看起來還有點小酷炫希望大家喜歡,完整源碼在?github ,可點擊“閱讀原文”查看推薦閱讀

    設計模式概覽:六大設計原則

    RecyclerView 的緩存復用機制

    ServiceManager 的工作原理

    關注我

    助你升職加薪

    Android 面試官

    原創不易,在看支持! 創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎

    總結

    以上是生活随笔為你收集整理的绘制半圆_Android Canvas 绘制小黄人的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。