Android 高級UI5 畫筆Paint的基本用法

Android 高級UI 目錄

1.setStyle(Paint.Style style)

設(shè)置畫筆樣式,取值有
Paint.Style.FILL :填充內(nèi)部
Paint.Style.FILL_AND_STROKE :填充內(nèi)部和描邊
Paint.Style.STROKE :僅描邊

代碼實例:

public class PaintViewBasic extends View {
    private Paint mPaint;

    public PaintViewBasic(Context context) {
        super(context);
        mPaint = new Paint();
    }

    public PaintViewBasic(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        mPaint = new Paint();
    }

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

    private void drawStyle( Canvas canvas ) {

        mPaint.setColor(Color.RED);//設(shè)置畫筆的顏色
        mPaint.setTextSize(60);//設(shè)置文字大小
        mPaint.setStrokeWidth(5);//設(shè)置畫筆的寬度
        mPaint.setAntiAlias(true);//設(shè)置抗鋸齒功能 true表示抗鋸齒 false則表示不需要這功能

        mPaint.setStyle(Paint.Style.STROKE);
        canvas.drawCircle(200,200,160,mPaint);

        mPaint.setStyle(Paint.Style.FILL);
        canvas.drawCircle(200,600,160,mPaint);

        mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
        canvas.drawCircle(200,1000,160,mPaint);

    }
    
}
Paint.Style

2.setStrokeCap(Paint.Cap cap)

設(shè)置線冒樣式,取值有
Paint.Cap.BUTT(無線冒)
Paint.Cap.ROUND(圓形線冒)
Paint.Cap.SQUARE(方形線冒)
注意:冒多出來的那塊區(qū)域就是線帽!就相當(dāng)于給原來的直線加上一個帽子一樣,所以叫線帽

    private void drawStrokeCap(Canvas canvas) {
        Paint paint = new Paint();

        paint.setAntiAlias(true);
        paint.setStrokeWidth(200);
        paint.setColor(Color.parseColor("#00ff00"));
        paint.setStrokeCap(Paint.Cap.BUTT);       // 線帽,即畫的線條兩端是否帶有圓角,butt,無圓角
        canvas.drawLine(200, 200, 500, 200, paint);

        paint.setColor(Color.parseColor("#ff0000"));
        paint.setStrokeCap(Paint.Cap.ROUND);       // 線帽,即畫的線條兩端是否帶有圓角,ROUND,圓角
        canvas.drawLine(200, 500, 500, 500, paint);

        paint.setColor(Color.parseColor("#0000ff"));
        paint.setStrokeCap(Paint.Cap.SQUARE);       // 線帽,即畫的線條兩端是否帶有圓角,SQUARE,矩形
        canvas.drawLine(200, 800, 500, 800, paint);
    }
Paint.Cap

3.setStrokeJoin(Paint.Join join)

設(shè)置線段連接處樣式,取值有:
Paint.Join.MITER(結(jié)合處為銳角)
Paint.Join.Round (結(jié)合處為圓弧)
Paint.Join.BEVEL (結(jié)合處為直線)

  private void drawStrokeJoin( Canvas canvas ) {
        Paint paint = new Paint();

        paint.setAntiAlias( true );
        paint.setStrokeWidth( 80 );
        paint.setStyle(Paint.Style.STROKE ); // 默認(rèn)是填充 Paint.Style.FILL
        paint.setColor( Color.parseColor("#0000ff") );

        Path path = new Path();
        path.moveTo(100, 100);
        path.lineTo(400, 100);
        path.lineTo(100, 300);
        paint.setStrokeJoin(Paint.Join.MITER);
        canvas.drawPath(path, paint);

        path.moveTo(100, 500);
        path.lineTo(400, 500);
        path.lineTo(100, 700);
        paint.setStrokeJoin(Paint.Join.ROUND);
        canvas.drawPath(path, paint);

        path.moveTo(100, 900);
        path.lineTo(400, 900);
        path.lineTo(100, 1100);
        paint.setStrokeJoin(Paint.Join.BEVEL);
        canvas.drawPath(path, paint);
    }

}

Paint.Join

4.setPathEffect(PathEffect effect)

設(shè)置繪制路徑的效果,如點畫線等

CornerPathEffect:

這個類的作用就是將Path的各個連接線段之間的夾角用一種更平滑的方式連接,類似于圓弧與切線的效果。
一般的,通過CornerPathEffect(float radius)指定一個具體的圓弧半徑來實例化一個CornerPathEffect。

DashPathEffect:

這個類的作用就是將Path的線段虛線化。
構(gòu)造函數(shù)為DashPathEffect(float[] intervals, float offset),其中intervals為虛線的ON和OFF數(shù)組,該數(shù)組的length必須大于等于2,phase為繪制時的偏移量。

