安卓自定义View进阶-Matrix详解

这应该是目前最详细的一篇讲解Matrix的中文文章了,在上一篇文章Matrix原理中,我们对Matrix做了一个简单的了解,偏向理论,在本文中则会详细的讲解Matrix的具体用法,以及与Matrix相关的一些实用技巧。

Matrix方法表

按照惯例,先放方法表做概览。

方法类别 相关API 摘要
基本方法 equals hashCode toString toShortString 比较、 获取哈希值、 转换为字符串
数值操作 set reset setValues getValues 设置、 重置、 设置数值、 获取数值
数值计算 mapPoints mapRadius mapRect mapVectors 计算变换后的数值
设置(set) setConcat setRotate setScale setSkew setTranslate 设置变换
前乘(pre) preConcat preRotate preScale preSkew preTranslate 前乘变换
后乘(post) postConcat postRotate postScale postSkew postTranslate 后乘变换
特殊方法 setPolyToPoly setRectToRect rectStaysRect setSinCos 一些特殊操作
矩阵相关 invert isAffine(API21) isIdentity 求逆矩阵、 是否为仿射矩阵、 是否为单位矩阵 …

Matrix方法详解

构造方法

构造方法没有在上面表格中列出。

无参构造
Matrix ()

创建一个全新的Matrix,使用格式如下:

Matrix matrix = new Matrix();

通过这种方式创建出来的并不是一个数值全部为空的矩阵,而是一个单位矩阵,如下:

有参构造
Matrix (Matrix src)

这种方法则需要一个已经存在的矩阵作为参数,使用格式如下:

Matrix matrix = new Matrix(src);

创建一个Matrix,并对src深拷贝(理解为新的matrix和src是两个对象,但内部数值相同即可)。

基本方法

基本方法内容比较简单,在此处简要介绍一下。

1.equals

比较两个Matrix的数值是否相同。

2.hashCode

获取Matrix的哈希值。

3.toString

将Matrix转换为字符串: Matrix{[1.0, 0.0, 0.0][0.0, 1.0, 0.0][0.0, 0.0, 1.0]}

4.toShortString

将Matrix转换为短字符串: [1.0, 0.0, 0.0][0.0, 1.0, 0.0][0.0, 0.0, 1.0]

数值操作

数值操作这一组方法可以帮助我们直接控制Matrix里面的数值。

1.set
void set (Matrix src)

没有返回值,有一个参数,作用是将参数Matrix的数值复制到当前Matrix中。如果参数为空,则重置当前Matrix,相当于reset()

2.reset
void reset ()

重置当前Matrix(将当前Matrix重置为单位矩阵)。

3.setValues
void setValues (float[] values)

setValues的参数是浮点型的一维数组,长度需要大于9,拷贝数组中的前9位数值赋值给当前Matrix。

4.getValues
void getValues (float[] values)

很显然,getValues和setValues是一对方法,参数也是浮点型的一维数组,长度需要大于9,将Matrix中的数值拷贝进参数的前9位中。

数值计算

1.mapPoints
void mapPoints (float[] pts)

void mapPoints (float[] dst, float[] src)

void mapPoints (float[] dst, int dstIndex,float[] src, int srcIndex, int pointCount)

计算一组点基于当前Matrix变换后的位置,(由于是计算点,所以参数中的float数组长度一般都是偶数的,若为奇数,则最后一个数值不参与计算)。

它有三个重载方法:

(1) void mapPoints (float[] pts) 方法仅有一个参数,pts数组作为参数传递原始数值,计算结果仍存放在pts中。

示例:

// 初始数据为三个点 (0, 0) (80, 100) (400, 300) 
float[] pts = new float[]{
  0, 0, 80, 100, 400, 300};

// 构造一个matrix,x坐标缩放0.5
Matrix matrix = new Matrix();
matrix.setScale(0.5f, 1f);

// 输出pts计算之前数据
Log.i(TAG, "before: "+ Arrays.toString(pts));

// 调用map方法计算
matrix.mapPoints(pts);

// 输出pts计算之后数据
Log.i(TAG, "after : "+ Arrays.toString(pts));

结果:

before: [0.0, 0.0, 80.0, 100.0, 400.0, 300.0]
after : [0.0, 0.0, 40.0, 100.0, 200.0, 300.0]

