Flex【原创】模拟Android图案解锁
Flex 实现Android图案解锁功能
看见Andorid系统里面有图案解锁的功能,试想能用Flex的移动开发实现吗?答案是:完全可以!
环境:Flex 4.6(air3.2)
先看我的包结构:
第一视图Sample_Locked2View:

<?xml version="1.0" encoding="utf-8"?> <!--TEST APPLICATION 's first view--> <s:View xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" title="主页视图" xmlns:view="com.yyf.view.*" actionBarVisible="false"> <s:layout> <s:VerticalLayout horizontalAlign="center" verticalAlign="middle"/> </s:layout> <fx:Script> <![CDATA[ import com.yyf.model.MainDataModel; import com.yyf.util.Tools; [Bindable]private var mainModel:MainDataModel = MainDataModel.getInstance(); ]]> </fx:Script> <view:LockedView id="lock" horizontalCenter="0" verticalCenter="0" creationComplete="mainModel.hotArea = (new Tools()).getHotArea(lock);"/> <s:Label id="notice" text="Your Password : {mainModel.password}" visible="{mainModel.successCreated}"/> <!--display the HOT_AREA--> <!-- <s:Label backgroundAlpha="0.3" backgroundColor="0xFED913" x="{mainModel.hotArea.min_x}" y="{mainModel.hotArea.min_y}" width="{mainModel.hotArea.max_x - mainModel.hotArea.min_x}" height="{mainModel.hotArea.max_y - mainModel.hotArea.min_y}"/>--> </s:View>
核心逻辑类Circle,用于处理绘图相关计算:

