Android圖形解鎖的繪製

先上圖:


wKioL1L439awMlQLAAK5ZXiLWuA077.jpg


其實很簡單,不用過多解釋,一點點註釋就夠了。


Java代碼:

package com.example.graphicunlock;
import android.os.Bundle;
import android.os.Handler;
import android.app.Activity;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Bitmap.Config;
import android.graphics.Paint.Style;
import android.graphics.Path;
import android.graphics.PointF;
import android.graphics.PorterDuff.Mode;
import android.graphics.PorterDuffXfermode;
import android.util.DisplayMetrics;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.view.ViewGroup.LayoutParams;
import android.view.Window;
import android.view.WindowManager;
import android.widget.ImageView;
import android.widget.RelativeLayout;
public class MainActivity extends Activity implements OnTouchListener {
    private RelativeLayout relativeLayout;// 用來擺放九個圓形
    private ImageView view;// 用來繪製解鎖路徑
    private Path path;// 劃過的路徑
    private Paint paint;
    private Canvas canvas;
    private Dot[] array = new Dot[9];// 圓形的數組
    private Dot lastDot;// 上一個經過的點
    private Bitmap bitmap;// 繪製用的bitmap
    private boolean drawing = false;// 是否正在畫圖
    private int radius = 0;// 圓形半徑
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // 鎖定豎屏
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
        // 不顯示標題欄
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        // 全屏
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
                WindowManager.LayoutParams.FLAG_FULLSCREEN);
        setContentView(R.layout.activity_main);
        relativeLayout = (RelativeLayout) findViewById(R.id.rela);
        view = (ImageView) findViewById(R.id.view);
        view.setOnTouchListener(this);
        drawDots();
    }
    /**
     * 放置九個圓形 將九個圓形在屏幕中居中放置,每屏幕的三分之一寬度爲一格,橫豎排各三個,每個圓寬度是屏幕寬度的1/6
     */
    protected void drawDots() {
        int TopMars = (getScreenHeight() - getScreenWidth()) / 2;
        radius = getScreenWidth() / 12;
        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 3; j++) {
                RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(
                        radius * 2, radius * 2);
                params.leftMargin = (int) (radius * 4 * (j + 0.25));
                params.topMargin = (int) (TopMars + radius * 4 * (i + 0.25));
                // 新建半徑爲radius的圓形
                Dot dot = new Dot(this, radius);
                array[i * 3 + j] = dot;
                relativeLayout.addView(dot, params);
            }
        }
    }
    /**
     * 檢查pointF是否在某個圓形範圍內
     *
     * @param point
     *            要檢查的點
     * @return 如果確實在某個圓形範圍內,則返回該圓形,反之返回null
     */
    private Dot hitValidDot(PointF point) {
        for (int i = 0; i < array.length; i++) {
            Dot dot = array[i];
            if (!dot.getPassed()) {
                int[] location = { 0, 0 };
                dot.getLocationOnScreen(location);
                if (Math.sqrt((point.x - location[0] - radius)
                        * (point.x - location[0] - radius)
                        + (point.y - location[1] - radius)
                        * (point.y - location[1] - radius)) < radius) {
                    return dot;
                }
            }
        }
        return null;
    }
    /**
     * 要繪製到的目標圖片上的觸摸事件 本方法裏view.invalidate()並不是必須的,有沒有一樣……
     */
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            // 檢查手機按下的點是否在某個圓形內,如果是則以此圓形爲起點開始繪製圖形
            PointF point = new PointF(event.getRawX(), event.getRawY());
            Dot dot = hitValidDot(point);
            if (dot != null) {
                // 開始繪製 先實例化要繪製的bitmap canvas paint 和繪製的路徑path
                bitmap = Bitmap.createBitmap(getWindowWidth(),
                        getWindowHeight(), Config.ARGB_8888);
                canvas = new Canvas(bitmap);
                paint = new Paint();
                path = new Path();
                // 獲取此圓形中心點的位置
                RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) dot
                        .getLayoutParams();
                PointF startPoint = new PointF(params.leftMargin + radius,
                        params.topMargin + radius);
                // 將loasDot賦值給dot,並將dot設置爲經過狀態
                lastDot = dot;
                lastDot.drawPassed();
                // 將圓形的中心點設置爲路徑的起點 並設置要繪製路徑的顏色的寬度
                path.moveTo(startPoint.x, startPoint.y);
                paint.setARGB(255, 0, 0, 255);
                paint.setStrokeWidth(8);
                paint.setStyle(Style.STROKE);
                // 繪製到屏幕
                view.setImageBitmap(bitmap);
                // 標記爲正在繪圖中
                drawing = true;
            }
            break;
        case MotionEvent.ACTION_MOVE:
            if (drawing) {
                // 先清空圖片 否則看到的是每次繪製的疊加效果
                clear();
                // 同MotionEvent.ACTION_DOWN中一樣 檢查是否經過了某一點
                PointF point2 = new PointF(event.getRawX(), event.getRawY());
                Dot dot2 = hitValidDot(point2);
                if (dot2 != null) {
                    // 不過有時候兩點之間可能會有第三個點,如果第三個點爲非經過狀態,則將此點設置爲經過狀態
                    Dot dotBetween = checkDotBetween(lastDot, dot2);
                    if (dotBetween != null) {
                        lastDot = dotBetween;
                        lastDot.drawPassed();
                        RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) dot2
                                .getLayoutParams();
                        path.lineTo(params.leftMargin + radius,
                                params.topMargin + radius);
                    }
                    lastDot = dot2;
                    lastDot.drawPassed();
                    RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) dot2
                            .getLayoutParams();
                    path.lineTo(params.leftMargin + radius, params.topMargin
                            + radius);
                }
                // 繪製出經過的所有點的路徑
                canvas.drawPath(path, paint);
                // 繪製出上一個點到手指觸摸的位置的路徑
                RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) lastDot
                        .getLayoutParams();
                canvas.drawLine(params.leftMargin + radius, params.topMargin
                        + radius, event.getX(), event.getY(), paint);
                view.invalidate();
            }
            break;
        case MotionEvent.ACTION_UP:
            if (drawing) {
                // 手指擡起後,清空並重新繪製所有經過的點的路徑,這樣就會清除上一個點到手指觸摸的位置的路徑了
                clear();
                canvas.drawPath(path, paint);
                view.invalidate();
                // 繪製完畢,將繪製狀態改爲false
                drawing = false;
                // 三秒種後重置,放在這僅僅是爲了測試重置功能
                new Handler().postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        clearAllDrawing();
                    }
                }, 3000);
            }
            break;
        default:
            break;
        }
        return true;
    }
    /**
     * 重置所有爲初始狀態
     */
    protected void clearAllDrawing() {
        clear();
        for (int i = 0; i < array.length; i++) {
            Dot dot = array[i];
            if (dot != null) {
                dot.drawNormal();
            }
        }
        drawing = false;
    }
    /**
     * 查檢兩點之間是否經過第三點,如果是則返回第三點,否則返回null
     */
    protected Dot checkDotBetween(Dot dot1, Dot dot2) {
        int[] loc1 = { 0, 0 };
        int[] loc2 = { 0, 0 };
        dot1.getLocationOnScreen(loc1);
        dot2.getLocationOnScreen(loc2);
        // 兩點之間的中點
        PointF pointF = new PointF((loc1[0] + loc2[0]) / 2 + radius,
                (loc1[1] + loc2[1]) / 2 + radius);
        return hitValidDot(pointF);
    }
    /**
     * 清空畫面
     */
    protected void clear() {
        if (canvas != null && paint != null) {
            paint.setXfermode(new PorterDuffXfermode(Mode.CLEAR));
            canvas.drawPaint(paint);
            paint.setXfermode(new PorterDuffXfermode(Mode.SRC));
            view.invalidate();
        }
    }
    /**
     * @return 屏幕寬度
     */
    public int getScreenWidth() {
        DisplayMetrics metrics = new DisplayMetrics();
        getWindowManager().getDefaultDisplay().getMetrics(metrics);
        return metrics.widthPixels;
    }
    /**
     * @return 屏幕高度
     */
    public int getScreenHeight() {
        DisplayMetrics metrics = new DisplayMetrics();
        getWindowManager().getDefaultDisplay().getMetrics(metrics);
        return metrics.heightPixels;
    }
    /**
     * @return 返回窗口內容的寬度,不包括通知欄的標題欄,其實跟getScreenWidth()一樣
     */
    public int getWindowWidth() {
        return getWindow().findViewById(Window.ID_ANDROID_CONTENT).getWidth();
    }
    /**
     * @return 返回窗口內容的高度,不包括通知欄的標題欄,但是在這裏是全屏,所以與getScreenHeight()返回的其實是一致的
     */
    public int getWindowHeight() {
        return getWindow().findViewById(Window.ID_ANDROID_CONTENT).getHeight();
    }
    /**
     * 圓形
     */
    public class Dot extends ImageView {
        private int dotradius = 0;// 圓形半徑
        private boolean passed = false;// 是否經過的狀態
        public Dot(Context context) {
            super(context);
        }
        public Dot(Context context, int rad) {
            super(context);
            dotradius = rad;
            setLayoutParams(new LayoutParams(dotradius * 2, dotradius * 2));
            drawNormal();
        }
        /**
         * 繪製未經過時的狀態
         */
        public void drawNormal() {
            passed = false;
            Bitmap bm = Bitmap.createBitmap(dotradius * 2, dotradius * 2,
                    Config.ARGB_8888);
            Paint paint = new Paint();
            Canvas canvas = new Canvas(bm);
            paint.setAntiAlias(true);
            paint.setARGB(255, 156, 156, 156);
            paint.setStyle(Style.STROKE);
            paint.setStrokeWidth(5);
            canvas.drawCircle(dotradius, dotradius,
                    dotradius - paint.getStrokeWidth(), paint);
            paint.setStrokeWidth(1);
            paint.setStyle(Style.FILL_AND_STROKE);
            canvas.drawCircle(dotradius, dotradius, 3, paint);
            setImageBitmap(bm);
        }
        /**
         * 繪製經過時的狀態
         */
        public void drawPassed() {
            passed = true;
            Bitmap bm = Bitmap.createBitmap(dotradius * 2, dotradius * 2,
                    Config.ARGB_8888);
            Paint paint = new Paint();
            Canvas canvas = new Canvas(bm);
            paint.setAntiAlias(true);
            paint.setARGB(255, 0, 0, 255);
            paint.setStyle(Style.STROKE);
            paint.setStrokeWidth(5);
            canvas.drawCircle(dotradius, dotradius,
                    dotradius - paint.getStrokeWidth(), paint);
            paint.setStyle(Style.FILL_AND_STROKE);
            canvas.drawCircle(dotradius, dotradius, dotradius / 3, paint);
            setImageBitmap(bm);
        }
        public boolean getPassed() {
            return passed;
        }
    }
}


佈局xml代碼,很簡單:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity" >
    <RelativeLayout
        android:id="@+id/rela"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
    <ImageView
        android:id="@+id/view"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</RelativeLayout>


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章