Android 自定義View學(xué)習(xí)(六)——Paint 關(guān)于Shader的學(xué)習(xí)


1.Shader 著色器

著色器就是用來上色的,可以用來實(shí)現(xiàn)一系列的漸變诊胞、渲染效果撵孤,有5個子類

  1. BitmapShader 位圖Shader
  2. LinerGradient 線性Shader
  3. RadialGradient 光束Shader
  4. SweepGradient 梯度Shader
  5. ComposeShader 混合Shader

BitmapShader是唯一個可以用來給一個圖片著色邪码,其他四個就是漸變闭专、渲染效果


2.BitmapShader 位圖著色器

構(gòu)造方法:
BitmapShader(@NonNull Bitmap bitmap, TileMode tileX, TileMode tileY)
構(gòu)造方法中除了一個Bitmap外影钉,還需要兩個TileMode枚舉類型的參數(shù),一個代表在x軸的模式廉赔,一個在y軸的模式

2.1 TileMode 瓷磚模式

TileMode是Shader中的一個枚舉蜡塌,有三個值

  • CLAMP 拉伸馏艾,圖片的最后的一個像素攒至,不斷重復(fù)
  • REPEAT 重復(fù),橫向账忘、縱向不斷重復(fù)
  • MIRROR 鏡像溉浙,橫向不斷翻轉(zhuǎn)重復(fù)戳稽,縱向不斷翻轉(zhuǎn)重復(fù)
   private void init() {
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        final Bitmap mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.w);
        final BitmapShader shader = new BitmapShader(mBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
        mPaint.setShader(shader);
    }
    /**
     * 利用 clmp得到圓形圖片
     */
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        float x = getWidth() / 2;
        float y = getHeight() / 2;
        float radius = Math.min(getWidth(), getHeight()) / 2;
        canvas.drawCircle(x, y, radius,mPaint);
    }

利用CLMP得到一個圓形圖片


image.png

image.png

這時圖片的下面部分已經(jīng)出現(xiàn)了問題播赁,明顯是最后一個元素被拉伸過多容为,使用這種方式得到圓形圖片替劈,拉伸后的圖片要大于控件的大小陨献,這樣最后一個元素既然被拉伸變形湿故,也看不到


2.2 BitmapShader下三種模式的效果

  • REPEAT 重復(fù)
    簡單修改代碼坛猪,將CLAMP變?yōu)?code>REPEAT,圖片資源變?yōu)?code>R.mipmap.ic_launcher就斤,畫的圖形由圓形變?yōu)榫匦窝蠡季种械膶挾雀臑?code>match_parent
    REPEAT.png

  • MIRROR 鏡像
    上下對稱洋魂,出現(xiàn)鏡像
    image.png

上面的情況兩個TileMode都是同一個值

BitmapShader總是先應(yīng)用Y軸上的模式后绷旗,再應(yīng)用X軸上的模式


3 LinearGradient 線性漸變

構(gòu)造方法喜鼓,有兩個:

LinearGradient(float x0, float y0, float x1, float y1, int color0, int color1,TileMode tile)

LinearGradient(float x0, float y0, float x1, float y1, int colors[], float positions[],TileMode tile)

3.1第一種構(gòu)造方法簡單使用:

  private void init() {
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        LinearGradient shader = new LinearGradient(0, 0, 600, 600, Color.CYAN, Color.BLUE, Shader.TileMode.REPEAT);
        mPaint.setShader(shader);
    }
  
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawRect(0,0,600,600,mPaint);
    }
  • x0 繪制x軸起始點(diǎn)
  • y0 繪制y軸起始點(diǎn)
  • x1 繪制x軸結(jié)束點(diǎn)
  • y1 繪制y軸結(jié)束點(diǎn)
  • color0 起始顏色
  • color1 結(jié)束顏色
  • tile 瓷磚模式
    LinearGradient第1種構(gòu)造方法.png

    效果很容易理解,在控件中從0,0左上角點(diǎn)到600,600右下角兩種顏色漸變

3.2第二種構(gòu)造方法簡單使用:

