我们已经实现了图片的缩放以及图片的归位和动画,下面我们可以尝试加入裁剪框
数据层控制裁剪框的移动我参考了网上的开源项目,个人认为方法比较巧妙,但是使用的方法可能乍一看让人有点摸不着头脑,借此机会我也来学习和解析一下。
一般来说,裁剪框具有八个可操作的区域:四个角和四个边
enum Anchor {
LEFT(1),
RIGHT(2),
TOP(4),
BOTTOM(8),
LEFT_TOP(5),
RIGHT_TOP(6),
LEFT_BOTTOM(9),
RIGHT_BOTTOM(10);
int v;
static final int[] PN = {1, -1};
Anchor(int v) {
this.v = v;
}
public void move(RectF win, RectF frame, float dx, float dy) {
float[] maxFrame = cohesion(win, CLIP_MARGIN);
float[] minFrame = cohesion(frame, CLIP_FRAME_MIN);
float[] theFrame = cohesion(frame, 0);
float[] dxy = {dx, 0, dy};
for (int i = 0; i < 4; i++) {
if (((1 << i) & v) != 0) {
int pn = PN[i & 1];
theFrame[i] = pn * getRealValue(pn * (theFrame[i] + dxy[i & 2]),
pn * maxFrame[i], pn * minFrame[i + PN[i & 1]]);
}
}
frame.set(theFrame[0], theFrame[2], theFrame[1], theFrame[3]);
}
public static float getRealValue(float v, float min, float max) {
return Math.min(Math.max(v, min), max);
}
public static float[] cohesion(RectF win, float v) {
return new float[]{
win.left + v, win.right - v,
win.top + v, win.bottom - v
};
}
public static boolean isCohesionContains(RectF frame, float v, float x, float y) {
return frame.left + v < x && frame.right - v > x
&& frame.top + v < y && frame.bottom - v > y;
}
public static Anchor valueOf(int v) {
Anchor[] values = values();
for (Anchor anchor : values) {
if (anchor.v == v) {
return anchor;
}
}
return null;
}
}
我们重点分析一下这个枚举类,我们可以看到主要的方法是move方法,接收四个参数,前两个矩阵分别是最大窗口和当前窗口,通过getFrame方法得到四个边界,分别是最大边界,最小边界和当前边界。
float[] dxy = {dx, 0, dy};
for (int i = 0; i < 4; i++) {
if (((1 << i) & v) != 0) {
int pn = PN[i & 1];
theFrame[i] = pn * revise(pn * (theFrame[i] + dxy[i & 2]),
pn * maxFrame[i], pn * minFrame[i + PN[i & 1]]);
}
}
frame.set(theFrame[0], theFrame[2], theFrame[1], theFrame[3]);
在这一堆诡异的位运算之前,我们观察一下枚举类的值有没有什么奥秘
LEFT(1),//1
RIGHT(2),//10
TOP(4),//100
BOTTOM(8),//1000
LEFT_TOP(5),//101
RIGHT_TOP(6),//110
LEFT_BOTTOM(9),//1001
RIGHT_BOTTOM(10);//1010
这里为什么不用1 2 3 4 5 6 7 8而是1 2 4 8 5 6 9 10呢
我们看一下二进制表示,可以发现left---第0位是1 bottom----第3位是1
那么left_bottom----第0位和第3位是1
这里就是很巧妙的地方,为我们节省了很多的if--else判断。
((1 << i) & v) != 0
这里我们就得到了v(当前锚点的值)的第i位是不是1,从而判断计算新的rectF需要进行哪些计算。
static final int[] PN = {1, -1};
int pn = PN[i & 1];
这里我们看到使用i & 1来计算位移的正负
即左和上相关锚点pn为正 右和下相关锚点pn为负
我们以右下角向上移动为例进行分析
首先right_bottom第一位和第三位为1 也就是在i=1和i=3时会进入计算
pn = -1
这里pn的作用其实就是不用再写分支进行判断,因为对于top 大于最大边框是合法值,而对于bottom,小于最大边框是合法值。
这几行代码很精简,可能乍一看可读性不如一堆if else,但是真的能让我们从中学到许多。
现在关于裁剪框RectF的更新已经完成了,接下来就是根据矩阵进行裁剪框的绘制啦!
我们明天再见啦~
项目github地址:https://github.com/lqy20160609/ImageClip