大橙子网站建设,新征程启航
为企业提供网站建设、域名注册、服务器等服务
在介绍之前,我们需要先了解默认情况下android屏幕旋转的机制:
站在用户的角度思考问题,与客户深入沟通,找到宾阳网站设计与宾阳网站推广的解决方案,凭借多年的经验,让设计与互联网技术结合,创造个性化、用户体验好的作品,建站类型包括:成都做网站、网站建设、企业官网、英文网站、手机端网站、网站推广、空间域名、网络空间、企业邮箱。业务覆盖宾阳地区。
默认情况下,当用户手机的重力感应器打开后,旋转屏幕方向,会导致当前activity发生onDestroy- onCreate,这样会重新构造当前activity和界面布局,如果在Camera界面,则表现为卡顿或者黑屏一段时间。如果是在横竖屏UI设计方面,那么想很好地支持屏幕旋转,则建议在res中建立layout-land和layout-port两个文件夹,把横屏和竖屏的布局文件分别放入对应的layout文件夹中。
了解了这些以后,我们对android的屏幕旋转方法进行如下总结:
1. AndroidManifest.xml设置
如果单单想设置横屏或者竖屏,那么只需要添加横竖屏代码:
android:screenOrientation="landscape"横屏设置;
android:screenOrientation="portrait"竖屏设置;
这种方法的优点:即使屏幕旋转,Activity也不会重新onCreate。
缺点:屏幕只有一个方向。
2. 代码动态设置
如果你需要动态改变横竖屏设置,那么,只需要在代码中调用setRequestedOrientation()函数:
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
//横屏设置
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
//竖屏设置
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
//默认设置
这种方法优点:可以随意动态设置,满足我们人为改变横竖屏的要求,同时满足横竖屏UI不同的设计需求;
缺点:如果改变设置,那么,Activity会被销毁,重新构建,即重新onCreate;
3. 重写onConfigurationChanged
如果你不希望旋转屏幕的时候Activity被不断的onCreate(这种情况往往会造成屏幕切换时的卡顿),那么,可以使用此方法:
首先,在AndroidMainfest.xml中添加configChanges:
activity android:name=".Test"
android:configChanges="orientation|keyboard"
/activity
注意,keyboardHidden表示键盘辅助功能隐藏,如果你的开发API等级等于或高于13,还需要设置screenSize,因为screenSize会在屏幕旋转时改变;
android:configChanges="keyboardHidden|orientation|screenSize"
然后,在Activity中重写onConfigurationChanged方法,这个方法将会在屏幕旋转变化时,进行监听处理:
public void onConfigurationChanged(Configuration newConfig) {
// TODO Auto-generated method stubsuper.onConfigurationChanged(newConfig);
if (newConfig.orientation==Configuration.ORIENTATION_LANDSCAPE){
// Nothing need to be done here
} else {
// Nothing need to be done here
}
}
这个方法的优点:我们可以随时监听屏幕旋转变化,并对应做出相应的操作;
缺点:它只能一次旋转90度,如果一下子旋转180度,onConfigurationChanged函数不会被调用。
4. 结合OrientationEventListener,自定义旋转监听设置
如果你想更加完美,更加完全的掌控监听屏幕旋转变化,比如,转屏时不想重新onCreate,尤其是在Camera界面,不想出现旋转preview时屏幕的卡顿、黑屏等问题,那么,可以尝试:
首先,创建OrientationEventListener对象:
private OrientationEventListener mOrientationListener;
// screen orientation listener
private boolean mScreenProtrait = true;
private boolean mCurrentOrient = false;
然后,自定义屏幕变化回调接口
abstract protected void OrientationChanged(int orientation);
//screen orientation change event
最后,自定义监听类
private final void startOrientationChangeListener() {
mOrientationListener = new OrientationEventListener(this) {
@Override
public void onOrientationChanged(int rotation) {
if (((rotation = 0) (rotation = 45)) || (rotation = 315)||((rotation=135)(rotation=225))) {//portrait
mCurrentOrient = true;
if(mCurrentOrient!=mScreenProtrait)
{
mScreenProtrait = mCurrentOrient;
OrientationChanged(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
Log.d(TAG, "Screen orientation changed from Landscape to Portrait!");
}
}
else if (((rotation 45) (rotation 135))||((rotation225)(rotation315))) {//landscape
mCurrentOrient = false;
if(mCurrentOrient!=mScreenProtrait)
{
mScreenProtrait = mCurrentOrient;
OrientationChanged(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
Log.d(TAG, "Screen orientation changed from Portrait to Landscape!");
}
}
}
};
mOrientationListener.enable();
}
在onCreate()中调用:
startOrientationChangeListener();
这个方法的优点:你可以任意随时准确的监听屏幕旋转变化的状态,可以随时动态改变横竖屏状态;
注:对于Camera来说,你可以设置初始化为横屏或者竖屏,然后对外提供旋转监听,这样,既可以获得屏幕旋转状态,让你做出相应的操作,又不会出现重新onCreate当前Activity造成的卡顿与短暂的黑屏切换。
横屏180°旋转系统不会回调到到 onConfigurationChanged() ,只能使用其他的方案,目前有2个方案
1、使用 OrientationEventListener 监听屏幕的旋转,里面本质使用的是 TYPE_ACCELEROMETER 传感器,具体如下:
开启调用 mOrientationListener. enable() , 关闭调用 mOrientationListener. disable() ;这种方式对性能消耗比较大, 但是可以获取到手机当前的角度
2、使用监听 DisplayManager 方式,手机切换方向会导致UI 显示的改变,所以会回调到这里
这种方式不会耗性能
设备平放,屏幕朝正上方。以下四个常量分别代表:
private static final int ROTATION_0 = 0;//初始情况。这个时候设备是横屏还是竖屏与硬件设备安装时默认的显示方向有关。
private static final int ROTATION_90 = 1;//设置屏幕方向自动旋转后,右侧翻起侧立时,屏幕会旋转到这个方向。
private static final int ROTATION_270 = 2;//设置屏幕方向自动旋转后,左侧翻起度侧立时,屏幕会旋转到这个方向。
private static final int ROTATION_180 = 3;//设置屏幕方向自动旋转后,屏幕底部侧立时,屏幕会旋转到这个方向。
再看两个数组:
view plain
private static final int[][][] THRESHOLDS_WITH_180 = new int[][][] {
{{60, 165}, {165, 195}, {195, 300}},
{{0, 30}, {165, 195}, {195, 315}, {315, 360}},
{{0, 45}, {45, 165}, {165, 195}, {330, 360}},
{{0, 45}, {45, 135}, {225, 315}, {315, 360}},
};
private static final int[][] ROTATE_TO_WITH_180 = new int[][] {
{ROTATION_90, ROTATION_180, ROTATION_270},
{ROTATION_0, ROTATION_180, ROTATION_90, ROTATION_0},
{ROTATION_0, ROTATION_270, ROTATION_180, ROTATION_0},
{ROTATION_0, ROTATION_90, ROTATION_270, ROTATION_0},
};
当前屏幕旋转方向为ROTATION_0时,取int[][] threshold=THRESHOLDS_WITH_180[0];
当前屏幕旋转方向为ROTATION_90时,取int[][] threshold=THRESHOLDS_WITH_180[1];
当前屏幕旋转方向为ROTATION_270时,取int[][] threshold=THRESHOLDS_WITH_180[2];
当前屏幕旋转方向为ROTATION_180时,取int[][] threshold=THRESHOLDS_WITH_180[3];
其中,threshold中的每一个元素由两个值构成,用来表示一个范围。
WindowOrientationListener会注册一个Accelerator类型的SensorEventListener,当有新的SensorEvent产生时,调用filterOrientation产生一个int orientation值。这个值会在threshold的各个元素表示的范围中匹配,看会落在哪个范围。假设当前屏幕方向为ROTATION_0,那么threshold={{60, 165}, {165, 195}, {195, 300}},假设这个时候把屏幕左侧翻起90度。filterOrientation计算出的orientation值落在了第三个元素范围内,那么去ROTATE_TO_WITH_180中寻找与它对应的值,发现是ROTATION_270,那么就把当前屏幕旋转方向改变为270度。threshold的取值就变成了THRESHOLDS_WITH_180[2]。当把屏幕再次放平时,filterOrientation计算出的orientation值会落在第一个元素表示的范围内。去ROTATE_TO_WITH_180中寻找与它对应的值,发现是ROTATION_0,那么当前屏幕旋转方向被改变为0度。
还有一个变量比较重要,mAllow180Rotation,这个变量设置为false时,就不使用THRESHOLDS_WITH_180和ROTATE_TO_WITH_180这一对数组来做上面这些变的了,就使用THRESHOLDS和ROTATE_TO。
其实,我研究了半天也没有搞清filterOrientation的算法以及THRESHOLDS_WITH_180和THRESHOLDS这两个数组里面的每个数字代表的具体意义。最后只搞清了上面的这个流程,还有ROTATION_0, ROTATION_90, ROTATION_270, ROTATION_180这四个角度分别代表哪四个方向。但这足以应付我们要做的事情了。
比如,我想让屏幕最多只旋转90度和180度,不让它有旋转270度的机会。那就把ROTATE_TO_WITH_180里面的ROTATION_270全部变成90度。这样,应该旋转到270度时,就会旋转到90度了。如果不想让屏幕旋转,把所有值都改成ROTATION_0就可以了。
再深入挖掘一下这个话题
PhonwWindowManager是唯一实现WindowOrientationListener接口的类,它管理着整个设备界面的显示。当PhonwWindowManager通过WindowOrientationListener知道屏幕方向发生旋转时,会告诉WindowManagerService:
mWindowManager.setRotation(rotation, false, mFancyRotationAnimation);
而WindowManagerService得到这个通知后,会做两个比较重要的事情:
1、Surface.setOrientation(0, rotation, animFlags);
2、mRotationWatchers.get(i).onRotationChanged(rotation);
我们知道,每个Activity都有一个View树,每个View树都是绘画在一个Surface上面的。通过上面这两步,先把Surface给旋转了,再告诉Activity重新绘制View树,就完了整个屏幕的旋转。
手机界面的自动旋转可以到设置里面设置
但是有些播放器的自动旋转是软件控制的,也就是说你关了自动旋转,播放视频的时候有些软件之本身播放旋转。
先上效果图
源码
单点拖动图片对图片进行平移操作。双手缩放图片大小和旋转图片到一定的角度。图片缩放的时候 不能大于最大的缩放因子和小于最小的缩放因子。大于最大缩放因子或者小于最小缩放因子需要对图像进行回弹。图片旋转的角度只能为90度的倍数,不满足90度要进行回弹。图片回弹要一个渐变的效果。
大体思路: 首先,Android中提供了Matrix类可以对图像进行处理。其次,要显示一张图片最容易想到的就是ImageView。回弹要求渐变的过程,可以通过属性动画进行设置。所以大体的思路是:继承ImageView,重写onTouchEvent()方法,判断事件类型,在对应的事件使用Matrix对图像进行变换。
Matrix是一个已经封装好的矩阵,最重要的作用就是对坐标点进行变换。
举个栗子:
1.某个点(x0,y0,1)通过单位矩阵E映射得到的点还是(x0,y0,1)。
3.点(x0,y0,1)通过矩阵T映射得到的点就会做如下的变换
可以看到点(x0,y0,1)经过T矩阵在x轴方向上平移了dx,在y轴方向上平移了dy。
通过以上的变换可以得到具体的思路: 我们维护一个图像对应的矩阵mCurrentMatrix,该矩阵主要是对ImageView中的图像的各个点进行映射。ImageView在容器位置摆放完成之后,置mCurrentMatrix矩阵为单位矩阵。当onTouchEvent()方法中触发单点触控并且手指进行平移的时候,调用矩阵mCurrentMatrix的postTranslate(dx,dy),对mCurrentMatrix进行变换。当手指抬起,利用变换结束后的矩阵对图像的各个点进行映射,从而得到平移变换后的图像。同理可得,在两只手指进行缩放旋转的时候,我们对矩阵mCurrentMatrix进行各种变换,当缩放旋转的事件结束再利用变换完的矩阵去映射图像的各个点,从而得到缩放、旋转后的图像。
安卓自定义View进阶 - Matrix原理
安卓自定义View进阶 - Matrix详解
首先理清事件的逻辑:
初始化图像大小和位置
缩放图像大小和控件大小自适应,平移图像中心和控件中心重合
onTouchEvent()函数
平移操作
将图像对应的矩阵进行变换。
缩放操作
mBoundRectF为记录图像边界的矩形。缩放的时候选取图像的中心进行缩放。
旋转操作
旋转的时候旋转的旋转中心也是图像的中心
图像中各个点的映射
调用ImageView的setImageMatrix(Matrix matrix)会让ImageView根据设置的matrix去重新绘制图像。
更新图像的矩形边界
获得图像的矩形,并根据矩阵映射矩形各个点的坐标。
缩放回弹
旋转回弹
一些计算方法
要求图像的变换是一个渐变的过程,很容易想到的就是属性动画。因为属性动画本身就是对值进行不断set的过程。而我们维护的矩阵也是一个值,所以很自然可以想到,如果得到回弹之前的矩阵的值以及回弹之后矩阵的值,就可以根据动画监听器中动画当前的系数值去改变矩阵的值。
对animator对象设置完监听器之后,就可以在手指抬起的时候调用属性动画的start()方法开启动画。
自定义可平移、缩放、旋转的控件主要点有两个方面:一是onTouchEvent()中判断平移、旋转、缩放的触发条件,平移位移量、缩放比例因子、旋转角度的计算。二是Matrix矩阵的应用。