DiscretePathEffect:

這個類的作用是打散Path的線段,使得在原來路徑的基礎(chǔ)上發(fā)生打散效果。
一般的,通過構(gòu)造DiscretePathEffect(float segmentLength,float deviation)來構(gòu)造一個實例,其中,segmentLength指定最大的段長,deviation指定偏離量。

PathDashPathEffect:

這個類的作用是使用Path圖形來填充當(dāng)前的路徑,其構(gòu)造函數(shù)為PathDashPathEffect (Path shape, float advance, float phase,PathDashPathEffect.Stylestyle)。
shape則是指填充圖形,advance指每個圖形間的間距,phase為繪制時的偏移量,style為該類自由的枚舉值,有三種情況:Style.ROTATE、Style.MORPH和
Style.TRANSLATE。其中ROTATE的情況下,線段連接處的圖形轉(zhuǎn)換以旋轉(zhuǎn)到與下一段移動方向相一致的角度進(jìn)行轉(zhuǎn)轉(zhuǎn),MORPH時圖形會以發(fā)生拉伸或壓縮等變形的情況與下一段相連接,TRANSLATE時,圖形會以位置平移的方式與下一段相連接。

ComposePathEffect:

組合效果,這個類需要兩個PathEffect參數(shù)來構(gòu)造一個實例,ComposePathEffect (PathEffect outerpe,PathEffect innerpe),表現(xiàn)時,會首先將innerpe表現(xiàn)出來,然后再在innerpe的基礎(chǔ)上去增加outerpe的效果。

SumPathEffect:

疊加效果,這個類也需要兩個PathEffect作為參數(shù)SumPathEffect(PathEffect first,PathEffect second),但與ComposePathEffect不同的是,在表現(xiàn)時,會分別對兩個參數(shù)的效果各自獨立進(jìn)行表現(xiàn),然后將兩個效果簡單的重疊在一起顯示出來。

關(guān)于參數(shù)phase

在存在phase參數(shù)的兩個類里,如果phase參數(shù)的值不停發(fā)生改變,那么所繪制的圖形也會隨著偏移量而不斷的發(fā)生變動,這個時候,看起來這條線就像動起來了一樣。

private float phase;
    private PathEffect[] effects;
    private  int[] colors;

    private void drawPathEffect(Canvas canvas) {
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeWidth(4);
        // 創(chuàng)建,并初始化Path
        Path path = new Path();
        path.moveTo(0, 0);
        for (int i = 1; i <= 35; i++) {
            // 生成15個點,隨機(jī)生成它們的坐標(biāo),并將它們連成一條Path
            path.lineTo(i * 20, (float) Math.random() * 60);
        }
        // 初始化七個顏色
        colors = new int[]{Color.BLACK, Color.BLUE, Color.CYAN, Color.GREEN, Color.MAGENTA, Color.RED,
                Color.GRAY};


        // 將背景填充成白色
        canvas.drawColor(Color.WHITE);
        effects = new PathEffect[7];
        // -------下面開始初始化7中路徑的效果
        // 使用路徑效果
        effects[0] = null;
        // 使用CornerPathEffect路徑效果
        effects[1] = new CornerPathEffect(10);
        // 初始化DiscretePathEffect
        effects[2] = new DiscretePathEffect(3.0f, 5.0f);
        // 初始化DashPathEffect
        effects[3] = new DashPathEffect(new float[]{20, 10, 5, 10}, phase);
        // 初始化PathDashPathEffect
        Path p = new Path();
        p.addRect(0, 0, 8, 8, Path.Direction.CCW);
        effects[4] = new PathDashPathEffect(p, 12, phase, PathDashPathEffect.Style.ROTATE);
        // 初始化PathDashPathEffect
        effects[5] = new ComposePathEffect(effects[2], effects[4]);
        effects[6] = new SumPathEffect(effects[4], effects[3]);
        // 將畫布移到8,8處開始繪制
        canvas.translate(8, 8);
        // 依次使用7中不同路徑效果,7種不同的顏色來繪制路徑
        for (int i = 0; i < effects.length; i++) {
            mPaint.setPathEffect(effects[i]);
            mPaint.setColor(colors[i]);
            canvas.drawPath(path, mPaint);
            canvas.translate(0, 200);
        }
        // 改變phase值,形成動畫效果
        phase += 1;
        invalidate();
    }
PathEffect

5.setShadowLayer(float radius, float dx, float dy, int shadowColor)

