本文介紹一種使用Rxjava實現圖片交互操做的方法。支持單指拖動,雙指旋轉縮放,效果以下: java
首先自定義TrsImageView
繼承ImageView
,設置ScaleType
爲Matrix
,咱們使用矩陣計算最終的translate, rotate和scale。git
public class TrsImageView extends ImageView {
public TrsImageView(Context context) {
super(context);
init();
}
private void init() {
Matrix matrix = new Matrix();
setScaleType(ScaleType.MATRIX);
setImageMatrix(matrix);
}
}
複製代碼
用create
方法建立Observable
,只考慮單指和雙指的狀況,用share
操做符使Observable
能夠被屢次訂閱。自定義Event
類,保存觸摸事件id和位置。github
private void init() {
...
Observable<Event> touchStream = Observable.create((ObservableEmitter<Event> emitter) -> {
setOnTouchListener((v, event) -> {
int pointerCount = event.getPointerCount();
if (pointerCount == 1) {
Event e = new Event();
e.action = event.getActionMasked();
e.p1 = new Vector(event.getX(), event.getY());
emitter.onNext(e);
} else if (pointerCount == 2) {
Event e = new Event();
e.action = event.getActionMasked();
e.p1 = new Vector(event.getX(0), event.getY(0));
e.p2 = new Vector(event.getX(1), event.getY(1));
emitter.onNext(e);
}
return true;
});
}).share();
}
複製代碼
filter
操做符獲取不一樣觸摸事件的Observableprivate void init() {
...
Observable<Event> pointer1Down = touchStream.filter(e -> e.action == MotionEvent.ACTION_DOWN);
Observable<Event> pointer2Down = touchStream.filter(e -> e.action == MotionEvent.ACTION_POINTER_DOWN);
Observable<Event> pointerMove = touchStream.filter(e -> e.action == MotionEvent.ACTION_MOVE);
Observable<Event> pointer2Up = touchStream.filter(e -> e.action == MotionEvent.ACTION_POINTER_UP);
Observable<Event> pointer1Up = touchStream.filter(e -> e.action == MotionEvent.ACTION_UP);
}
複製代碼
首先考慮單指拖動的操做流程:bash
手指按下 -> 手指移動 -> 手指擡起
複製代碼
咱們用兩次相鄰的手指移動
的位移去移動圖片,計算方法以下:post
Observable<Vector> delta1 = Observable.combineLatest(pointerMove, pointerMove.skip(1), (prev, cur) -> prev.p1.subtract(cur.p1));
複製代碼
完整流程代碼以下:spa
pointer1Down
.flatMap(e -> delta1.takeUntil(pointer1Up))
.subscribe(v -> {
matrix.postTranslate(v.x, v.y);
setImageMatrix(matrix);
});
複製代碼
再來考慮雙指操做流程:code
第二個手指按下 -> 手指移動 -> 第二個手指擡起
複製代碼
一樣,咱們用兩次相鄰的手指移動
計算圖片的位移、旋轉和縮放,定義類Delta
保存這些值cdn
Observable<Delta> delta2 = Observable.combineLatest(pointerMove, pointerMove.skip(1), (prev, cur) -> {
Delta delta = new Delta();
delta.center = cur.center();
delta.translate = prev.center().subtract(cur.center());
delta.scale = prev.length() / cur.length();
delta.rotate = cur.vector().angle(prev.vector());
return delta;
});
複製代碼
完整流程代碼以下:blog
pointer2Down
.flatMap(e -> delta2.takeUntil(pointer2Up))
.subscribe(d -> {
matrix.postTranslate(d.translate.x, d.translate.y);
matrix.postRotate(d.rotate, d.center.x, d.center.y);
matrix.postScale(d.scale, d.scale, d.center.x, d.center.y);
setImageMatrix(matrix);
});
複製代碼
第二個手指按下的時候,單指拖動流程應該中止,第二個手指擡起的時候,單指拖動流程應該從新開始。因此咱們須要修改單指拖動流程的實現:繼承
pointer1Down
.mergeWith(pointer2Up)
.flatMap(e -> delta1.takeUntil(pointer1Up).takeUntil(pointer2Down))
.subscribe(v -> {
matrix.postTranslate(v.x, v.y);
setImageMatrix(matrix);
});
複製代碼
完整代碼見這裏。