ViewConfiguration类
ViewConfiguration是系统中关于视图的各种特性的常量记录对象。其中包含各种基础数据,在编写自定义控件时会用经常用到。
下面介绍几个常用的方法:
1.在可滑动的控件中用于区别单击子控件和滑动操作的一个伐值。
mTouchSlop = configuration.getScaledTouchSlop();
2.用于设置最小加速率和最大速率.
mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();
mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
3.滚动距离
mOverscrollDistance = configuration.getScaledOverscrollDistance();
4.fling距离
mOverflingDistance = configuration.getScaledOverflingDistance();
5.摩擦力,用来计算减速度
mDeceleration = computeDeceleration(ViewConfiguration.getScrollFriction());
ViewConfiguration的常量总结:
/** * 包含了方法和标准的常量用来设置UI的超时、大小和距离 */
public class ViewConfiguration {
// 设定水平滚动条的宽度和垂直滚动条的高度,单位是像素px
private static final int SCROLL_BAR_SIZE = 10;
//定义滚动条逐渐消失的时间,单位是毫秒
private static final int SCROLL_BAR_FADE_DURATION = 250;
// 默认的滚动条多少秒之后消失,单位是毫秒
private static final int SCROLL_BAR_DEFAULT_DELAY = 300;
// 定义边缘地方褪色的长度
private static final int FADING_EDGE_LENGTH = 12;
//定义子控件按下状态的持续事件
private static final int PRESSED_STATE_DURATION = 125;
//定义一个按下状态转变成长按状态的转变时间
private static final int LONG_PRESS_TIMEOUT = 500;
//定义用户在按住适当按钮,弹出全局的对话框的持续时间
private static final int GLOBAL_ACTIONS_KEY_TIMEOUT = 500;
//定义一个touch事件中是点击事件还是一个滑动事件所需的时间,如果用户在这个时间之内滑动,那么就认为是一个点击事件
private static final int TAP_TIMEOUT = 115;
/** * Defines the duration in milliseconds we will wait to see if a touch event * is a jump tap. If the user does not complete the jump tap within this interval, it is * considered to be a tap. */
//定义一个touch事件时候是一个点击事件。如果用户在这个时间内没有完成这个点击,那么就认为是一个点击事件
private static final int JUMP_TAP_TIMEOUT = 500;
//定义双击事件的间隔时间
private static final int DOUBLE_TAP_TIMEOUT = 300;
//定义一个缩放控制反馈到用户界面的时间
private static final int ZOOM_CONTROLS_TIMEOUT = 3000;
/** * Inset in pixels to look for touchable content when the user touches the edge of the screen */
private static final int EDGE_SLOP = 12;
/** * Distance a touch can wander before we think the user is scrolling in pixels */
private static final int TOUCH_SLOP = 16;
/** * Distance a touch can wander before we think the user is attempting a paged scroll * (in dips) */
private static final int PAGING_TOUCH_SLOP = TOUCH_SLOP * 2;
/** * Distance between the first touch and second touch to still be considered a double tap */
private static final int DOUBLE_TAP_SLOP = 100;
/** * Distance a touch needs to be outside of a window's bounds for it to * count as outside for purposes of dismissing the window. */
private static final int WINDOW_TOUCH_SLOP = 16;
//用来初始化fling的最小速度,单位是每秒多少像素
private static final int MINIMUM_FLING_VELOCITY = 50;
//用来初始化fling的最大速度,单位是每秒多少像素
private static final int MAXIMUM_FLING_VELOCITY = 4000;
//视图绘图缓存的最大尺寸,以字节表示。在ARGB888格式下,这个尺寸应至少等于屏幕的大小
@Deprecated
private static final int MAXIMUM_DRAWING_CACHE_SIZE = 320 * 480 * 4; // HVGA screen, ARGB8888
//flings和scrolls摩擦力度大小的系数
private static float SCROLL_FRICTION = 0.015f;
/** * Max distance to over scroll for edge effects */
private static final int OVERSCROLL_DISTANCE = 0;
/** * Max distance to over fling for edge effects */
private static final int OVERFLING_DISTANCE = 4;
}
API:14以后提供了方法判断是否有物理按键:ViewConfiguration.hasPermanentMenuKey(). 一般来说物理按键和虚拟按键只会有一种。可以用这个方法来判断是否有导航栏NavigationBar和计算NavigationBar的高度。
判断是否有导航栏NavigationBar:
public static boolean checkDeviceHasNavigationBar(Context activity) {
//通过判断设备是否有返回键、菜单键(不是虚拟键,是手机屏幕外的按键)来确定是否有navigation bar
boolean hasMenuKey = ViewConfiguration.get(activity)
.hasPermanentMenuKey();
boolean hasBackKey = KeyCharacterMap
.deviceHasKey(KeyEvent.KEYCODE_BACK);
if (!hasMenuKey && !hasBackKey) {
// 做任何你需要做的,这个设备有一个导航栏
return true;
}
return false;
}
计算NavigationBar的高度:
public int getNavigationBarHeight(Context c) {
int result = 0;
boolean hasMenuKey = ViewConfiguration.get(c).hasPermanentMenuKey();
boolean hasBackKey = KeyCharacterMap.deviceHasKey(KeyEvent.KEYCODE_BACK);
if (!hasMenuKey && !hasBackKey) {
//The device has a navigation bar
Resources resources = c.getResources();
int orientation = getResources().getConfiguration().orientation;
int resourceId;
if (isTablet(c)) {
resourceId = resources.getIdentifier(orientation == Configuration.ORIENTATION_PORTRAIT ? "navigation_bar_height" : "navigation_bar_height_landscape", "dimen", "android");
} else {
resourceId = resources.getIdentifier(orientation == Configuration.ORIENTATION_PORTRAIT ? "navigation_bar_height" : "navigation_bar_width", "dimen", "android");
}
if (resourceId > 0) {
return getResources().getDimensionPixelSize(resourceId);
}
}
return result;
}
Configuration类
Configuration类虽然和ViewConfiguration类名字之差一个“view”,但是二者并无继承关系,也没有共同的父类。 Configuration类是专门用来描述手机设备上的配置信息。这些配置信息包括用户特定的配置项,也包括系统的动态设备配置。
程序中可调用Activity的如下方法来获取Configuration对象
//获取系统的Configuration对象
Configuration cfg = getResources().getConfiguration();
其中以下的参数代表的配置信息:
fontScale:获取当前用户设置的字体的缩放因子。
keyboard:获取当前设备所关联的键盘类型。该属性的返回值:KEYBOARD_12KEY(只有12个键的小键盘)、KEYBOARD_NOKEYS、KEYBOARD_QWERTY(普通键盘)
keyboardHidden:该属性返回一个boolean值用于标识当前键盘是否可用。该属性不仅会判断系统的硬件键盘,也会判断系统的软键盘(位于屏幕)。
locale:获取用户当前的Locale.
mcc:获取移动信号的国家码
mnc:获取移动信号的网络码
navigation:判断系统上方向导航设备的类型。该属性的返回值:NAVIGATION_NONAV(无导航)、NAVIGATION_DPAD(DPAD导航) NAVIGATION_TRACKBALL(轨迹球导航)、NAVIGATION_WHEEL(滚轮导航)
orientation:获取系统屏幕的方向。该属性的返回值:ORIENTATION_LANDSCAPE(横向屏幕)、ORIENTATION_PORTRAIT(竖向屏幕)
touchscreen:获取系统触摸屏的触摸方式。该属性的返回值:TOUCHSCREEN_NOTOUCH(无触摸屏)、TOUCHSCREEN_STYLUS(触摸笔式触摸屏)、
TOUCHSCREEN_FINGER(接收手指的触摸屏)
下面写一个监听屏幕旋转的demo:
MainActivity如下:
public class MainActivity extends Activity {
private Button mButton;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
System.out.println("---> onCreate()");
init();
}
private void init(){
mButton=(Button) findViewById(R.id.button);
mButton.setOnClickListener(new ClickListenerImpl());
}
private class ClickListenerImpl implements View.OnClickListener {
@Override
public void onClick(View v) {
getConfigurationInfo();
}
}
private void getConfigurationInfo(){
Configuration configuration=getResources().getConfiguration();
//获取屏幕方向
int l=configuration.ORIENTATION_LANDSCAPE;
int p=configuration.ORIENTATION_PORTRAIT;
if (configuration.orientation==l) {
System.out.println("现在是横屏");
}
if (configuration.orientation==p) {
System.out.println("现在是竖屏");
}
//获取国家码和网络码
int countryNum=configuration.mcc;
int netNum=configuration.mnc;
System.out.println("国家码="+countryNum+",网络码="+netNum);
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
System.out.println("---> onConfigurationChanged()");
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString("name", "mxn");
outState.putInt("id", 21);
System.out.println("---> onSaveInstanceState()");
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
String name=savedInstanceState.getString("name");
int id=savedInstanceState.getInt("id");
System.out.println("---> onRestoreInstanceState()");
System.out.println("名字="+name+",编号="+id);
}
}
main.xml如下:
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="获取Configuration信息"
android:textSize="25sp"
android:layout_marginTop="80dip"
android:layout_centerHorizontal="true"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="测试ConfigurationChange"
android:textSize="25sp"
android:layout_centerInParent="true"
/>
</RelativeLayout>
AndroidManifest.xml如下:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.mxn.soul.demo" >
<application
android:allowBackup="true"
android:icon="@mipmap/icon"
android:label="@string/app_name"
android:name=".BaseApplication"
android:theme="@style/AppTheme" >
<activity
android:name=".MainActivity"
android:configChanges="keyboardHidden|orientation|screenSize"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
1.点击Button利用Configuration查看手机上的配置信息
11-27 11:56:41.763 4093-4093/com.mxn.soul.demo I/System.out: 现在是横屏
11-27 11:56:41.763 4093-4093/com.mxn.soul.demo I/System.out: 国家码=0,网络码=0
2.在manifest为Activity配置configChanges时,旋转屏幕,输出如下:
11-27 11:59:26.827 8684-8684/com.mxn.soul.demo I/System.out: ---> onCreate()
11-27 11:59:31.110 8684-8684/com.mxn.soul.demo I/System.out: ---> onConfigurationChanged()
由于设备和系统版本的差异,少数情况下设置android:configChanges=”orientation” 无效, 建议设置为:android:configChanges=”keyboardHidden|orientation|screenSize” ,表示当前Activity可以对屏幕是否旋转进行监听 (当然也可对其他系统信息进行监听) 配置后屏幕旋转时会调用onConfigurationChanged()方法.
注意:当onConfigurationChanged发生之后,并不会对activiyt的生命周期有影响的,也就是说当横竖修改之后,android 并不会有任何生命周期的变化。同理还有(键盘显示或者隐藏、用户的语言设置、用户字体修改、对键盘类型修改、键盘导航修改) 这些都可以触发相关的activity的事件产生。
android:configChanges配置说明:
Android通过终止、重启应用程序来重新加载资源文件,以做到对语言、区域和硬件实时变化的支持。它的默认的行为不是总是方便和令人满意的, 尤其当配置变化(如屏幕方向和键盘可视)、用户旋转设备或划出键盘等。你可以通过监测和响应定制你的应用程序来对这些变化作出响应。 为了能让Activity能监听实时的配置变化,需要在manifest节点里添加“android:configChanges”特性,指定你要处理的配置变化事件。 接下来的列表给出了你可以指定的配置变化的事件值:
*orientation 屏幕在纵向和横向间旋转。 *keyboardHidden 键盘显示或隐藏。 *fontScale 用户变更了首选的字体大小。 *locale 用户选择了不同的语言设定。 *keyboard 键盘类型变更,例如手机从12键盘切换到全键盘 *ouchscreen或navigation 键盘或导航方式变化,一般不会发生这样的事件。
你可以选择捕获多个事件,通过在各事件值间使用“|”。 override onConfigurationChanged这个方法去捕获配置变化,通过传入的Configuration 对象的值进行合适的处理。 需要提醒的是需要调用父类的方法super.onConfigurationChanged(newConfig);并且要重新加载Activity使用的资源,以防有变更。
当onConfigurationChanged 被调用时,Activity的资源变量都已经用新的值进行了更新,所以它们是安全的。任何你没有显式指明的配置变化事件, 都将由应用程序捕获,而且仍然会引起应用程序的重启,而不会调用onConfigurationChanged 方法。
3.若不配置android:configChanges,那么每次屏幕旋转的时候都会调用Activity的onCreate()方法 而不会调用onConfigurationChanged()。取消在2中的设置 .此时每次旋转屏幕都会调用onCreate(),并在屏幕旋转前调用onSaveInstanceState()保存现场状态,在选中后调用onRestoreInstanceState() 恢复现场. 所以此时调用顺序为: onSaveInstanceState->onCreate()->onRestoreInstanceState()。除此以外: 当系统内存紧张时可暂时杀死该Activity,内存允许时重启该Activity.在这样情况下也是该调用顺序,原理亦类似.
输出如下:
11-27 12:04:07.508 13914-13914/com.mxn.soul.demo I/System.out: ---> onCreate()
11-27 12:04:11.400 13914-13914/com.mxn.soul.demo I/System.out: ---> onSaveInstanceState()
11-27 12:04:11.464 13914-13914/com.mxn.soul.demo I/System.out: ---> onCreate()
11-27 12:04:11.465 13914-13914/com.mxn.soul.demo I/System.out: ---> onRestoreInstanceState()
11-27 12:04:11.465 13914-13914/com.mxn.soul.demo I/System.out: 名字=mxn,编号=21
另外还有一个判断是否是平板的方法如下(官方用法):
public static boolean isTablet(Context context) {
return (context.getResources().getConfiguration().screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) >= Configuration.SCREENLAYOUT_SIZE_LARGE;
}