Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android繪圖機制與處理技巧(四)——Android圖像處理之畫筆特效處理

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

編輯:關於Android編程

除了常用的畫筆屬性,比如普通的畫筆(Paint),帶邊框、填充的style,顏色(Color),寬度(StrokeWidth),抗鋸齒(ANTI_ALIAS_FLAG)等,Android還提供了各種各樣專業的畫筆工具,如記號筆、毛筆、蠟筆等,使用它們可以實現更加豐富的效果。

PorterDuffXfermode

下圖中列舉了16種PorterDuffXfermode,有點像數學中集合的交集、並集這樣的概念,它控制的是兩個圖像間的混合顯示模式。

這裡寫圖片描述vcq9o6xkc3TKx8/Iu621xM280M6jrLb4c3Jjyse687uttcTNvNDOoaM8L3A+DQo8cD7V4tCpxKPKvdKysrvKx76ts6PKudPDo6zTw7XE1+624LXEysejrMq508PSu9XFzbzGrNf3zqrB7dK71cXNvMastcTV2tXWsuOjrM2ouf2/2NbG1drV1rLjtcTNvNDOo6zAtL/Y1sbPwsPmsbvV2tXWzbzQzrXEz9TKvtCnufuho8bk1tDX7rOj08O1xL7NysfNqLn9RFNUX0lOoaJTUkNfSU7Eo8q9wLTKtc/WvavSu7j2vtjQzs28xqyx5LPJ1LK9x828xqy78tXf1LLQzs28xqy1xNCnufuhozwvcD4NCjxoNCBpZD0="圓角矩形">圓角矩形

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

這裡寫圖片描述

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

        Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.test1);
        mBitmap = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(mBitmap);
        Paint paint = new Paint();
        paint.setAntiAlias(true);
        canvas.drawRoundRect(0, 0, bitmap.getWidth(), bitmap.getHeight(), 80, 80, paint);
        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
        canvas.drawBitmap(bitmap, 0, 0, paint);

刮刮卡效果

若要實現刮刮卡效果(刮刮卡一般有兩個圖層,即上面的用來被刮掉的圖層和下面隱藏的圖層)。在初始狀態下,上面的圖層會將下面整個圖層覆蓋,當你用手刮上面的圖層的時候,下面的圖層會慢慢顯示出來,類似於畫圖工具中的橡皮擦效果。

首先要做一些初始化工作,例如准備好圖片,設置好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);
獲取用戶手指滑動所產生的路徑並將使用DST_IN模式將路徑繪制到前面覆蓋的圖層上,代碼如下所示。使用Path保存用戶手指劃過的痕跡。當然,如果使用貝塞爾曲線來做優化則會得到更好的顯示效果,這裡為了簡化功能,就不使用貝塞爾曲線了。
    @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 onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawBitmap(mBgBitmap, 0, 0, null);
        canvas.drawBitmap(mFgBitmap, 0, 0, null);
    }

效果圖如下所示:

這裡寫圖片描述

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

Shader

Shader又被稱之為著色器、渲染器,它用來實現一系列的漸變、渲染效果。Android中的Shader包括以下幾種。

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

BitmapShader

與其他的Shader所產生的漸變不同,BitmapShader產生的是一個圖像,這有點像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();
        //用一張圖片創建一支具有圖像填充功能的畫筆,並使用這只畫筆繪制一個圓形
        paint.setShader(bitmapShader);
        canvas.drawCircle(500, 250, 200, paint);
    }

如果把TileMode改為REPEAT,並將Bitmap改為較小的ic_launcher圖標,就可以看到幾種模式的區別了,代碼如下:

    @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方法參數中的TileMode與在BitmapShader中的含義基本相同,這裡將繪制矩形的大小設置為與漸變圖像大小相同,所以沒有看見REPEAT的效果。如果將圖形擴大,REPEAT的效果就出來了,如下圖所示:

這裡寫圖片描述

倒影效果

這些漸變效果通常不會直接使用在程序裡。通常情況下,把這種漸變效果作為一個遮罩層來使用,同時結合PorterDuffXfermode。這樣處理後,遮罩層就不再是一個生硬的圖形,而是一個具有漸變效果的圖層。這樣處理的效果會更加柔和、更加自然。下面用LinearGradient和PorterDuffXfermode來創建一個具有倒影效果的圖片,效果圖如下:

這裡寫圖片描述

代碼如下:

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();
        //實現圖片的垂直翻轉,避免使用旋轉變換的復雜計算
        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系統提供了如下中展示的幾種繪制PathEffect的方式,從上到下依次是:

這裡寫圖片描述

沒效果 CornerPathEffect——就是將拐角處變得圓滑,具體圓滑的程度,則由參數決定 DiscretePathEffect——使用這個效果之後,線段上就會產生許多雜點 DashPathEffect——這個效果可以用來繪制虛線,用一個數組來設置各個點之間的間隔。此後繪制虛線時就重復這樣的間隔進行繪制,另一個參數phase則用來控制繪制時數組的一個偏移量,通常可以通過設置值來實現路徑的動態效果。 PathDashPathEffect——這個效果與DashPathEffect類似,只不過它的功能更加強大,可以設置顯示點的圖形,即方形點的虛線、圓形點的虛線。 ComposePathEffect——通過ComposePathEffect來組合PathEffect,就是將任意兩種路徑特性組合起來形成一個新的效果。

上圖實現代碼如下:

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);

        //使用隨機數來生成一些隨機的點形成一條路徑
        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);
        }
    }
}

 

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved