Android繪圖機制與處理技巧——Android圖像處理之畫筆特效處理

除了常用的畫筆屬性,比如普通的畫筆(Paint)绑咱,帶邊框显拜、填充的style烙如,顏色(Color),寬度(StrokeWidth)苹祟,抗鋸齒(ANTI_ALIAS_FLAG)等砸抛,Android還提供了各種各樣專業(yè)的畫筆工具,如記號筆树枫、毛筆直焙、蠟筆等,使用它們可以實現(xiàn)更加豐富的效果砂轻。

PorterDuffXfermode

下圖中列舉了16種PorterDuffXfermode奔誓,有點像數(shù)學中集合的交集、并集這樣的概念搔涝,它控制的是兩個圖像間的混合顯示模式厨喂。


7A323AB4-C43C-4313-B964-5934C81EE7F0.png

PorterDuffXfermode設置的是兩個圖層交集區(qū)域的顯示方式和措,dst是先畫的圖形,而src是后畫的圖形杯聚。

這些模式也不是經(jīng)常使用臼婆,用的最多的是抒痒,使用一張圖片作為另一張圖片的遮罩層幌绍,通過控制遮罩層的圖形,來控制下面被遮罩圖形的顯示效果故响。其中最常用的就是通過DST_IN傀广、SRC_IN模式來實現(xiàn)將一個矩形圖片變成圓角圖片或者圓形圖片的效果。

  • 圓角矩形

要使用PorterDuffXfermode非常簡單彩届,只需要讓畫面擁有這個屬性就可以了伪冰。比如要實現(xiàn)下面圓角矩形的效果:

device-2017-04-01-131542.png

先用一個普通畫筆畫一個Mask遮罩層,再用帶PorterDuffXfermode的畫筆將圖像畫在遮罩層上樟蠕,這樣就可以通過上面所說的效果來混合兩個圖像了贮聂,代碼如下:

    mPaint=new Paint();

    Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.iu);

    mBitmap = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.ARGB_8888);

    Canvas canvas=new Canvas(mBitmap);

    mPaint.setAntiAlias(true);

    canvas.drawRoundRect(0,0,bitmap.getWidth(),bitmap.getHeight(),80,80,mPaint);

    mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));

    canvas.drawBitmap(bitmap,0,0,mPaint);

    mImageView= (ImageView) findViewById(R.id.img);
    mImageView.setImageBitmap(mBitmap);
  • 刮刮卡效果

若要實現(xiàn)刮刮卡效果(刮刮卡一般有兩個圖層,即上面的用來被刮掉的圖層和下面隱藏的圖層)寨辩。在初始狀態(tài)下吓懈,上面的圖層會將下面整個圖層覆蓋,當你用手刮上面的圖層的時候靡狞,下面的圖層會慢慢顯示出來耻警,類似于畫圖工具中的橡皮擦效果。

首先要做一些初始化工作甸怕,例如準備好圖片甘穿,設置好Paint的一些屬性,代碼如下:

     mPaint = new Paint();
     /**
     * 將畫筆的透明度設置為0梢杭,這樣才能顯示出擦除效果温兼。
     * 在使用PorterDuffXfermode進行圖層混合時,并不是簡單地只進行圖層的計算武契,同時也會計算透明通道的值妨托。
     * 正是由于混合了透明通道,才形成了這樣的效果吝羞。
     */
    mPaint.setAlpha(0);
    mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
    mPaint.setStyle(Paint.Style.STROKE);
    //使Paint的筆觸更加圓滑一點
    mPaint.setStrokeJoin(Paint.Join.ROUND);
    mPaint.setStrokeWidth(50);
    //使Paint的連接處更加圓滑一點
    mPaint.setStrokeCap(Paint.Cap.ROUND);

    mPath = new Path();
    mBgBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.test);
    mFgBitmap = Bitmap.createBitmap(mBgBitmap.getWidth(), mBgBitmap.getHeight(), Bitmap.Config.ARGB_8888);

    mCanvas = new Canvas(mFgBitmap);
    mCanvas.drawColor(Color.GRAY);