陰影制作:包括各種形狀(矩形,圓形等等),以及文字等等都能設(shè)置陰影。

    private void drawShadowLayer(Canvas canvas) {
        // 建立Paint 物件
        Paint paint1 = new Paint();
        paint1.setTextSize(100);
        // 設(shè)定顏色
        paint1.setColor(Color.BLACK);
        // 設(shè)定陰影(柔邊, X 軸位移, Y 軸位移, 陰影顏色)
        paint1.setShadowLayer(10, 5, 5, Color.GRAY);
        // 實心矩形& 其陰影
        canvas.drawText("我愛你", 20,100,paint1);
        Paint paint2 = new Paint();
        paint2.setTextSize(100);
        paint2.setColor(Color.GREEN);
        paint2.setShadowLayer(10, 6, 6, Color.GRAY);
        canvas.drawText("你真傻", 20,200,paint2);

        //cx和cy為圓點的坐標(biāo)
        int radius = 80;
        int offest = 40;
        int startX = radius + offest;
        int startY = radius + offest + 200;

        Paint paint3 = new Paint();
        //如果不關(guān)閉硬件加速,setShadowLayer無效
        setLayerType(LAYER_TYPE_SOFTWARE, null);
        paint3.setShadowLayer(20, -20, 10, Color.DKGRAY);
        canvas.drawCircle(startX, startY, radius, paint3);
        paint3.setStyle(Paint.Style.STROKE);
        paint3.setStrokeWidth(5);
        canvas.drawCircle(startX + radius * 2 + offest, startY, radius, paint3);
    }
ShadowLayer

6.setXfermode(Xfermode xfermode)

Xfermode國外有大神稱之為過渡模式,這種翻譯比較貼切但恐怕不易理解,大家也可以直接稱之為圖像混合模式,因為所謂的“過渡”其實就是圖像混合的一種,這個方法跟我們上面講到的setColorFilter蠻相似的。查看API文檔發(fā)現(xiàn)其果然有三個子類:AvoidXfermode, PixelXorXfermode和PorterDuffXfermode,這三個子類實現(xiàn)的功能要比setColorFilter的三個子類復(fù)雜得多。

由于AvoidXfermode, PixelXorXfermode都已經(jīng)被標(biāo)注為過時了,所以這次主要研究的是仍然在使用的PorterDuffXfermode:

PorterDuffXfermode

該類同樣有且只有一個含參的構(gòu)造方法PorterDuffXfermode(PorterDuff.Mode mode),雖說構(gòu)造方法的簽名列表里只有一個PorterDuff.Mode的參數(shù),但是它可以實現(xiàn)很多酷斃的圖形效果?。《鳳orterDuffXfermode就是圖形混合模式的意思,其概念最早來自于SIGGRAPH的Tomas Proter和Tom Duff,混合圖形的概念極大地推動了圖形圖像學(xué)的發(fā)展,延伸到計算機(jī)圖形圖像學(xué)像Adobe和AutoDesk公司著名的多款設(shè)計軟件都可以說一定程度上受到影響,而我們PorterDuffXfermode的名字也來源于這倆人的人名組合PorterDuff,那PorterDuffXfermode能做些什么呢?我們先來看一張API DEMO里的圖片:


image.png

這張圖片從一定程度上形象地說明了圖形混合的作用,兩個圖形一圓一方通過一定的計算產(chǎn)生不同的組合效果,在API中Android為我們提供了18種(比上圖多了兩種ADD和OVERLAY)模式:

ADD:飽和相加,對圖像飽和度進(jìn)行相加,不常用

CLEAR:清除圖像

DARKEN:變暗,較深的顏色覆蓋較淺的顏色,若兩者深淺程度相同則混合

DST:只顯示目標(biāo)圖像

DST_ATOP:在源圖像和目標(biāo)圖像相交的地方繪制【目標(biāo)圖像】,在不相交的地方繪制【源圖像】,相交處的效果受到源圖像和目標(biāo)圖像alpha的影響

DST_IN:只在源圖像和目標(biāo)圖像相交的地方繪制【目標(biāo)圖像】,繪制效果受到源圖像對應(yīng)地方透明度影響

DST_OUT:只在源圖像和目標(biāo)圖像不相交的地方繪制【目標(biāo)圖像】,在相交的地方根據(jù)源圖像的alpha進(jìn)行過濾,源圖像完全不透明則完全過濾,完全透明則不過濾

DST_OVER:將目標(biāo)圖像放在源圖像上方

LIGHTEN:變亮,與DARKEN相反,DARKEN和LIGHTEN生成的圖像結(jié)果與Android對顏色值深淺的定義有關(guān)

MULTIPLY:正片疊底,源圖像素顏色值乘以目標(biāo)圖像素顏色值除以255得到混合后圖像像素顏色值

OVERLAY:疊加

SCREEN:濾色,色調(diào)均和,保留兩個圖層中較白的部分,較暗的部分被遮蓋

SRC:只顯示源圖像

SRC_ATOP:在源圖像和目標(biāo)圖像相交的地方繪制【源圖像】,在不相交的地方繪制【目標(biāo)圖像】,相交處的效果受到源圖像和目標(biāo)圖像alpha的影響

