专栏首页Android技术分享骚操作:使用RxJava实现ImageView的拖动、旋转和缩放

骚操作:使用RxJava实现ImageView的拖动、旋转和缩放

本文介绍一种使用Rxjava实现图片交互操作的方法。支持单指拖动,双指旋转缩放,效果如下:

自定义View

首先自定义TrsImageView继承ImageView,设置ScaleTypeMatrix,我们使用矩阵计算最终的translate, rotate和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);
    }
}

创建touch事件Observable

create方法创建Observable,只考虑单指和双指的情况,用share操作符使Observable可以被多次订阅。自定义Event类,保存触摸事件id和位置。

    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操作符获取不同触摸事件的Observable

    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);
    }

计算位移、旋转和缩放

首先考虑单指拖动的操作流程:

手指按下 -> 手指移动 -> 手指抬起

我们用两次相邻的手指移动的位移去移动图片,计算方法如下:

Observable<Vector> delta1 = Observable.combineLatest(pointerMove, pointerMove.skip(1), (prev, cur) -> prev.p1.subtract(cur.p1));

完整流程代码如下:

pointer1Down
        .flatMap(e -> delta1.takeUntil(pointer1Up))
        .subscribe(v -> {
            matrix.postTranslate(v.x, v.y);
            setImageMatrix(matrix);
        });

再来考虑双指操作流程:

第二个手指按下 -> 手指移动 -> 第二个手指抬起

同样,我们用两次相邻的手指移动计算图片的位移、旋转和缩放,定义类Delta保存这些值

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;
});

完整流程代码如下:

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);
        });

完整代码见这里

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Android布局优化:include 、merge、ViewStub的详细总结

    在开发Android布局时,我们常将一些通用的视图提取到一个单独的layout文件中,然后使用<include>标签在需要使用的其他layout布局文件中加载进...

    Android技术干货分享
  • 【工作经验分享】:在滴滴和头条干了 2 年开发,这些太真实…

    先简单交代一下背景吧,某不知名985的本硕,17年毕业加入滴滴,今年下半年跳槽到了头条,一直从事后端研发相关的工作。

    Android技术干货分享
  • 性能优化(一)APP 启动优化(不敢说秒开,但是最终优化完真不到 1s)

    这里也许会有人问 APP 启动还需要优化吗?启动又不是我们自己写的代码,难道 Google 工程师会犯这么低级的错吗?其实这还真不是 Google 的错,应该说...

    Android技术干货分享
  • 【数据分析】商品新零售行业——客户价值分析驾驶舱(附详细操作) | 留言送书

    大家可以叫我黄同学(博客名:Huang Supreme),一个应用统计硕士,爱好写一些技术博客,志在用通俗易懂的写作风格,帮助大家学到知识,学好知识!

    磐创AI
  • python做RFM代码太复杂,使用Tableau多简单!

    本文旨在通过2015-2018的客户订单分析,了解各大区销售经营情况、不同偏好,并通过RFM模型来进行客户价值分类,实现定向营销。

    朱小五
  • Linux编程--指针的++操作

    在学习ELF文件的过程中,看到Matrix中有类似于如下的代码来遍历ELF文件的e->phdr程序头表的内容。

    None_Ling
  • Activity初入门,创建和配置如此简单

    Activity是Android应用的重要组成单元之一,也是Android应用最常见的组件之一。前面看到的示例通常都只包含一个Activity或一个Ap...

    分享达人秀
  • 信号的基本概念

    有些出传感器的电压值非常小,例如麦克风产生的电压量级为10-6V,在记录信号或者重构信号时,会将信号线性放大的一个合适的值,通常用dB表示这个值。

    FPGA开源工作室
  • Android 属性 allowBackup

    最近用的一个库有allowBackup=true属性, 我的app里有allowBackup=false属性, IDE报错:

    望天
  • IBM WebSphere Application Liberty Profile苗条瘦身之道初探及剖析 博客分类: JavaJava IBM WebSph

    IBM WebSphere Application Server 向来以重量级而著称,而大量抛弃EJB这大巨头后,无论商用还是开源的应用服务器都走上轻量化的轨道...

    阿敏总司令

扫码关注云+社区

领取腾讯云代金券