This paper introduces a method of picture interaction using Rxjava.Supports single-finger dragging and double-finger rotating scaling as follows:
Custom View
First, the custom TrsImageView inherits the ImageView, sets the ScaleType to Matrix, and uses the matrix to calculate the final translate, rotate, and scale.
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 touch event Observable
Create Observable using the create method, considering only single- and double-fingered cases, and use the share operator to make Observable available for multiple subscriptions.Customize the Event class to save the touch event id and location.
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(); }
Observable using filter operator to get different touch events
private 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); }
Calculating displacement, rotation, and scaling
First consider the flow of single-finger dragging:
Finger Press->Finger Move->Finger Raise
We use the displacement of two adjacent fingers to move the picture, calculated as follows:
Observable<Vector> delta1 = Observable.combineLatest(pointerMove, pointerMove.skip(1), (prev, cur) -> prev.p1.subtract(cur.p1));
The complete process code is as follows:
pointer1Down .flatMap(e -> delta1.takeUntil(pointer1Up)) .subscribe(v -> { matrix.postTranslate(v.x, v.y); setImageMatrix(matrix); });
Consider the double finger process again:
Second finger press - > finger move - > Second finger lift
Similarly, we use two adjacent finger movements to calculate the displacement, rotation, and scaling of the picture, defining the class Delta to hold these values
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; });
The complete process code is as follows:
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); });
When the second finger is pressed, the single-finger dragging process should stop, and when the second finger is raised, the single-finger dragging process should start again.So we need to modify the implementation of the single-finger dragging process:
pointer1Down .mergeWith(pointer2Up) .flatMap(e -> delta1.takeUntil(pointer1Up).takeUntil(pointer2Down)) .subscribe(v -> { matrix.postTranslate(v.x, v.y); setImageMatrix(matrix); });
See full code Here.
Gold, silver and silver are almost gone. No partner has found a job yet to recommend an article: