【Android】android镜像翻转

Android镜像翻转指的是将屏幕进行水平的翻转,达到所有内容显示都会反向的效果,就像是在镜子中看到的界面一样。这种应用的使用场景相对比较受限,主要用在一些需要使用Android手机界面进行镜面投影的地方,比如说车载手机hud导航之类的。

镜像翻转的效果如下:

    

镜像水平翻转前后效果

在没办法对硬件进行直接翻转的时候,只能从代码进行着手。最先想到的方法是一种比较弱的实现方案,就是对界面进行截图,然后对截图进行翻转,再让其替换掉原先的界面,这种方法是可行的,但是会出现很严重的内存问题,因为图片很耗内存,而且不利于动态界面的实现,比如控件会变动位置,控件内容会变化的情况。这就需要其他更靠谱的方案。

下面提供三种解决方案,能够解决一部分镜像翻转问题。

1.翻转动画

第一种方法是使用Android翻转动画进行实现。

该方法需要重写动画,实现翻转,并将该动画添加到布局中,之后只要将动画的时长设置到0就能忽略掉动画过程,从而直接获取到动画的最终效果。需要重写Animate类,用 android.graphics.Camera和 android.graphics.Matrix可以比较容易地实现翻转效果,代码实现如下:

/**
 * Created by obo on 15/11/26.
 */

import android.graphics.Camera;
import android.graphics.Matrix;
import android.view.animation.Animation;
import android.view.animation.Transformation;

public class Rotate3dAnimation extends Animation {

    // 中心点
    private final float mCenterX;
    private final float mCenterY;
    // 3D变换处理camera(不是摄像头)
    private Camera mCamera = new Camera();

    /**
     * @param centerX 翻转中心x坐标
     * @param centerY 翻转中心y坐标
     */
    public Rotate3dAnimation(float centerX,
                             float centerY) {
        mCenterX = centerX;
        mCenterY = centerY;
    }

    @Override
    protected void applyTransformation(float interpolatedTime, Transformation t) {
        // 生成中间角度
        final Camera camera = mCamera;
        final Matrix matrix = t.getMatrix();
        camera.save();
        camera.rotateY(180);
        // 取得变换后的矩阵
        camera.getMatrix(matrix);

        camera.restore();
        matrix.preTranslate(-mCenterX, -mCenterY);
        matrix.postTranslate(mCenterX, mCenterY);
    }
}

调用的方法如下:

 View layoutView = findViewById(R.id.reverse_layout);
 Animation animation = new Rotate3dAnimation(layoutView.getWidth() / 2, layoutView.getHeight() / 2);
 animation.setFillAfter(true);
 layoutView.startAnimation(animation);

这里的reverse_layout是一个RelativeLayout的布局,调用了该段代码之后能将Layout和layout所承载的内容都进行翻转。思路是将layoutView从中心点进行180度的水平翻转,需要设置setFillAfter为true来保持翻转后的最终状态。这里需要注意的是,这段代码不能直接放在onCreate里面调用,因为在onCreate的时候,layout的大小还没有被计算出来,如果想在onCreate里面使用可以这样:

final View layoutView = findViewById(R.id.reverse_layout);

ViewTreeObserver vto = layoutView.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
    @Override
    public void onGlobalLayout() {

        Animation animation = new Rotate3dAnimation(layoutView.getMeasuredWidth() / 2, layoutView.getMeasuredHeight() / 2);
        animation.setFillAfter(true);
        layoutView.startAnimation(animation);
    }
});

可以为layoutView加一个布局的监听,监听到layoutView布局加载了之后,就能正常获取layoutView的大小了,也就能正常输出效果了。

2.重写控件

对控件进行重写是另外一个实现的思路。假设承载界面的Layout是RelativeLayout,则可以对整个RelativeLayout进行重写,重写的代码可以如下:

import android.content.Context;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.widget.RelativeLayout;

/**
 * Created by obo on 15/12/4.
 */
public class ReverseLayout extends RelativeLayout {

    public boolean isReverse = true;

    public ReverseLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public void dispatchDraw(Canvas canvas) {

        if (isReverse)
            canvas.scale(-1, 1, getWidth() / 2, getHeight() / 2);

        super.dispatchDraw(canvas);
    }
}

之后,在布局xml中将最外层的RelativeLayout替换成ReverseLayout就能对界面进行翻转。这样的翻转能够将Layout里面所有的控件都进行翻转,如果需要翻转的仅仅只是一个TextView的话,则可以单单对一个TextView进行重写,这个时候,就不需要重写dispatchDraw方法,而应该重写onDraw方法,如下:

import android.content.Context;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.widget.TextView;