package com.yyf.element { import com.yyf.event.PasswordCreateEevent; import com.yyf.model.MainDataModel; import com.yyf.util.Config; import com.yyf.view.LockedView; import flash.display.BitmapData; import flash.display.Sprite; import flash.events.FocusEvent; import flash.events.MouseEvent; import flash.geom.Point; import mx.core.FlexGlobals; import mx.core.UIComponent; import mx.events.FlexEvent; import spark.components.Application; /** *<br>Circle UI</br> * @author yao yf * 2012-04-19 * */ public class Circle extends UIComponent { /**radius of the circle**/ public var radius:Number; /**color of the circle**/ public var color:uint; /**index ofd the circle**/ public var index:int = 0; /** * @private **/ //selected color private var selectedColor:uint; //current Circle target private var target:Circle; //line private var line:Line = Line.getInstance(); //temp line private var linetmp:LineTemp = LineTemp.getInstance(); //current start point for draw temp line private var currentStaPoint:Point = new Point(); //has excute the FUNCTION drawLine() private var hasLine:Boolean = false; //has excute the FUNCTION circleChange() private var hasCircleChanged:Boolean = false; //model data private var mainModel:MainDataModel = MainDataModel.getInstance(); //main application private static const MAIN_APP:Application = FlexGlobals.topLevelApplication as Application; /** * Constructor * @param radius:Number * @param color:uint * **/ public function Circle(radius:Number,color:uint) { super(); this.radius = radius; this.color = color; this.draw(); addListener(); mainModel.notice = Config.DEFUALT; } //draw private function draw():void{ this.graphics.clear(); this.graphics.beginFill(color); this.graphics.drawCircle(this.radius,this.radius,this.radius); } //add Event Listener private function addListener():void{ this.addEventListener(MouseEvent.MOUSE_DOWN,onMouseDown); } //onMouseDown Handler private function onMouseDown(event:MouseEvent):void { mainModel.notice = Config.DURING; target = event.target as Circle; circleChange(target); currentStaPoint = new Point(target.x + radius,target.y + radius); mainModel.dispatched = false; MAIN_APP.addEventListener(MouseEvent.MOUSE_MOVE,onMouseMove); MAIN_APP.addEventListener(MouseEvent.MOUSE_UP,onMouseUp); } //onMouseMove Handler private function onMouseMove(event:MouseEvent):void { target = event.target as Circle; if(target){ circleChange(target); currentStaPoint = new Point(target.x + radius,target.y + radius); }else{ if(event.target is Application)return; // var stagePoint:Point = new Point(event.stageX,event.stageY); // trace(stagePoint.x+" =,= "+stagePoint.y); drawTmpLine(target); } } //onMouseUp Handler private function onMouseUp(event:MouseEvent):void { MAIN_APP.removeEventListener(MouseEvent.MOUSE_MOVE,onMouseMove); MAIN_APP.removeEventListener(MouseEvent.MOUSE_UP,onMouseUp); mainModel.enable = false; linetmp.clear(); if(!mainModel.dispatched){ mainModel.dispatchEvent(new PasswordCreateEevent(PasswordCreateEevent.EVT_PASSWORD_CREATED,mainModel.password)); } mainModel.dispatched = true; } //circleChange //add a orange small circle on this center protected function circleChange(target:Circle):void{ if(!target)return; if(!target.hasCircleChanged){ target.graphics.beginFill(0xFF5D18); target.graphics.drawCircle(this.radius,this.radius,this.radius/2); if(!target.hasLine){ drawLine(target); } target.hasCircleChanged = true; } } /** * draw line path with mouse move * @param target:Circle * **/ protected function drawLine(target:Circle):void{ linetmp.clear(); line.graphics.lineStyle(Line.THINKNESS,Line.COLOR); var c_point:Point; if(target == this){ // c_point = localToGlobal(new Point(this.x,this.y)); line.graphics.moveTo(this.x + radius,this.y + radius); }else{ // c_point = localToGlobal(new Point(target.x,target.y)); line.graphics.lineTo(target.x + radius,target.y + radius); } target.hasLine = true; mainModel.password += target.index + ""; } /** * draw temp line path with mouse move * @param target:Circle * @param stagePoint:Point * **/ protected function drawTmpLine(target:Circle):void{ // var currentMovPoint:Point = localToGlobal(new Point(owner.mouseX,owner.mouseY)); // trace(currentMovPoint.x + " =,= " + currentMovPoint.y); // if(currentMovPoint.x > mainModel.hotArea.min_x && currentMovPoint.x < mainModel.hotArea.max_x // && currentMovPoint.y > mainModel.hotArea.min_y && currentMovPoint.y < mainModel.hotArea.max_y) // { // trace(owner.mouseX + " , " + owner.mouseY); // trace(owner.width + " , " + owner.height); if(owner.mouseX > 6 && owner.mouseX < (owner.width - 6) && owner.mouseY > 6 && owner.mouseY < (owner.height - 6)) { linetmp.clear(); linetmp.graphics.lineStyle(Line.THINKNESS,Line.COLOR); linetmp.graphics.moveTo(currentStaPoint.x , currentStaPoint.y); linetmp.graphics.lineTo(owner.mouseX,owner.mouseY); } } //get color String from ower pixel private function getColorString():String{ var tmpDta:BitmapData = new BitmapData(owner.width, owner.height); tmpDta.draw(owner); selectedColor = tmpDta.getPixel(owner.mouseX,owner.mouseY); var colorDisplay:String = selectedColor.toString(16).toLocaleUpperCase(); if(colorDisplay.length < 6){ switch(colorDisplay.length){ case 1 : colorDisplay = "00000" + colorDisplay;break; case 2 : colorDisplay = "0000" + colorDisplay;break; case 3 : colorDisplay = "000" + colorDisplay;break; case 4 : colorDisplay = "00" + colorDisplay;break; case 5 : colorDisplay = "0" + colorDisplay;break; default : break; } } return colorDisplay; } } }
线对象Line(SingleTon):

package com.yyf.element { import mx.core.UIComponent; /** *<br>Line UI</br> * SingleTon * @author yao yf * 2012-04-19 * */ public class Line extends UIComponent { private static var model:Line; public static const THINKNESS:Number = 25; public static const COLOR:uint = 0xFF5D18; public function clear():void{ this.graphics.clear(); } public function Line(singletonclass:SingleTonClass) { if(model != null){ throw new Error("SingleTon!"); } } public static function getInstance():Line{ if(model == null){ model = new Line(new SingleTonClass()); } return model; } } } class SingleTonClass{}
临时线对象LineTemp(SingleTon)此对象为绘制过程中的临时线路径,绘制完成后将被清除:

package com.yyf.element { import mx.core.UIComponent; /** *<br>Temp Line UI</br> * SingleTon * @author yao yf * 2012-04-19 * */ public class LineTemp extends UIComponent { private static var model:LineTemp; public static const THINKNESS:Number = 25; public static const COLOR:uint = 0xFF5D18; public function clear():void{ this.graphics.clear(); } public function LineTemp(singletonclass:SingleTonClass) { if(model != null){ throw new Error("SingleTon!"); } } public static function getInstance():LineTemp{ if(model == null){ model = new LineTemp(new SingleTonClass()); } return model; } } } class SingleTonClass{}
核心视图类LockedView,用于呈图案解锁图面板:

<?xml version="1.0" encoding="utf-8"?> <!-- Locked Component ========================== Explain:custom component for Locked ========================== Code by yyf 2012-04-19 --> <s:BorderContainer xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/mx" width="275" height="350" creationComplete="creationCompleteHandler(event)"> <fx:Script> <![CDATA[ import com.yyf.element.Circle; import com.yyf.element.Line; import com.yyf.element.LineTemp; import com.yyf.event.PasswordCreateEevent; import com.yyf.model.MainDataModel; import com.yyf.util.Config; import mx.collections.ArrayCollection; // import mx.controls.Alert; import mx.events.FlexEvent; /** * @prvate * **/ private const RADIUS:Number = 30; private const COLOR:uint = 0xFED913; //store the Circles private static var circleData:ArrayCollection; //line path private var line:com.yyf.element.Line = com.yyf.element.Line.getInstance(); //temp line path private var linetmp:LineTemp = LineTemp.getInstance(); //model data [Bindable]private var mainModel:MainDataModel = MainDataModel.getInstance(); /**init**/ private function creationCompleteHandler(event:FlexEvent):void { mainModel.addEventListener(PasswordCreateEevent.EVT_PASSWORD_CREATED,onCreated); displayCircle(); } /**display nine Circles**/ private function displayCircle():void{ circleData = new ArrayCollection(); line.includeInLayout = false; linetmp.includeInLayout = false; this.circles.addElement(line); this.circles.addElement(linetmp); for(var i:int = 0 ; i < 9 ; i++){ var c:Circle = new Circle(RADIUS,COLOR); c.index = (i+1); this.circles.addElement(c); circleData.addItem(c); } } /**remove all of the circles group's elements**/ private function removeCircle():void{ circleData.removeAll(); circleData.refresh(); circleData = null; var i:int = this.circles.numElements; while(i >= 1){ this.circles.removeElementAt(0); i--; } } /**RESET button handler**/ private function reset_clickHandler(event:MouseEvent):void { mainModel.successCreated = false; mainModel.enable = true; mainModel.password = ""; line.clear(); linetmp.clear(); removeCircle(); displayCircle(); } /**GO buton handler**/ private function go_clickHandler(event:MouseEvent):void { // Alert.show("Your Password : " + mainModel.password); trace("Your Password : " + mainModel.password); } //PasswordCreateEevent.EVT_PASSWORD_CREATED call back handler private function onCreated(event:PasswordCreateEevent):void { var pw:String = event.password; if(pw.length < 4){ mainModel.notice = Config.ERROR; mainModel.successCreated = false; trace("Your Password's lenth can not less than 4 !"); }else{ mainModel.notice = Config.SUCCESS; mainModel.successCreated = true; } } ]]> </fx:Script> <fx:Declarations> </fx:Declarations> <s:VGroup width="100%" height="100%" horizontalAlign="center" verticalAlign="middle" paddingBottom="5" paddingTop="5" paddingLeft="5" paddingRight="5"> <s:Label id="noticelbl" text="{mainModel.notice}"/> <s:Line xFrom="0" xTo="260"> <s:stroke> <s:SolidColorStroke color="{COLOR}" weight="2" caps="square"/> </s:stroke> </s:Line> <s:Group id="circles" width="100%" height="100%" enabled="{mainModel.enable}"> <s:layout> <s:TileLayout horizontalGap="90" verticalGap="90" requestedColumnCount="3" requestedRowCount="3" paddingLeft="10" paddingTop="10"/> </s:layout> </s:Group> <s:Line xFrom="0" xTo="260"> <s:stroke> <s:SolidColorStroke color="{COLOR}" weight="2" caps="square"/> </s:stroke> </s:Line> <s:HGroup height="35"> <s:Button id="reset" label="Reset" width="70" height="35" click="reset_clickHandler(event)"/> <s:Button id="go" label="Go" width="70" height="35" click="go_clickHandler(event)" enabled="{mainModel.successCreated}"/> </s:HGroup> </s:VGroup> </s:BorderContainer>
下面是效果图:
废话少说,本人一贯作风——崇尚开源,附源码下载地址:
https://files.cnblogs.com/loveFlex/Sample_Locked2.rar
欢迎大家留言~
作者: Binyy
出处: https://www.cnblogs.com/loveFlex
城市:wuhan
微信:momakeyy
详细源码请移步,记得点个星星噢~ https://gitee.com/binyylovesino/lilo-ui 欢迎各路大佬指导、提问~
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出 原文链接 如有问题, 可邮件(408460486@qq.com)或者微信咨询.
posted on 2012-04-24 09:58 Binyy_Wuhan 阅读(2378) 评论(4) 编辑 收藏 举报
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义