兩個構(gòu)造方法的區(qū)別在于衔肢,第4參數(shù)庄岖,是一個int[],第5個參數(shù)為一個float[]角骤,使用這個構(gòu)造方法可以設(shè)置多種顏色的漸變

  • colors 顏色int值數(shù)組
  • postions 數(shù)組中的值有效范圍是0f~1f隅忿,漸變結(jié)束所在區(qū)域的比例胳赌,1f的結(jié)束位置,與x1,y1有關(guān)
    LinearGradient第2種構(gòu)造方法.png

    三種顏色挺勿,在起始點(diǎn)(0,0)蚊丐,二分之一點(diǎn)(300,300),結(jié)束點(diǎn)(600,600)的漸變效果

3.3 圖片倒影效果:

思路:

  1. 繪制原圖呛梆,考慮繪制坐標(biāo)融痛,圖片縮放
  2. 繪制倒影,利用Matrix
  3. 繪制漸變層目派,利用PortDuffXfermode和LinearGradient
image.png

代碼如下:

public class ReflectView extends View {
    private Paint mPaint;
    private Bitmap dstBitmap, srcBitmap;
    private PorterDuffXfermode xfermode;
    private int x, y;

    public ReflectView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    private void init() {
        //原圖Bitmap
        dstBitmap = decodeBitmapFormRes(getResources(), R.drawable.wa,540, 960);
        //垂直翻轉(zhuǎn)
        Matrix matrix = new Matrix();
        matrix.setScale(1f, -1f);
        //倒影Bitmap
        srcBitmap = Bitmap.createBitmap(dstBitmap, 0, 0, dstBitmap.getWidth(), dstBitmap.getHeight(), matrix, true);
        //初始化畫筆
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        xfermode = new PorterDuffXfermode(PorterDuff.Mode.DST_IN);
         //屏幕寬度
        int screenW = getResources().getDisplayMetrics().widthPixels;
        //起始點(diǎn)
        x = screenW / 2 - dstBitmap.getWidth() / 2;
        y = 0;
        //設(shè)置漸變矩形
        mPaint.setShader(new LinearGradient(x, dstBitmap.getHeight(), x, dstBitmap.getHeight() + dstBitmap.getHeight() / 2, 0xDD000000, Color.TRANSPARENT, Shader.TileMode.CLAMP));
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //繪制背景
        canvas.drawColor(Color.BLACK);
        //繪制原圖
        canvas.drawBitmap(dstBitmap, x, y, null);
        //繪制倒影圖片
        canvas.drawBitmap(srcBitmap, x, dstBitmap.getHeight(), null);
        mPaint.setXfermode(xfermode);
        //繪制漸變層
        canvas.drawRect(x, dstBitmap.getHeight(), x + dstBitmap.getWidth(), dstBitmap.getHeight() * 2, mPaint);
        mPaint.setXfermode(null);
    }

    /**
     * 測量
     */
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int wSpecMode = MeasureSpec.getMode(widthMeasureSpec);
        int wSpecSize = MeasureSpec.getSize(widthMeasureSpec);
        int hSpecMode = MeasureSpec.getMode(heightMeasureSpec);
        int hSpecSize = MeasureSpec.getSize(heightMeasureSpec);

        if (wSpecMode == MeasureSpec.AT_MOST && hSpecMode == MeasureSpec.AT_MOST) {
            setMeasuredDimension(300, 300);
        } else if (wSpecMode == MeasureSpec.AT_MOST) {
            setMeasuredDimension(300, hSpecSize);
        } else if (hSpecMode == MeasureSpec.AT_MOST) {
            setMeasuredDimension(wSpecSize, 300);
        }
    }

    /**
     * 圖片的縮放
     */
    private Bitmap decodeBitmapFormRes(Resources resources, int resId, int targetWidth, int targetHeight) {
        final BitmapFactory.Options options = new BitmapFactory.Options();
        options.inPreferredConfig = Bitmap.Config.RGB_565;
        options.inJustDecodeBounds = false;
        BitmapFactory.decodeResource(resources, resId, options);
        int inSample = calculateInSample(options, targetWidth, targetHeight);
        options.inSampleSize = inSample;
        return BitmapFactory.decodeResource(resources, resId, options);
    }

    private int calculateInSample(BitmapFactory.Options options, int targetWidth, int targetHeight) {
        if (targetWidth <= 0 || targetHeight <= 0) {
            return 1;
        }
        int inSample = 1;
        final int rawWidth = options.outWidth;
        final int rawHeight = options.outHeight;
        if (rawWidth > targetWidth || rawHeight > targetHeight) {
            final int halfWidth = rawWidth / 2;
            final int halfHeight = rawHeight / 2;
            while ((halfWidth / inSample >= targetWidth) && (halfHeight / inSample >= targetHeight)) {
                inSample *= 2;
            }
        }
        return inSample;
    }
}