(2) void mapPoints (float[] dst, float[] src) ,src作为参数传递原始数值,计算结果存放在dst中,src不变。

如果原始数据需要保留则一般使用这种方法。

示例:

// 初始数据为三个点 (0, 0) (80, 100) (400, 300)
float[] src = new float[]{
  0, 0, 80, 100, 400, 300};
float[] dst = new float[6];

// 构造一个matrix,x坐标缩放0.5
Matrix matrix = new Matrix();
matrix.setScale(0.5f, 1f);

// 输出计算之前数据
Log.i(TAG, "before: src="+ Arrays.toString(src));
Log.i(TAG, "before: dst="+ Arrays.toString(dst));

// 调用map方法计算
matrix.mapPoints(dst,src);

// 输出计算之后数据
Log.i(TAG, "after : src="+ Arrays.toString(src));
Log.i(TAG, "after : dst="+ Arrays.toString(dst));

结果:

before: src=[0.0, 0.0, 80.0, 100.0, 400.0, 300.0]
before: dst=[0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
after : src=[0.0, 0.0, 80.0, 100.0, 400.0, 300.0]
after : dst=[0.0, 0.0, 40.0, 100.0, 
  • 10
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
好的,这是一个手势解锁的Demo,你可以参考一下: ``` import UIKit class GestureLockViewController: UIViewController { // MARK: - Properties var buttons = [UIButton]() var selectedButtons = [UIButton]() var lines = [CAShapeLayer]() var touchPoint: CGPoint? var isTouching = false // MARK: - Lifecycle override func viewDidLoad() { super.viewDidLoad() view.backgroundColor = .white let margin: CGFloat = 40 let distance: CGFloat = 80 let buttonWidth: CGFloat = 60 let buttonHeight: CGFloat = 60 let viewWidth = view.bounds.width let viewHeight = view.bounds.height for i in 0..<9 { let row = CGFloat(i / 3) let col = CGFloat(i % 3) let x = margin + col * (buttonWidth + distance) let y = margin + row * (buttonHeight + distance) let button = UIButton(frame: CGRect(x: x, y: y, width: buttonWidth, height: buttonHeight)) button.layer.cornerRadius = buttonWidth / 2 button.layer.borderWidth = 2 button.layer.borderColor = UIColor.lightGray.cgColor button.tag = i view.addSubview(button) buttons.append(button) } } // MARK: - Gesture Methods override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { guard let touch = touches.first else { return } let point = touch.location(in: view) for button in buttons { if button.frame.contains(point) && !selectedButtons.contains(button) { touchPoint = button.center selectedButtons.append(button) isTouching = true break } } } override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) { guard isTouching, let touch = touches.first else { return } let point = touch.location(in: view) touchPoint = point for button in buttons { if button.frame.contains(point) && !selectedButtons.contains(button) { button.isSelected = true selectedButtons.append(button) let line = CAShapeLayer() line.strokeColor = UIColor.gray.cgColor line.fillColor = UIColor.clear.cgColor line.lineWidth = 3 view.layer.addSublayer(line) lines.append(line) break } } drawLines() } override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) { isTouching = false touchPoint = nil for button in buttons { button.isSelected = false } validatePassword() clearSelectedButtons() clearLines() } override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) { isTouching = false touchPoint = nil for button in buttons { button.isSelected = false } clearSelectedButtons() clearLines() } // MARK: - Private Methods private func drawLines() { guard let point = touchPoint else { return } let linePath = UIBezierPath() linePath.move(to: point) for button in selectedButtons { linePath.addLine(to: button.center) } if let lastButton = selectedButtons.last, isTouching { linePath.addLine(to: lastButton.convert(lastButton.center, to: view)) } lines.last?.path = linePath.cgPath } private func clearSelectedButtons() { for button in selectedButtons { button.isSelected = false } selectedButtons.removeAll() } private func clearLines() { for line in lines { line.removeFromSuperlayer() } lines.removeAll() } private func validatePassword() { let password = selectedButtons.map { "\($0.tag)" }.joined() print("Gesture password: \(password)") } } ``` 这个Demo实现了一个3x3的手势解锁界面,使用了`UIButton`和`CAShapeLayer`来实现。当用户滑动手指时,会根据手指位置,自动连接之前选中的按钮,形成一条线。当用户抬起手指时,会根据选中的按钮的顺序,输出一个密码。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值