SRC_IN:只在源圖像和目標(biāo)圖像相交的地方繪制【源圖像】

SRC_OUT:只在源圖像和目標(biāo)圖像不相交的地方繪制【源圖像】,相交的地方根據(jù)目標(biāo)圖像的對應(yīng)地方的alpha進(jìn)行過濾,目標(biāo)圖像完全不透明則完全過濾,完全透明則不過濾

SRC_OVER:將源圖像放在目標(biāo)圖像上方

XOR:在源圖像和目標(biāo)圖像相交的地方之外繪制它們,在相交的地方受到對應(yīng)alpha和色值影響,如果完全不透明則相交處完全不繪制

public class PorterDuffView extends View {

    Paint mPaint;
    Context mContext;
    int BlueColor;
    int PinkColor;
    int mWith;
    int mHeight;
    public PorterDuffView(Context context) {
        super(context);
        init(context);
    }
    public PorterDuffView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }

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

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        mHeight = getMeasuredHeight();
        mWith = getMeasuredWidth();
    }

    private void init(Context context) {
        mContext = context;
        BlueColor = ContextCompat.getColor(mContext, R.color.colorPrimary);
        PinkColor = ContextCompat.getColor(mContext, R.color.colorAccent);
        mPaint = new Paint();
        mPaint.setStyle(Paint.Style.FILL);
        mPaint.setAntiAlias(true);
    }
    private Bitmap drawRectBm(){
        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        paint.setColor(BlueColor);
        paint.setStyle(Paint.Style.FILL);
        paint.setAntiAlias(true);
        Bitmap bm = Bitmap.createBitmap(200,200, Bitmap.Config.ARGB_8888);
        Canvas cavas = new Canvas(bm);
        cavas.drawRect(new RectF(0,0,70,70),paint);
        return bm;
    }
    private  Bitmap drawCircleBm(){
        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        paint.setColor(PinkColor);
        paint.setStyle(Paint.Style.FILL);
        paint.setAntiAlias(true);
        Bitmap bm = Bitmap.createBitmap(200,200, Bitmap.Config.ARGB_8888);
        Canvas cavas = new Canvas(bm);
        cavas.drawCircle(70,70,35,paint);
        return bm;
    }
    @Override
    protected void onDraw(Canvas canvas) {
        mPaint.setFilterBitmap(false);
        mPaint.setStyle(Paint.Style.FILL);
        mPaint.setTextSize(20);
        RectF recf = new RectF(20,20,60,60);
        mPaint.setColor(BlueColor);
        canvas.drawRect(recf,mPaint);
        mPaint.setColor(PinkColor);
        canvas.drawCircle(100,40,20,mPaint);
        @SuppressLint("WrongConstant") int sc = canvas.saveLayer(0, 0,mWith,mHeight, null, Canvas.MATRIX_SAVE_FLAG |
                Canvas.CLIP_SAVE_FLAG |
                Canvas.HAS_ALPHA_LAYER_SAVE_FLAG |
                Canvas.FULL_COLOR_LAYER_SAVE_FLAG |
                Canvas.CLIP_TO_LAYER_SAVE_FLAG);
        int y = 180;
        int x = 50;
        for(PorterDuff.Mode mode : PorterDuff.Mode.values()){
            if(y >= 900){
                y = 180;
                x += 200;
            }
            mPaint.setXfermode(null);
            canvas.drawText(mode.name(),x + 100,y,mPaint);
            canvas.drawBitmap(drawRectBm(),x,y,mPaint);
            mPaint.setXfermode(new PorterDuffXfermode(mode));
            canvas.drawBitmap(drawCircleBm(),x,y,mPaint);
            y += 120;
        }
        mPaint.setXfermode(null);
        // 還原畫布
        canvas.restoreToCount(sc);
    }
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 本文是自定義View的第二篇,主要學(xué)習(xí):Paint第一篇地址Android自定義View(一) -- 初識 本文計...
    T9的第三個三角閱讀 4,528評論 2 26
  • 系列文章之 Android中自定義View(一)系列文章之 Android中自定義View(二)系列文章之 And...
    YoungerDev閱讀 2,340評論 0 4
  • 第一期時,在韓國,黃家,助理突然來了一句:“哥,中國有一檔《我是歌手第四季》的節(jié)目需要你們兄妹參加。”致列回眸一笑...
    怦然希米閱讀 381評論 2 1
  • 李程剛閱讀 261評論 0 0
  • 整體背景原則 1.交易原則是讓自己利益最大化。 2.成長是最關(guān)鍵。 成長必須做選擇,選擇以 我選擇的事能不能讓我積...
    touinesol閱讀 348評論 0 0

友情鏈接更多精彩內(nèi)容