4.RadialGradient 光束漸變

兩個構(gòu)造方法:

RadialGradient(float centerX, float centerY, float radius,  int centerColor, int edgeColor, @NonNull TileMode tileMode)

RadialGradient(float centerX, float centerY, float radius, @NonNull int colors[], @Nullable float stops[], @NonNull TileMode tileMode)

private void init() {
        final RadialGradient shader = new RadialGradient(300f,300f,300,Color.RED,Color.YELLOW, Shader.TileMode.CLAMP);
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        //LinearGradient shader = new LinearGradient(0, 0, 600, 600,colors,positions, Shader.TileMode.REPEAT);
        mPaint.setShader(shader);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawRect(0,0,600,600,mPaint);
    }
RadialGradient第1種構(gòu)造方法.png
  • centerX 漸變中心點(diǎn)的X軸坐標(biāo)
  • centerY 漸變中心點(diǎn)的Y軸坐標(biāo)
  • radius 漸變區(qū)域的半徑
    控件大小為600 * 600(300f,300f)為控件的中心土辩,半徑為300

第二種構(gòu)造方法簡單使用:

  private void init() {
        final int[] colors = new int[]{Color.YELLOW,Color.BLUE ,Color.RED};
        final float[] positions = new float[]{0f, 0.5f ,1f};
        final RadialGradient shader = new RadialGradient(300f,300f,300,colors,positions, Shader.TileMode.CLAMP);
         mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
         mPaint.setShader(shader);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawRect(0,0,600,600,mPaint);
    }
RadialGradient第2種構(gòu)造方法.png

colors中的元素個數(shù)要和positions中的元素個數(shù)相等


5. SweepGradient 梯度漸變

兩個構(gòu)造方法:

SweepGradient(float cx, float cy, int color0, int color1) 
SweepGradient(float cx, float cy, int colors[], float positions[])

cx,cy是旋轉(zhuǎn)點(diǎn)的x,y軸坐標(biāo),漸變過程總是順時針方向旋轉(zhuǎn)

第一種構(gòu)造方法簡單使用:

private void init() {
        final SweepGradient shader = new SweepGradient(300f,300f,Color.RED,Color.YELLOW);
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint.setShader(shader);
         mPaint.setShader(shader);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawRect(0,0,600,600,mPaint);
    }

第一種構(gòu)造方法簡單使用

控件的大小為600 * 600,300f * 300f就是整個控件的中心,可以試試其他的值


第二種構(gòu)造方法簡單使用:


image.png
 private void init() {
        final int[] colors = new int[]{Color.MAGENTA, Color.CYAN,Color.YELLOW,Color.BLUE ,Color.RED};
        final float[] positions = new float[]{0f, 0.25f,0.5f ,0.75f,1f};
        final SweepGradient shader = new SweepGradient(300f, 300f, colors, positions);

        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint.setShader(shader);
         mPaint.setShader(shader);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawRect(0,0,600,600,mPaint);
    }

6. ComposeShader 混合漸變

兩個構(gòu)造方法:
···
ComposeShader(Shader shaderA, Shader shaderB, PorterDuff.Mode mode)
ComposeShader(Shader shaderA, Shader shaderB, Xfermode mode)
···
構(gòu)造方法中,前兩個參數(shù)相同补鼻,都是需要一個著色器沪么,差別在于第三個參數(shù)州胳。第一個構(gòu)造方法需要一個PorterDuff.Mode瓤湘,而第二個構(gòu)造構(gòu)造方法需要PorterDuffXfermode

