天天看點

Android Api Demos登頂之路(九十一)Graphics-->TouchPaint

/*
 * 這個demon可以和Graphics->FingerPaint一起對比了解,隻不過在本例中應用了多點觸控,
 * 通過MotionEvent的一系列統方法檢測觸摸屏的壓力和接觸區域的大小等資訊,以實作繪制出一些
 * 特殊的效果。在本例中我們對原來的demon進行簡化,隻針對現在最常用的觸摸屏手機進行示範。
 */
public class MainActivity extends Activity {
    private boolean isDrawSplat;
    private boolean fading;
    private static final int[] COLORS = new int[] { Color.BLACK, Color.RED,
            Color.YELLOW, Color.GREEN, Color.CYAN, Color.BLUE, Color.MAGENTA, };
    private static final int BACKGROUND_COLOR = Color.WHITE;
    private SampleView mView;

    private static final int MSG_FADE = ;
    private static final int CLEAR_ID = Menu.FIRST;
    private static final int FADE_ID = Menu.FIRST+;
    private static final int DRAWSPLAT_ID = Menu.FIRST+;
    private static final int FADE_DELAY = ;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mView=new SampleView(this);
        setContentView(mView);
        mView.requestFocus();

        if(savedInstanceState!=null){
            fading=savedInstanceState.getBoolean("fading",true);
        }else{
            fading=true;
        }
    }

    //建立菜單
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        menu.add(,CLEAR_ID,,"Clear");
        menu.add(,FADE_ID,,"Fade").setCheckable(true);
        menu.add(,DRAWSPLAT_ID,,"Drawsplat");
        return super.onCreateOptionsMenu(menu);
    }

    @Override
    public boolean onPrepareOptionsMenu(Menu menu) {
        menu.findItem(FADE_ID).setChecked(fading);
        return super.onPrepareOptionsMenu(menu);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
        case CLEAR_ID:
            mView.clear();
            return true;
        case FADE_ID:
            fading=!fading;
            if(fading){
                startFading();
            }else{
                stopFading();
            }
            return true;
        case DRAWSPLAT_ID:
            isDrawSplat=true;
            mView.invalidate();
            return true;
        default:
            return super.onOptionsItemSelected(item);
        }
    }

    private Handler mHandler=new Handler(){
        public void handleMessage(android.os.Message msg) {
            switch (msg.what) {
            case MSG_FADE:
                mView.fade();
                sheduleFade();
                break;
            default:
                super.handleMessage(msg);
                break;
            }
        };
    };


    private void stopFading() {
        mHandler.removeMessages(MSG_FADE);
    }

    private void startFading() {
        mHandler.removeMessages(MSG_FADE);
        sheduleFade();
    }



    private void sheduleFade() {
        mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_FADE), FADE_DELAY);
    }

    @Override
    protected void onResume() {
        super.onResume();
        if(fading){
            startFading();
        }
    }

    @Override
    protected void onPause() {
        super.onPause();
        stopFading();
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putBoolean("fading", fading);
    }

    private class SampleView extends View {
        private Paint mPaint;
        private Paint mFadePaint;
        private Bitmap mBitmap;
        private Canvas mCanvas;
        private Random mRandom = new Random();

        private static final int SPLAT_VECTORS = ;
        private static final int FADE_ALPHA = ;
        private static final int MAX_FADE_STEPS =  / FADE_ALPHA + ;
        private int mFadeSteps = MAX_FADE_STEPS;

        public SampleView(Context context) {
            super(context);
            setFocusable(true);
            setFocusableInTouchMode(true);

            mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
            int index = (int) (mRandom.nextFloat() * COLORS.length);
            mPaint.setColor(COLORS[index]);

            mFadePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
            mFadePaint.setColor(BACKGROUND_COLOR);
            mFadePaint.setAlpha(FADE_ALPHA);

            //System.out.println("MAX_FADE_STEPS:" + MAX_FADE_STEPS);
        }

        // 清除繪圖闆中的内容
        public void clear() {
            if (mCanvas != null) {
                mPaint.setColor(BACKGROUND_COLOR);
                // 用背景色重新整理畫布
                mCanvas.drawPaint(mPaint);
                invalidate();

                mFadeSteps = MAX_FADE_STEPS;
            }
        }

        /*
         * 當選擇fade菜單時将實作漸隐效果,通過一次次使用透明度為FADE_ALPHA顔色為背景色的畫筆不斷重新整理
         * 畫布,共重新整理MAX_FADE_STEPS次來實作逐漸褪色的效果
         */
        public void fade() {
            if (mCanvas != null && mFadeSteps < MAX_FADE_STEPS) {
                mCanvas.drawPaint(mFadePaint);
                invalidate();
                mFadeSteps++;
            }
        }

        // 當螢幕尺寸變化時使畫闆占滿螢幕
        @Override
        protected void onSizeChanged(int w, int h, int oldw, int oldh) {
            int curW = mBitmap != null ? mBitmap.getWidth() : ;
            int curH = mBitmap != null ? mBitmap.getHeight() : ;
            if (curW >= w && curH >= h) {
                return;
            }

            if (curW < w)
                curW = w;
            if (curH < h)
                curH = h;

            Bitmap newBitmap = Bitmap.createBitmap(curW, curH,
                    Bitmap.Config.ARGB_8888);
            Canvas newCanvas = new Canvas();
            newCanvas.setBitmap(newBitmap);
            if (mBitmap != null) {
                newCanvas.drawBitmap(mBitmap, , , null);
            }
            mBitmap = newBitmap;
            mCanvas = newCanvas;
            // 初始化mFadeSteps
            mFadeSteps = MAX_FADE_STEPS;
        }

        @Override
        protected void onDraw(Canvas canvas) {
            if (mBitmap != null) {
                canvas.drawBitmap(mBitmap, , , null);
            }
        }

        @Override
        public boolean onTouchEvent(MotionEvent event) {
            // 傳回執行的動作,例如按下觸摸屏或者劃動
            int action = event.getActionMasked();
            if (action == MotionEvent.ACTION_DOWN
                    || action == MotionEvent.ACTION_MOVE
                    || action == MotionEvent.ACTION_HOVER_MOVE) {
                // 當執行滑動動作時,傳回可用的運動位置的數目。因為系統根據手指移動的距離來判定
                // Move動作,該傳回值即為即在判定為是一次滑動動作時在手指移動的距離内記錄的點的
                // 數量,我們可以周遊這些點并擷取到這些點的資訊。也可以了解為傳回曆史觸摸區域的大小
                int N = event.getHistorySize();
                // System.out.println("N:"+N);
                // 擷取觸控點的個數
                int p = event.getPointerCount();
                for (int i = ; i < N; i++) {
                    for (int j = ; j < p; j++) {
                        paint(event.getHistoricalX(j, i), event.getHistoricalY(
                                j, i), event.getHistoricalPressure(j, i),
                                event.getHistoricalTouchMajor(j, i),
                                event.getHistoricalTouchMinor(j, i),
                                event.getHistoricalOrientation(j, i),
                                event.getHistoricalAxisValue(
                                        MotionEvent.AXIS_DISTANCE, j, i),
                                event.getHistoricalAxisValue(
                                        MotionEvent.AXIS_TILT, j, i));

                    }
                }

                for (int j = ; j < p; j++) {
                    paint(// 擷取目前觸點的坐标
                    event.getX(j), event.getY(j),
                    // 擷取目前觸點的壓力值
                            event.getPressure(j),
                            // 擷取接觸區域(定義為一個橢圓形)長軸的長度
                            event.getTouchMajor(j),
                            // 接觸區域短軸的長度
                            event.getTouchMinor(j),
                            // 以垂直方向為準接觸區域按順時針方向偏向的弧度值
                            event.getOrientation(j),
                            // 擷取動作事件的距離
                            event.getAxisValue(MotionEvent.AXIS_DISTANCE, j),
                            // 擷取動作事件的傾斜度
                            event.getAxisValue(MotionEvent.AXIS_TILT, j));

                }
            }
            return true;
        }

        private void paint(float x, float y, float pressure, float major,
                float minor, float orientation, float distace, float tilt) {
            if (mBitmap != null) {
                // 如果接觸區域的大小不可用,則使用預設大小
                if (major <=  || minor <= ) {
                    major = minor = ;
                }
            }

            if (isDrawSplat == true) {
                mPaint.setAlpha();
                drawSplat(mCanvas, x, y, orientation, distace, tilt, mPaint);
            }else{
                 mPaint.setAlpha(Math.min((int)(pressure * ), ));
                 drawOval(mCanvas, x, y, major, minor, orientation, mPaint);
            }

            mFadeSteps=;
            invalidate();
        }

        private RectF reusableOvalRect=new RectF();
        private void drawOval(Canvas canvas, float x, float y, float major,
                float minor, float orientation, Paint paint) {
            canvas.save(Canvas.MATRIX_SAVE_FLAG);
            canvas.rotate((float) (orientation*/Math.PI), x, y);
            reusableOvalRect.left=x-minor/;
            reusableOvalRect.right=x+minor/;
            reusableOvalRect.top=y-major/;
            reusableOvalRect.bottom=y+major/;
            canvas.drawOval(reusableOvalRect, mPaint);
            canvas.restore();
        }

        // 定義具有擴散效果的畫筆圖案
        private void drawSplat(Canvas canvas, float x, float y,
                float orientation, float distance, float tilt, Paint paint) {
            float z = distance *  + ;

            // Calculate the center of the spray.
            float nx = (float) (Math.sin(orientation) * Math.sin(tilt));
            float ny = (float) (-Math.cos(orientation) * Math.sin(tilt));
            float nz = (float) Math.cos(tilt);
            if (nz < ) {
                return;
            }
            float cd = z / nz;
            float cx = nx * cd;
            float cy = ny * cd;

            for (int i = ; i < SPLAT_VECTORS; i++) {
                double direction = mRandom.nextDouble() * Math.PI * ;
                double dispersion = mRandom.nextGaussian() * ;
                double vx = Math.cos(direction) * dispersion;
                double vy = Math.sin(direction) * dispersion;
                double vz = ;

                // Apply the nozzle tilt angle.
                double temp = vy;
                vy = temp * Math.cos(tilt) - vz * Math.sin(tilt);
                vz = temp * Math.sin(tilt) + vz * Math.cos(tilt);

                // Apply the nozzle orientation angle.
                temp = vx;
                vx = temp * Math.cos(orientation) - vy * Math.sin(orientation);
                vy = temp * Math.sin(orientation) + vy * Math.cos(orientation);

                // Determine where the paint will hit the surface.
                if (vz < ) {
                    continue;
                }
                float pd = (float) (z / vz);
                float px = (float) (vx * pd);
                float py = (float) (vy * pd);

                // Throw some paint at this location, relative to the center of
                // the spray.
                mCanvas.drawCircle(x + px - cx, y + py - cy, f, paint);
            }

        }

    }
}
           

繼續閱讀