zoukankan      html  css  js  c++  java
  • Android九宫格解锁自定义控件(附源码)

    九宫格解锁大家应该经常用到,比如支付宝里,就用到这个。我借鉴了一些大神的作品,并在基础在进行了一些优化修改封装,做到了高内聚低耦合,使用也极其简单方便。先放上几张截图吧(包括图片也直接借用大神的)

    这个控件的宽度和高度相等,都为屏幕的宽度。因此,这个控件可以根据需要调整所在屏幕的位置。也可以在布局文件中设置控件的宽度,结果就是这个控件是一个正方形,并且限制在正方形内做手势动作。我们追求的就是简单实用方便。好了现在我贴下代码

    控件SudokoView .java

    package com.fay.sudokounlock;
    
    import android.content.Context;
    import android.graphics.Bitmap;
    import android.graphics.BitmapFactory;
    import android.graphics.Canvas;
    import android.graphics.Color;
    import android.graphics.Paint;
    import android.graphics.Paint.Cap;
    import android.util.AttributeSet;
    import android.util.Log;
    import android.view.MotionEvent;
    import android.view.View;
    /**
     * {@link email:1940125001@qq.com}
     * @author Fay
     * @since 2014/5/23
     */
    public class SudokoView extends View {
    	private String TAG = "SudokoView";
    	
    	//width for this SudokoView
    	private int width ;
    	
    	//height for this SudokoView,width = height
    	private int height ;
    	
    	//spacing between two points
    	private int spacing = 0;
    	
    	private int startX = 0;
    	private int startY = 0;
    	
    	//current x-Location when move
    	private int moveX = 0;
    	
    	//current y-Location when move
    	private int moveY = 0;
    	
    	//check whether the any point is selected
    	private boolean hasSelected = false;
    	
    	//outer paint for the line
    	private Paint outerPaint = null;
    	
    	//inner paint for the line
    	private Paint innerPaint = null;
    	
    	//the String of lock
    	private StringBuilder lockString = new StringBuilder();
    	
    	//default bitmap for the point
    	private Bitmap defaultBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.lock_default);
    	
    	//selected bitmap for the point
    	private Bitmap selectedBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.lock_selected);
        
    	//the radius for the default bitmap
    	private int defaultRadius = defaultBitmap.getWidth() / 2;
    	
    	//the radius for the selected bitmap
    	private int selectedRadius = selectedBitmap.getWidth() / 2;
    	
    	//start point for touching;
    	//this will only appears when the ACTION_DOWN in the area of nine points.
    	private PointInfo startPoint = null;
    	
    	//nine point for this SudokoView
    	private PointInfo ninePoints[] = new PointInfo[9];
    	
    	private OnLockFinishListener mOnLockFinishListener = null;
    	
    	public SudokoView(Context context, AttributeSet attrs, int defStyleAttr) {
    		super(context, attrs, defStyleAttr);
    	}
    
    	public SudokoView(Context context, AttributeSet attrs) {
    		super(context, attrs);
    	}
    
    	public SudokoView(Context context) {
    		super(context);
    	}
    	
    	@Override
    	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    		super.onMeasure(widthMeasureSpec, widthMeasureSpec);
    	}
    
    	@Override
    	protected void onLayout(boolean changed, int left, int top, int right,
    			int bottom) {
    		width = getWidth();
    		height = getWidth();
    		spacing = (width - selectedRadius * 2 * 3) / 4;
    		initPoints();
    		initPaint();
    		super.onLayout(changed, left, top, right, bottom);
    	}
    	
    	@Override
    	protected void onDraw(Canvas canvas) {
    		if (startX > 0 && startY > 0 && moveX > 0 && moveY > 0) {
    			canvas.drawLine(startX, startY, moveX, moveY, outerPaint);
    		}
    		drawNinePoint(canvas);
    		super.onDraw(canvas);
    	}
    	
    	
    	@Override
    	public boolean onTouchEvent(MotionEvent event) {
    		switch (event.getAction()) {
    		case MotionEvent.ACTION_DOWN:
    			if (hasSelected) {//if the points has been selected previously, clear all
    				initPoints();
    				startPoint = null;
    				hasSelected = false;
    				lockString.delete(0, lockString.length());
    				invalidate();
    				return false;//must return false to end touch
    			}
    			int x = (int) event.getX();
    			int y = (int) event.getY();
    			for (PointInfo pointInfo : ninePoints) {
    				if (pointInfo.isInPoint(x, y)) {
    					pointInfo.setSelected(true);
    					startPoint = pointInfo;
    					startX = pointInfo.getCenterX();
    					startY = pointInfo.getCenterY();
    					lockString.append(pointInfo.getNumber());
    					hasSelected = true;
    				}
    			}
    			break;
    		case MotionEvent.ACTION_MOVE:
    			Log.v(TAG, "ACTION_MOVE");
    			moveX = (int) event.getX();
    			moveY = (int) event.getY();
    			for (PointInfo pointInfo : ninePoints) {
    				if (!pointInfo.isSelected() && pointInfo.isInPoint(moveX, moveY)) {
    					startX = pointInfo.getCenterX();
    					startY = pointInfo.getCenterY();
    					int length = lockString.length();
    					if (length > 0) {
    						int previousNumber = lockString.charAt(length - 1) - 48; 
    						ninePoints[pointInfo.getNumber()].setSelected(true);
    						ninePoints[previousNumber].setNextNumber(pointInfo.getNumber());
    					}
    					hasSelected = true;
    					lockString.append(pointInfo.getNumber());
    				}
    			}
    			break;
    		case MotionEvent.ACTION_UP:
    			if (lockString.length() > 0) {
    				mOnLockFinishListener.finish(lockString);
    			}
    			startX = 0;
    			startY = 0;
    			moveX = 0;
    			moveY = 0;
    			break;
    		}
    		invalidate();
    		return true;
    	}
    
    	/**
    	 * draw the nine points for this SudokoView including default bitmap and selected bitmap
    	 * @param Canvas canvas
    	 */
    	private void drawNinePoint(Canvas canvas) {
    		if (null != startPoint) {
    			drawLine(canvas, startPoint);
    		}
    		for (PointInfo mPointInfo : ninePoints) {
    			//Firstly, if the point is selected, draw the selected bitmap on this point
    			if (mPointInfo.isSelected()) {
    				canvas.drawBitmap(selectedBitmap, mPointInfo.getSelectedX(), mPointInfo.getSelectedY(), null);
    			}
    			//Then, draw the default bitmap on this point
    			canvas.drawBitmap(defaultBitmap, mPointInfo.getDefaultX(), mPointInfo.getDefaultY(), null);
    		}
    	}
    	
    	/**
    	 * draw a line between two points
    	 * @param Canvas canvas
    	 * @param PointInfo mPointInfo
    	 */
    	private void drawLine(Canvas canvas, PointInfo mPointInfo) {
    		while (mPointInfo.isHasNextPoint()) {
    			int index = mPointInfo.getNextNumber();
    			canvas.drawLine(mPointInfo.getCenterX(), mPointInfo.getCenterY(), ninePoints[index].getCenterX(), ninePoints[index].getCenterY(), outerPaint);
    			canvas.drawLine(mPointInfo.getCenterX(), mPointInfo.getCenterY(), ninePoints[index].getCenterX(), ninePoints[index].getCenterY(), innerPaint);
    			mPointInfo = ninePoints[index];
    		}
    	}
    	
    	/**
    	 * initialize the paint 
    	 */
    	private void initPaint() {
    		outerPaint = new Paint();
    		outerPaint.setColor(Color.GRAY);
    		outerPaint.setStrokeWidth(defaultBitmap.getWidth());
    		outerPaint.setAntiAlias(true);
    		outerPaint.setStrokeCap(Cap.ROUND);
    		
    		
    		innerPaint = new Paint();
    		innerPaint.setColor(Color.WHITE);
    		innerPaint.setStrokeWidth(defaultBitmap.getWidth() - 5);
    		innerPaint.setAntiAlias(true);
    		innerPaint.setStrokeCap(Cap.ROUND);
    	}
    	
    	
    	/**
    	 * initialize basic nine points
    	 */
    	private void initPoints() {
    		int selectedX = spacing;
    		int selectedY = spacing;
    		int defaultX = spacing + selectedRadius - defaultRadius;
    		int defaultY = spacing + selectedRadius - defaultRadius;
    		PointInfo mPointInfo = null;
    		for (int index = 0; index < 9; index ++) {
    			if  (index == 3 || index == 6) {
    				selectedX = spacing;
    				selectedY += selectedRadius * 2 + spacing;
    				defaultX = spacing + selectedRadius - defaultRadius;
    				defaultY += selectedRadius * 2 + spacing;
    			} else {
    				if (index != 0) {
    					selectedX += selectedRadius * 2 + spacing;
    					//selectedY = selectedY;
    					defaultX += selectedRadius * 2 + spacing;
    					//defaultY = defaultY;
    				}
    			}
    			mPointInfo = new PointInfo(defaultX, defaultY, selectedX, selectedY, defaultRadius, selectedRadius, index, index);
    			mPointInfo.setSelected(false);
    			mPointInfo.setNumber(index);
    			mPointInfo.setNextNumber(index);
    		    ninePoints[index] = mPointInfo;
    		}
    	}
    	
    	/**
    	 * Listener when the locking is finishing
    	 */
    	public interface OnLockFinishListener {
    		void finish(StringBuilder lockString) ;
    	}
    	
    	/**
    	 * set the Listener for the callback
    	 */
    	public void setOnLockFinishListener (OnLockFinishListener mOnLockFinishListener) {
    		this.mOnLockFinishListener = mOnLockFinishListener;
    	}
    	
    }
    

      每一个点单元的实体类PointInfo.java

    package com.fay.sudokounlock;
    
    import java.io.Serializable;
    /**
     * the basic data for the point
     * @author Fay
     * @since 2014/5/23
     */
    public class PointInfo implements Serializable{
        private static final long serialVersionUID = 1L;
        //default x-Location
    	private int defaultX;
    	
    	//default y-Location
    	private int defaultY;
    	
    	//selected x-Location
    	private int selectedX;
    	
    	//selected y-Location
    	private int selectedY;
    	
    	//the radius for the default bitmap
    	private int defaultRadius;
    	
    	//the radius for the selected bitmap
    	private int selectedRadius;
    	
    	//the number about this point
    	private int number;
    	
    	//the next point connection with this point
    	private int nextNumber;
    	
    	//whether the point is selected
    	private boolean isSelected;
    	
    	public PointInfo(int defaultX, int defaultY, int selectedX, int selectedY,
    			int defaultRadius, int selectedRadius, int number, int nextNumber) {
    		super();
    		this.defaultX = defaultX;
    		this.defaultY = defaultY;
    		this.selectedX = selectedX;
    		this.selectedY = selectedY;
    		this.defaultRadius = defaultRadius;
    		this.selectedRadius = selectedRadius;
    		this.number = number;
    		this.nextNumber = number;//
    	}
    	public int getDefaultX() {
    		return defaultX;
    	}
    	public void setDefaultX(int defaultX) {
    		this.defaultX = defaultX;
    	}
    	public int getDefaultY() {
    		return defaultY;
    	}
    	public void setDefaultY(int defaultY) {
    		this.defaultY = defaultY;
    	}
    	public int getSelectedX() {
    		return selectedX;
    	}
    	public void setSelectedX(int selectedX) {
    		this.selectedX = selectedX;
    	}
    	public int getSelectedY() {
    		return selectedY;
    	}
    	public void setSelectedY(int selectedY) {
    		this.selectedY = selectedY;
    	}
    	public int getDefaultRadius() {
    		return defaultRadius;
    	}
    	public void setDefaultRadius(int defaultRadius) {
    		this.defaultRadius = defaultRadius;
    	}
    	public int getSelectedRadius() {
    		return selectedRadius;
    	}
    	public void setSelectedRadius(int selectedRadius) {
    		this.selectedRadius = selectedRadius;
    	}
    	public int getNumber() {
    		return number;
    	}
    	public void setNumber(int number) {
    		this.number = number;
    	}
    	public int getNextNumber() {
    		return nextNumber;
    	}
    	public void setNextNumber(int nextNumber) {
    		this.nextNumber = nextNumber;
    	}
    	public boolean isSelected() {
    		return isSelected;
    	}
    	public void setSelected(boolean isSelected) {
    		this.isSelected = isSelected;
    	}
    	
    	/*
    	 * whether current point which touch is in the area of this point
    	 * @return boolean
    	 */
    	public boolean isInPoint(int x, int y) {
    		return  (selectedX <= x && x <= selectedX + 2 * selectedRadius 
    				&& selectedY <= y && y <= selectedY + 2 * selectedRadius);
    	}
    	
    	/**
    	 * whether current point the next connecting point
    	 * @return boolean
    	 */
    	public boolean isHasNextPoint() {
    		//if equals, return false, else return true
    		return (number == nextNumber) ? false : true; 
    	}
    	
    	/**
    	 * get the center x-Location of this point
    	 * @return Integer
    	 */
    	public int getCenterX() {
    		return selectedX + selectedRadius;
    	}
    	
    	/**
    	 * get the center y-Location of this point
    	 * @return
    	 */
    	public int getCenterY() {
    		return selectedY + selectedRadius;
    	}
    	
    	@Override
    	public String toString() {
    		return "PointInfo [defaultX=" + defaultX + ", defaultY=" + defaultY
    				+ ", selectedX=" + selectedX + ", selectedY=" + selectedY
    				+ ", defaultRadius=" + defaultRadius + ", selectedRadius="
    				+ selectedRadius + ", number=" + number + ", nextNumber="
    				+ nextNumber + ", isSelected=" + isSelected + "]";
    	}
    	
    }
    

      最后我们看下使用的MainActivity.java

    package com.fay.sudokounlock;
    
    import com.fay.sudokounlock.SudokoView.OnLockFinishListener;
    
    import android.app.Activity;
    import android.os.Bundle;
    import android.widget.Toast;
    
    public class MainActivity extends Activity {
        private SudokoView mSudokoView = null;
    	@Override
    	protected void onCreate(Bundle savedInstanceState) {
    		super.onCreate(savedInstanceState);
    		setContentView(R.layout.activity_main);
    		mSudokoView = (SudokoView) findViewById(R.id.sudoko);
    		mSudokoView.setOnLockFinishListener(new OnLockFinishListener() {
    			@Override
    			public void finish(StringBuilder lockString) {
    				Toast.makeText(getApplicationContext(), lockString, 2000).show();
    			}
    		});
    	}
    
    
    }
    

      然后看下布局文件

    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@android:color/white" >
        <com.fay.sudokounlock.SudokoView
            android:id="@+id/sudoko"
            android:layout_width="fill_parent"
            android:layout_height="0dp"
            android:layout_centerInParent="true" >
        </com.fay.sudokounlock.SudokoView>
    </RelativeLayout>
    

      相信到这里,各位可以分分钟熟悉使用这个控件。我们要的就是最大化简单、实用、规范。

         源码下载地址:https://files.cnblogs.com/yinweiliang/SudokoUnlock.rar

  • 相关阅读:
    【工具】sublime使用技巧
    怎样存钱利息最大及怎样买房付款最省钱问题
    存钱问题
    玛丽莲问题
    用线程做一个火车票购票系统(可以根据需要选择线程个数)
    系统编程拷贝文件或者目录(可以做出一个动态库哦)
    mysql优化-数据库设计基本原则
    项目 数据可视化1
    读书笔记2-三体
    python数据学习3 布林带
  • 原文地址:https://www.cnblogs.com/yinweiliang/p/3771044.html
Copyright © 2011-2022 走看看