/**
 * Created by obo on 15/12/6.
 */
public class ReverseTextView extends TextView {
    public ReverseTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public void onDraw(Canvas canvas) {
        canvas.scale(-1, 1, getWidth() / 2, getHeight() / 2);
        super.onDraw(canvas);
    }
}

onDraw和dispatchDraw的区别是onDraw只对当前的View有效,而不会影响其所包含的SubView,而dispatchDraw则会将翻转效果传递到所有的SubView。

3.SurfaceView翻转

以上两种方法能实现大多数View的翻转,但是都对SurfaceView没有效果,因为SurfaceView是通过双缓冲机制进行绘制的,不会经过onDraw或是dispatchDraw方法,也就不能对我们所进行的操作进行响应了,对于自定义的SurfaceView来说,可以对在lockCanvas中获取的Canvas对象进行翻转处理。

下面给出SurfaceView翻转实现的代码:

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

/**
 * Created by obo on 15/12/6.
 */
public class TestSurfaceView extends SurfaceView implements SurfaceHolder.Callback{

    SurfaceHolder surfaceHolder ;

    public TestSurfaceView(Context context, AttributeSet attrs) {
        super(context, attrs);
        surfaceHolder = this.getHolder();
        surfaceHolder.addCallback(this);
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {

        Canvas canvas = surfaceHolder.lockCanvas();

        //绘制之前先对画布进行翻转
        canvas.scale(-1,1, getWidth()/2,getHeight()/2);

        //开始自己的内容的绘制
        Paint paint = new Paint();
        canvas.drawColor(Color.WHITE);
        paint.setColor(Color.BLACK);
        paint.setTextSize(50);
        canvas.drawText("这是对SurfaceView的翻转",50,250,paint);
        surfaceHolder.unlockCanvasAndPost(canvas);
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {}

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {}

}

采用该方法之后,对SurfaceView也能进行翻转了,效果如下:


实际上,该方法是借助了第二种方法的思路,直接对canvas进行预先的处理从而达到我们所需要的效果的,所以也可以作为第二种方法的扩展。

4.手势翻转

需要注意的是,以上这几种方法仅仅是实现了显示的翻转,手势操作的位置并没有发生翻转。所以使用以上翻转方式的话需要结合手势翻转的实现,其实现思路是重写外层的viewgroup的onInterceptTouchEvent方法,对下发的MotionEvent进行一次翻转操作,使得childView接收到的手势都是反过来的。实现代码如下 :

package com.obo.reverseview.views.touchreverse;

import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.RelativeLayout;

/**
 * Created by obo on 16/5/4.
 * Email:obo1993@gmail.com
 * Git:https://github.com/OboBear
 * Blog:http://blog.csdn.net/leilba
 */
public class TouchReverseLayout extends RelativeLayout {

    public TouchReverseLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {

        ev.setLocation(this.getWidth() - ev.getX(),ev.getY());

        return super.onInterceptTouchEvent(ev);
    }
}

5.更优雅的方案

对于普通view(非SurfaceView),还有一个更加优雅的实现方案,而且不需要重写onInterceptTouchEvent方法,只需要调用父布局的setScaleY或者setScaleX方法即可。

// 获取需要翻转的父布局
layoutScale = findViewById(R.id.layout_scale);
// 翻转
layoutScale.setScaleY(-1);

6.总结

采用动画和重写控件的方案都能实现界面翻转的效果,而且性能方面都十分不错。

但是这两种方法都会存在以下一些问题:

1.仅仅翻转显示内容,不会翻转点击的坐标位置。也就是说,如果布局内最左边存在着一个按钮,则翻转后,按钮将会显示在界面最右边,但是想要点击按钮的话,还是在界面原先按钮所在的最左边进行点击才会得到响应。这里可以采取在父布局中对坐标进行重新定位的方法。

2. 无法翻转已经封装好了的SurfaceView。比如说,当前要将某第三方地图界面进行水平镜像翻转,发现用第一种和第二种方法都无效,查看了部分源码之后发现其实质是用SurfaceView进行实现的,但是SurfaceView是作为该地图的一个subView存在的,所以不能直接获取到该subView,也不能到该SubView的绘制层获取canvas了,这个时候第三种方法也无法进行施展,这里需要采用动态代理的方式来解决,可见Android动态代理为SurfaceHolder添加Hook

代码例子发布在github:

AndroidReverseView

时间: 2024-08-28 13:35:36

【Android】android镜像翻转的相关文章

Android实现Flip翻转动画效果_Android

本文实例讲述了Android实现Flip翻转动画效果的方法,分享给大家供大家学习借鉴. 具体实现代码如下: LinearLayout locationLL = (LinearLayout) findViewById(R.id.locationLL); LinearLayout baseLL = (LinearLayout) findViewById(R.id.baseLL); private void flipit() { Interpolator accelerator = new Accel

Android实现文字翻转动画的效果_Android

本文实现了Android程序文字翻转动画的小程序,具体代码如下: 先上效果图如下: 要求: 沿Y轴正方向看,数值减1时动画逆时针旋转,数值加1时动画顺时针旋转. 实现动画的具体细节见"RotateAnimation.Java".为方便查看动画旋转方向,可以将RotateAnimation.DEBUG值设置为true即可.
 RotateAnimation参考自APIDemos的Rotate3DAnimation
 RotateAnimation的构造函数需有三个参数,分别说明动画组件的

Android实现Flip翻转动画效果

本文实例讲述了Android实现Flip翻转动画效果的方法,分享给大家供大家学习借鉴. 具体实现代码如下: LinearLayout locationLL = (LinearLayout) findViewById(R.id.locationLL); LinearLayout baseLL = (LinearLayout) findViewById(R.id.baseLL); private void flipit() { Interpolator accelerator = new Accel

Android实现文字翻转动画的效果

本文实现了Android程序文字翻转动画的小程序,具体代码如下: 先上效果图如下: 要求: 沿Y轴正方向看,数值减1时动画逆时针旋转,数值加1时动画顺时针旋转. 实现动画的具体细节见"RotateAnimation.Java".为方便查看动画旋转方向,可以将RotateAnimation.DEBUG值设置为true即可.
 RotateAnimation参考自APIDemos的Rotate3DAnimation
 RotateAnimation的构造函数需有三个参数,分别说明动画组件的

PPT文档中怎么水平或竖直镜像翻转图片?

  PPT文档中怎么水平或竖直镜像翻转图片? 1.首先插入一张图片,这个不多说了.方法有很多,直接拖动图片到PPT相应位置,或者点击插入→图片. 2.选择格式,在格式下面有一个旋转.这个就可以解决常用的水平/竖直翻转图片. 3.如果需要更加精准的调节图片的旋转角度,比如微调一下人脸角度之类的.可以右键图片.选择设置图片格式. 4.在"设置图片格式"中,选择"三维旋转".里面可以进行角度控制的旋转. 5.水平/竖直/按角度旋转都可以. 6.其实最简单最常用的还是图片的

PPT文档中怎么水平或竖直镜像翻转图片

  PPT中如何水平或者竖直镜像翻转图片呢?这个功能可能不常用,但是很有用! 1.首先插入一张图片,这个不多说了.方法有很多,直接拖动图片到PPT相应位置,或者点击插入→图片. 文档中怎么水平或竖直镜像翻转图片-ps镜像翻转"> 2.选择格式,在格式下面有一个旋转.这个就可以解决常用的水平/竖直翻转图片. 3.如果需要更加精准的调节图片的旋转角度,比如微调一下人脸角度之类的.可以右键图片.选择设置图片格式. 4.在"设置图片格式"中,选择"三维旋转"

效果-Android Android 拖拽拼图功能的实现!!急!!在线等!急!

问题描述 Android Android 拖拽拼图功能的实现!!急!!在线等!急! 我要的效果是 比如我在左边选择了第一个模版,然后我拖拽到右边工作区域,松开放下,然后我在去左边选择模版,比如我在选择第一个模版,又拖动右边,比如我我放在我第一个的左边,这是要注意了,这2个图片不能重叠在一起!!如果我觉得第二个没放好!我可以在点击让他消失,在去左边选择一个模版,拖过来拼在上下左右的位置!!! 小弟先谢过各位了!! 有实例代码最好!!给小弟思路小弟也感激不尽!! 解决方案 在电脑中处理不行吗? 解决

[android]android自动化测试十之单元测试实例

android源代码中每个app下中都自带了一个test用例,下面主要介绍下camra单元测试用例  在AndroidManifest.xml中标明了测试用例instrumentation函数入口  Java代码 <?xml version="1.0" encoding="utf-8"?> <!-- Copyright (C) 2008 The Android Open Source Project Licensed under the Apach

文档-android: android 中文件的备份问题

问题描述 android: android 中文件的备份问题 我想问一下,如果我的app 生成了几个文件,有办法压缩备份成一个新的文件吗.? 我看有.backup 后缀的文件,是怎么生成的呢? 谢谢..最好有API 或者文档什么的 解决方案 Android文件访问权限和路径问题Android 本地文件读取, 数据库文件备份android读SDCard中txt文件中文乱码问题 解决方案二: .backup是一些备份文件.总体说来 Android API Level 8及其以上Android系统提供