獲取用戶手指滑動所產(chǎn)生的路徑并將使用DST_IN模式將路徑繪制到前面覆蓋的圖層上兰伤,代碼如下所示。使用Path保存用戶手指劃過的痕跡钧排。當然敦腔,如果使用貝塞爾曲線來做優(yōu)化則會得到更好的顯示效果,這里為了簡化功能恨溜,就不使用貝塞爾曲線了符衔。

@Override
public boolean onTouchEvent(MotionEvent event) {
    switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            mPath.reset();
            mPath.moveTo(event.getX(), event.getY());
            break;
        case MotionEvent.ACTION_MOVE:
            mPath.lineTo(event.getX(), event.getY());
            break;
    }
    //使用DST_IN模式將路徑繪制到前面覆蓋的圖層上
    mCanvas.drawPath(mPath, mPaint);
    invalidate();
    return true;
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    setMeasuredDimension(600,400);
}

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    canvas.drawBitmap(mBgBitmap, 0, 0, null);
    canvas.drawBitmap(mFgBitmap, 0, 0, null);
}

效果圖如下所示:

4月-01-2017 13-53-23.gif

在使用PorterDuffXfermode時還有一點需要注意找前,那就是最好在繪圖時,將硬件加速關閉判族,因為有些模式并不支持硬件加速躺盛。

Shader

Shader又被稱之為著色器、渲染器形帮,它用來實現(xiàn)一系列的漸變槽惫、渲染效果。Android中的Shader包括以下幾種辩撑。

BitmapShader——位圖Shader
LinearGradient——線性Shader
RadialGradient——光束Shader
SweepGradient——梯度Shader
ComposeGradient——混合Shader

  • BitmapShader

與其他的Shader所產(chǎn)生的漸變不同界斜,BitmapShader產(chǎn)生的是一個圖像,這有點像Photoshop中的圖像填充漸變合冀。它的作用就是通過Paint對畫布進行指定Bitmap的填充各薇,填充時有以下幾種模式可以選擇。

CLAMP拉伸——拉伸的是圖片最后的那一個像素君躺,不斷重復
REPEAT重復——橫向峭判、縱向不斷重復
MIRROR鏡像——橫向不斷翻轉重復,縱向不斷翻轉重復
這里最常用的就是CLAMP拉伸模式棕叫,雖然它會拉伸最后一個像素林螃,但是只要將圖像設置為一定的大小,就可以避免這種拉伸谍珊。下面將一個矩形的圖片變成一張圓形的圖片治宣,效果如下:

代碼如下:

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.test);
    BitmapShader bitmapShader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
    Paint paint = new Paint();
    //用一張圖片創(chuàng)建一支具有圖像填充功能的畫筆,并使用這只畫筆繪制一個圓形
    paint.setShader(bitmapShader);
    canvas.drawCircle(500, 250, 200, paint);
}

如果把TileMode改為REPEAT砌滞,并將Bitmap改為較小的ic_launcher圖標侮邀,就可以看到幾種模式的區(qū)別了,代碼如下:

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher);
    BitmapShader bitmapShader = new BitmapShader(bitmap, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT);
    Paint paint = new Paint();
    paint.setShader(bitmapShader);
    canvas.drawCircle(500, 250, 200, paint);
}

運行程序贝润,效果圖如下:

  • LinearGradient

要使用LinearGradient非常簡單绊茧,只需要指定漸變的起始顏色即可,代碼如下:

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    Paint paint = new Paint();
    paint.setShader(new LinearGradient(0, 0, 400, 400, Color.BLUE, Color.YELLOW, Shader.TileMode.REPEAT));
    canvas.drawRect(0, 0, 400, 400, paint);
}

效果圖如下圖打掘,他是一個從(0, 0)到(400, 400)的由藍色到黃色的漸變效果华畏。