前面的三種的漸變,都是一種單一的漸變,ComposeShader可以把前面兩種漸變混合進(jìn)一種漸變效果
簡單使用:

image.png

private void init() {
    setLayerType(View.LAYER_TYPE_SOFTWARE, null);//關(guān)閉硬件加速
    mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    final LinearGradient linearGradient = new LinearGradient(0, 0, 600, 600,Color.GREEN ,Color.BLUE ,Shader.TileMode.CLAMP);
    final RadialGradient radialGradient = new RadialGradient(300f,300f,300,Color.RED,Color.YELLOW, Shader.TileMode.CLAMP);
       
    final ComposeShader shader = new ComposeShader(linearGradient, radialGradient, PorterDuff.Mode.SCREEN);
    mPaint.setShader(shader);
}

必須要把硬件加速關(guān)閉抵赢,ComposeShader混合漸變效果才會出現(xiàn)淡诗,否則屏幕一片空白,手邊暫時沒有了別的手機(jī),不知道會不會也是這樣

第3個參數(shù)杉编,new PorterDuffXfermode(PorterDuff.Mode.SCREEN)PorterDuff.Mode.SCREEN這兩種寫法有啥區(qū)別救军,暫時也沒看出來

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末订晌,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子谷浅,更是在濱河造成了極大的恐慌眉睹,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,548評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件猛频,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)淹禾,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,497評論 3 399
  • 文/潘曉璐 我一進(jìn)店門隆檀,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人为鳄,你說我怎么就攤上這事队橙〈悖” “怎么了倾鲫?”我有些...
    開封第一講書人閱讀 167,990評論 0 360
  • 文/不壞的土叔 我叫張陵伶丐,是天一觀的道長。 經(jīng)常有香客問我利职,道長趣效,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,618評論 1 296
  • 正文 為了忘掉前任猪贪,我火速辦了婚禮跷敬,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘热押。我一直安慰自己西傀,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,618評論 6 397
  • 文/花漫 我一把揭開白布桶癣。 她就那樣靜靜地躺著拥褂,像睡著了一般。 火紅的嫁衣襯著肌膚如雪牙寞。 梳的紋絲不亂的頭發(fā)上饺鹃,一...
    開封第一講書人閱讀 52,246評論 1 308
  • 那天莫秆,我揣著相機(jī)與錄音,去河邊找鬼悔详。 笑死镊屎,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的茄螃。 我是一名探鬼主播缝驳,決...
    沈念sama閱讀 40,819評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼归苍!你這毒婦竟也來了用狱?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,725評論 0 276
  • 序言:老撾萬榮一對情侶失蹤霜医,失蹤者是張志新(化名)和其女友劉穎齿拂,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體肴敛,經(jīng)...
    沈念sama閱讀 46,268評論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡署海,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,356評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了医男。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片砸狞。...
    茶點(diǎn)故事閱讀 40,488評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖镀梭,靈堂內(nèi)的尸體忽然破棺而出刀森,到底是詐尸還是另有隱情,我是刑警寧澤报账,帶...
    沈念sama閱讀 36,181評論 5 350
  • 正文 年R本政府宣布研底,位于F島的核電站,受9級特大地震影響透罢,放射性物質(zhì)發(fā)生泄漏榜晦。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,862評論 3 333
  • 文/蒙蒙 一羽圃、第九天 我趴在偏房一處隱蔽的房頂上張望乾胶。 院中可真熱鬧,春花似錦朽寞、人聲如沸识窿。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,331評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽喻频。三九已至,卻和暖如春吨掌,著一層夾襖步出監(jiān)牢的瞬間半抱,已是汗流浹背脓恕。 一陣腳步聲響...
    開封第一講書人閱讀 33,445評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留窿侈,地道東北人炼幔。 一個月前我還...
    沈念sama閱讀 48,897評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像史简,于是被迫代替她去往敵國和親乃秀。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,500評論 2 359

推薦閱讀更多精彩內(nèi)容