您好,登錄后才能下訂單哦!
先上圖:
其實很簡單,不用過多解釋,一點點注釋就夠了。
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>
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。