LinearGradient方法參數(shù)中的TileMode與在BitmapShader中的含義基本相同,這里將繪制矩形的大小設置為與漸變圖像大小相同尊蚁,所以沒有看見REPEAT的效果亡笑。如果將圖形擴大,REPEAT的效果就出來了横朋,如下圖所示:

倒影效果

這些漸變效果通常不會直接使用在程序里仑乌。通常情況下,把這種漸變效果作為一個遮罩層來使用,同時結合PorterDuffXfermode晰甚。這樣處理后衙传,遮罩層就不再是一個生硬的圖形,而是一個具有漸變效果的圖層厕九。這樣處理的效果會更加柔和蓖捶、更加自然。下面用LinearGradient和PorterDuffXfermode來創(chuàng)建一個具有倒影效果的圖片扁远,效果圖如下:

代碼如下:


    public class ReflectView extends View {

    private Bitmap mSrcBitmap, mRefBitmap;
    private Paint mPaint;
    private PorterDuffXfermode mXfermode;

    public ReflectView(Context context) {
        super(context);
        initRes(context);
    }

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

    public ReflectView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initRes(context);
    }

    private void initRes(Context context) {
        mSrcBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.test);
        Matrix matrix = new Matrix();
        //實現(xiàn)圖片的垂直翻轉俊鱼,避免使用旋轉變換的復雜計算
        matrix.setScale(1, -1);
        mRefBitmap = Bitmap.createBitmap(mSrcBitmap, 0, 0, mSrcBitmap.getWidth(),
                mSrcBitmap.getHeight(), matrix, true);

        mPaint = new Paint();
        mPaint.setShader(new LinearGradient(0, mSrcBitmap.getHeight(), 0,
                mSrcBitmap.getHeight() + mSrcBitmap.getHeight() / 2, 0XDD000000, 0X10000000,
                Shader.TileMode.CLAMP));
        mXfermode = new PorterDuffXfermode(PorterDuff.Mode.DST_IN);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawColor(Color.BLACK);
        //繪制原圖
        canvas.drawBitmap(mSrcBitmap, 0, 0, null);
        //繪制倒影圖
        canvas.drawBitmap(mRefBitmap, 0, mSrcBitmap.getHeight(), null);
        mPaint.setXfermode(mXfermode);
        //繪制漸變效果矩形
        canvas.drawRect(0, mSrcBitmap.getHeight(), mSrcBitmap.getWidth(), mSrcBitmap.getHeight() * 2, mPaint);
        mPaint.setXfermode(null);
    }
}

PathEffect

PathEffect就是指用用各種筆觸效果來繪制路徑。Android系統(tǒng)提供了如下中展示的幾種繪制PathEffect的方式穿香,從上到下依次是:


59E4185D-8D67-4F98-AF65-D8F33EBF2E0D.png

沒效果
CornerPathEffect——就是將拐角處變得圓滑亭引,具體圓滑的程度绎速,則由參數(shù)決定
DiscretePathEffect——使用這個效果之后皮获,線段上就會產(chǎn)生許多雜點
DashPathEffect——這個效果可以用來繪制虛線,用一個數(shù)組來設置各個點之間的間隔纹冤。此后繪制虛線時就重復這樣的間隔進行繪制洒宝,另一個參數(shù)phase則用來控制繪制時數(shù)組的一個偏移量,通趁染可以通過設置值來實現(xiàn)路徑的動態(tài)效果雁歌。
PathDashPathEffect——這個效果與DashPathEffect類似,只不過它的功能更加強大知残,可以設置顯示點的圖形靠瞎,即方形點的虛線、圓形點的虛線求妹。
ComposePathEffect——通過ComposePathEffect來組合PathEffect乏盐,就是將任意兩種路徑特性組合起來形成一個新的效果。
上圖實現(xiàn)代碼如下:


public class PathEffectView extends View {

    private Paint mPaint;
    private Path mPath;
    private PathEffect[] mEffects;

    public PathEffectView(Context context) {
        super(context);
        init();
    }

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

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

    private void init() {
        mPaint = new Paint();
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeWidth(5);
        mPaint.setColor(Color.DKGRAY);

        //使用隨機數(shù)來生成一些隨機的點形成一條路徑
        mPath = new Path();
        mPath.moveTo(0, 0);
        for (int i = 0; i < 30; i++) {
            mPath.lineTo(i * 35, (float) (Math.random() * 100));
        }

        mEffects = new PathEffect[6];
        mEffects[0] = null;
        mEffects[1] = new CornerPathEffect(30);
        mEffects[2] = new DiscretePathEffect(3.0f, 5.0f);
        mEffects[3] = new DashPathEffect(new float[]{20, 10, 5, 10}, 0);
        Path path = new Path();
        path.addRect(0, 0, 8, 8, Path.Direction.CCW);
        mEffects[4] = new PathDashPathEffect(path, 12, 0, PathDashPathEffect.Style.ROTATE);
        mEffects[5] = new ComposePathEffect(mEffects[3], mEffects[1]);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        for (int i = 0; i < mEffects.length; i++) {
            mPaint.setPathEffect(mEffects[i]);
            canvas.drawPath(mPath, mPaint);
            canvas.translate(0, 200);
        }
    }
}
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末制恍,一起剝皮案震驚了整個濱河市父能,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌净神,老刑警劉巖何吝,帶你破解...
    沈念sama閱讀 218,858評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異鹃唯,居然都是意外死亡爱榕,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評論 3 395
  • 文/潘曉璐 我一進店門坡慌,熙熙樓的掌柜王于貴愁眉苦臉地迎上來黔酥,“玉大人,你說我怎么就攤上這事⌒跻” “怎么了趴酣?”我有些...
    開封第一講書人閱讀 165,282評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長坑夯。 經(jīng)常有香客問我岖寞,道長,這世上最難降的妖魔是什么柜蜈? 我笑而不...
    開封第一講書人閱讀 58,842評論 1 295
  • 正文 為了忘掉前任仗谆,我火速辦了婚禮,結果婚禮上淑履,老公的妹妹穿的比我還像新娘隶垮。我一直安慰自己,他們只是感情好秘噪,可當我...
    茶點故事閱讀 67,857評論 6 392
  • 文/花漫 我一把揭開白布狸吞。 她就那樣靜靜地躺著,像睡著了一般指煎。 火紅的嫁衣襯著肌膚如雪蹋偏。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,679評論 1 305
  • 那天至壤,我揣著相機與錄音威始,去河邊找鬼。 笑死像街,一個胖子當著我的面吹牛黎棠,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播镰绎,決...
    沈念sama閱讀 40,406評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼脓斩,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了跟狱?” 一聲冷哼從身側響起俭厚,我...
    開封第一講書人閱讀 39,311評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎驶臊,沒想到半個月后挪挤,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,767評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡关翎,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年扛门,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片纵寝。...
    茶點故事閱讀 40,090評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡论寨,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情葬凳,我是刑警寧澤绰垂,帶...
    沈念sama閱讀 35,785評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站火焰,受9級特大地震影響劲装,放射性物質發(fā)生泄漏。R本人自食惡果不足惜昌简,卻給世界環(huán)境...
    茶點故事閱讀 41,420評論 3 331
  • 文/蒙蒙 一占业、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧纯赎,春花似錦谦疾、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至佑附,卻和暖如春樊诺,著一層夾襖步出監(jiān)牢的瞬間仗考,已是汗流浹背音同。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評論 1 271
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留秃嗜,地道東北人权均。 一個月前我還...
    沈念sama閱讀 48,298評論 3 372
  • 正文 我出身青樓,卻偏偏與公主長得像锅锨,于是被迫代替她去往敵國和親叽赊。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,033評論 2 355

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