一、Custom View
1江锨、view的繼承關(guān)系
2、Android 如何繪制試圖層次
當(dāng)activity獲取焦點(diǎn)時(shí),它必須提供layout層次的根節(jié)點(diǎn),然后android 系統(tǒng)開始視圖的繪制過程。繪制是從layout的根節(jié)點(diǎn)開始的逊朽,按照從上往下的順序,父元素優(yōu)先子元素赊堪。
繪制的兩個(gè)過程:
- measuring pass:實(shí)現(xiàn)measure(int,int)方法虽画,順序也是從上往下,每個(gè)view保存它自己的測量值
- layout pass:實(shí)現(xiàn)layout(int,int,int,int)方法合武,順序從上往下,在這個(gè)階段每個(gè)layout manager負(fù)責(zé)他們各自所有子元素的位置竹揍,通過上一步測量的值
測量和繪制過程是交替進(jìn)行的囱嫩,layout manager可能運(yùn)行 measure pass 若干次吹艇。例如 linearlayout需要支持weight屬性撑教,relativelayout需要測量子節(jié)點(diǎn)多次才能確定約束關(guān)系秆乳。
view或activity可以再次觸發(fā)測量和繪制過程叶堆。通過 requestLayout()
在測量和布局計(jì)算完成后畦粮,視圖就開始繪制自己。這個(gè)操作通過invalidate()觸發(fā)。
3宣赔、view 截屏
每個(gè)view都支持創(chuàng)建當(dāng)前顯示狀態(tài)的圖片预麸。
# Build the Drawing Cache
view.buildDrawingCache();
# Create Bitmap
Bitmap cache = view.getDrawingCache();
# Save Bitmap
saveBitmap(cache);
view.destroyDrawingCache();
二、自定義view
1儒将、創(chuàng)建自定義view
通過繼承view或它的子類师崎,可以創(chuàng)建自定義view
通過onDraw()方法繪制視圖,如果需要重新繪制椅棺,調(diào)用invalidate()觸發(fā)onDraw()
如果定義自己的view,確保參考ViewConfiguration 類,它包含了一些常亮定義
2齐蔽、測量
必須調(diào)用 setMeasuredDimenstion(int,int)設(shè)置結(jié)果
3两疚、定義自定義 layout managers
通過繼承ViewGroup
自定義layout manager 可以重寫 onMeasure() 和 onLayout(),并且計(jì)算孩子元素的測量結(jié)果
測量孩子元素的大小通過measureChildWithMargins();
三、生命周期
一個(gè)視圖會(huì)在它依附到一個(gè)已依附到window的布局結(jié)構(gòu)時(shí)顯示含滴。
- onAttachedToWindow() 诱渤,當(dāng)window可以時(shí)調(diào)用
- onDetachedFromWindow(),當(dāng)視圖從父元素中移除時(shí)調(diào)用(父元素必須依附到window)。例如當(dāng)
activity被回收(finish()方法被調(diào)用)或者視圖在listview中被回收谈况。該方法可以用來停止動(dòng)畫和清理資源
四勺美、定義自定義屬性
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="ColorOptionsView">
<attr name="titleText" format="string" localization="suggested" />
<attr name="valueColor" format="color" />
</declare-styleable>
</resources>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
<!-- define new name space for your attributes -->
xmlns:custom="http://schemas.android.com/apk/res/com.vogella.android.view.compoundview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
>
<!-- Assume that this is your new component. It uses your new attributes -->
<com.vogella.android.view.compoundview.ColorOptionsView
android:layout_width="match_parent"
android:layout_height="?android:attr/listPreferredItemHeight"
custom:titleText="Background color"
custom:valueColor="@android:color/holo_green_light"
/>
</LinearLayout>
package com.vogella.android.view.compoundview;
import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
public class ColorOptionsView extends View {
private View mValue;
private ImageView mImage;
public ColorOptionsView(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray a = context.obtainStyledAttributes(attrs,
R.styleable.Options, 0, 0);
String titleText = a.getString(R.styleable.Options_titleText);
int valueColor = a.getColor(R.styleable.Options_valueColor,
android.R.color.holo_blue_light);
a.recycle();
// more stuff
}
}
對于自定義屬性中的format的值及其含義如下:
format屬性值:reference 、color碑韵、boolean赡茸、dimension、float祝闻、integer占卧、string、fraction联喘、enum华蜒、flag
-
reference:參考某一資源ID。
(1)屬性定義:
<declare-styleable name = "名稱">
<attr name = "background" format = "reference" />
</declare-styleable>
(2)屬性使用:
<ImageView
android:layout_width = "42dip"
android:layout_height = "42dip"
android:background = "@drawable/圖片ID"
/>
-
color:顏色值豁遭。
(1)屬性定義:
<declare-styleable name = "名稱">
<attr name = "textColor" format = "color" />
</declare-styleable>
(2)屬性使用:
<TextView
android:layout_width = "42dip"
android:layout_height = "42dip"
android:textColor = "#00FF00"
/>
-
boolean:布爾值叭喜。
(1)屬性定義:
<declare-styleable name = "名稱">
<attr name = "focusable" format = "boolean" />
</declare-styleable>
(2)屬性使用:
<Button
android:layout_width = "42dip"
android:layout_height = "42dip"
android:focusable = "true"
/>
-
dimension:尺寸值。
(1)屬性定義:
<declare-styleable name = "名稱">
<attr name = "layout_width" format = "dimension" />
</declare-styleable>
(2)屬性使用:
<Button
android:layout_width = "42dip"
android:layout_height = "42dip"
/>
-
float:浮點(diǎn)值蓖谢。
(1)屬性定義:
<declare-styleable name = "AlphaAnimation">
<attr name = "fromAlpha" format = "float" />
<attr name = "toAlpha" format = "float" />
</declare-styleable>
(2)屬性使用:
<alpha
android:fromAlpha = "1.0"
android:toAlpha = "0.7"
/>
-
integer:整型值捂蕴。
(1)屬性定義:
<declare-styleable name = "AnimatedRotateDrawable">
<attr name = "visible" />
<attr name = "frameDuration" format="integer" />
<attr name = "framesCount" format="integer" />
<attr name = "pivotX" />
<attr name = "pivotY" />
<attr name = "drawable" />
</declare-styleable>
(2)屬性使用:
<animated-rotate
xmlns:android = "http://schemas.android.com/apk/res/android"
android:drawable = "@drawable/圖片ID"
android:pivotX = "50%"
android:pivotY = "50%"
android:framesCount = "12"
android:frameDuration = "100"
/>
-
string:字符串。
(1)屬性定義:
<declare-styleable name = "MapView">
<attr name = "apiKey" format = "string" />
</declare-styleable>
(2)屬性使用:
<com.google.android.maps.MapView
android:layout_width = "fill_parent"
android:layout_height = "fill_parent"
android:apiKey = "0jOkQ80oD1JL9C6HAja99uGXCRiS2CGjKO_bc_g"
/>
-
fraction:百分?jǐn)?shù)闪幽。
(1)屬性定義:
<declare-styleable name="RotateDrawable">
<attr name = "visible" />
<attr name = "fromDegrees" format = "float" />
<attr name = "toDegrees" format = "float" />
<attr name = "pivotX" format = "fraction" />
<attr name = "pivotY" format = "fraction" />
<attr name = "drawable" />
</declare-styleable>
(2)屬性使用:
<rotate
xmlns:android = "http://schemas.android.com/apk/res/android"
android:interpolator = "@anim/動(dòng)畫ID"
android:fromDegrees = "0"
android:toDegrees = "360"
android:pivotX = "200%"
android:pivotY = "300%"
android:duration = "5000"
android:repeatMode = "restart"
android:repeatCount = "infinite"
/>
-
enum:枚舉值启绰。
(1)屬性定義:
<declare-styleable name="名稱">
<attr name="orientation">
<enum name="horizontal" value="0" />
<enum name="vertical" value="1" />
</attr>
</declare-styleable>
(2)屬性使用:
<LinearLayout
xmlns:android = "http://schemas.android.com/apk/res/android"
android:orientation = "vertical"
android:layout_width = "fill_parent"
android:layout_height = "fill_parent">
</LinearLayout>
-
flag:位或運(yùn)算。
(1)屬性定義:
<declare-styleable name="名稱">
<attr name="windowSoftInputMode">
<flag name = "stateUnspecified" value = "0" />
<flag name = "stateUnchanged" value = "1" />
<flag name = "stateHidden" value = "2" />
<flag name = "stateAlwaysHidden" value = "3" />
<flag name = "stateVisible" value = "4" />
<flag name = "stateAlwaysVisible" value = "5" />
<flag name = "adjustUnspecified" value = "0x00" />
<flag name = "adjustResize" value = "0x10" />
<flag name = "adjustPan" value = "0x20" />
<flag name = "adjustNothing" value = "0x30" />
</attr>
</declare-styleable>
(2)屬性使用:
<activity
android:name = ".StyleAndThemeActivity"
android:label = "@string/app_name"
android:windowSoftInputMode = "stateUnspecified |stateUnchanged | stateHidden">
<intent-filter>
<action android:name = "android.intent.action.MAIN" />
<category android:name ="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
特別要注意:
屬性定義時(shí)可以指定多種類型值沟使。
(1)屬性定義:
<declare-styleable name = "名稱">
<attr name = "background" format = "reference|color" />
</declare-styleable>
(2)屬性使用:
<ImageView
android:layout_width = "42dip"
android:layout_height = "42dip"
android:background = "@drawable/圖片ID|#00FF00"
/>
下面說說AttributeSet與TypedArray在自定義控件中的作用:
AttributeSet的作用就是在控件進(jìn)行初始化的時(shí)候委可,解析布局文件中該控件的屬性(key eg:background)與該值(value eg:@drawable/icon)的信息封裝在AttributeSet中,傳遞給該控件(View)的構(gòu)造函數(shù)。對于非Android自帶的屬性着倾,在View類中處理時(shí)是無法識別的拾酝,因此需要我們自己解析。所以這就要用到另外一個(gè)類TypedArray卡者。在AttributeSet中我們有屬性名稱蒿囤,有屬性值,但是控件如何知道哪個(gè)屬性代表什么意思呢崇决?這個(gè)工作就由TypedArray來做了材诽。TypedArray對象封裝了/values/attrs.xml中的styleable里定義的每個(gè)屬性的類型信息,通過TypedArray我們就可以知道AttributeSet中封裝的值到底是干什么的了恒傻,從而可以對這些數(shù)據(jù)進(jìn)行應(yīng)用脸侥。
AttributeSet就相當(dāng)于一盒糖,TypedArray就相當(dāng)于這盒糖上的標(biāo)簽說明盈厘,告訴用戶每個(gè)糖的口味等睁枕。這盒糖有什么口味是由用戶自己的styleable文件里面的內(nèi)容來決定的。
五沸手、練習(xí)
在 res/values下創(chuàng)建文件attrs.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="Options">
<attr name="titleText" format="string" localization="suggested" />
<attr name="valueColor" format="color" />
</declare-styleable>
</resources>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:custom="http://schemas.android.com/apk/res/com.vogella.android.view.compoundview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:showDividers="middle"
android:divider="?android:attr/listDivider"
tools:context=".MainActivity" >
<com.vogella.android.view.compoundview.ColorOptionsView
android:id="@+id/view1"
android:layout_width="match_parent"
android:layout_height="?android:attr/listPreferredItemHeight"
android:background="?android:selectableItemBackground"
android:onClick="onClicked"
custom:titleText="Background color"
custom:valueColor="@android:color/holo_green_light"
/>
<com.vogella.android.view.compoundview.ColorOptionsView
android:id="@+id/view2"
android:layout_width="match_parent"
android:layout_height="?android:attr/listPreferredItemHeight"
android:background="?android:selectableItemBackground"
android:onClick="onClicked"
custom:titleText="Foreground color"
custom:valueColor="@android:color/holo_orange_dark"
/>
</LinearLayout>
創(chuàng)建布局view_color_options.xml
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android" >
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_centerVertical="true"
android:layout_marginLeft="16dp"
android:textSize="18sp"
/>
<View
android:layout_width="26dp"
android:layout_height="26dp"
android:layout_centerVertical="true"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
/>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="16dp"
android:layout_centerVertical="true"
android:visibility="gone"
/>
</merge>
package com.vogella.android.customview.compoundview;
import com.vogella.android.view.compoundview.R;
import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
public class ColorOptionsView extends LinearLayout {
private View mValue;
private ImageView mImage;
public ColorOptionsView(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray a = context.obtainStyledAttributes(attrs,
R.styleable.ColorOptionsView, 0, 0);
String titleText = a.getString(R.styleable.ColorOptionsView_titleText);
int valueColor = a.getColor(R.styleable.ColorOptionsView_valueColor,
android.R.color.holo_blue_light);
a.recycle();
setOrientation(LinearLayout.HORIZONTAL);
setGravity(Gravity.CENTER_VERTICAL);
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(R.layout.view_color_options, this, true);
TextView title = (TextView) getChildAt(0);
title.setText(titleText);
mValue = getChildAt(1);
mValue.setBackgroundColor(valueColor);
mImage = (ImageView) getChildAt(2);
}
public ColorOptionsView(Context context) {
this(context, null);
}
public void setValueColor(int color) {
mValue.setBackgroundColor(color);
}
public void setImageVisible(boolean visible) {
mImage.setVisibility(visible ? View.VISIBLE : View.GONE);
}
}
package com.vogella.android.customview.compoundview;
import com.vogella.android.view.compoundview.R;
import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
import android.view.View;
import android.widget.Toast;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.activity_main, menu);
return true;
}
public void onClicked(View view) {
String text = view.getId() == R.id.view1 ? "Background" : "Foreground";
Toast.makeText(this, text, Toast.LENGTH_SHORT).show();
}
}
-----------------------華麗的分割線---------------------
最后附上ViewConfiguration 類的源碼
import android.app.AppGlobals;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Point;
import android.os.RemoteException;
import android.provider.Settings;
import android.util.DisplayMetrics;
import android.util.SparseArray;
/**
* 主要用來獲取一些在UI中所使用到的標(biāo)準(zhǔn)常量外遇,像超時(shí)、尺寸契吉、距離
*/
public class ViewConfiguration {
/**
* 定義了水平滾動(dòng)條的寬度和垂直滾動(dòng)條的高度跳仿,單位是dip
*/
private static final int SCROLL_BAR_SIZE = 10;
/**
* 滾動(dòng)條褪去所需要經(jīng)歷的時(shí)間,單位:milliseconds
*/
private static final int SCROLL_BAR_FADE_DURATION = 250;
/**
* 滾動(dòng)條褪去之前的默認(rèn)時(shí)間延遲捐晶,單位:milliseconds
*/
private static final int SCROLL_BAR_DEFAULT_DELAY = 300;
/**
* 定義褪去邊緣的長度塔嬉,單位:dip
*/
private static final int FADING_EDGE_LENGTH = 12;
/**
* 按下狀態(tài)在子控件上的持續(xù)時(shí)間,單位:milliseconds
*/
private static final int PRESSED_STATE_DURATION = 64;
/**
* 定義一個(gè)按下狀態(tài)轉(zhuǎn)變成長按狀態(tài)所需要持續(xù)的時(shí)間租悄,單位:milliseconds
*/
private static final int DEFAULT_LONG_PRESS_TIMEOUT = 500;
/**
* 定義連續(xù)重復(fù)按鍵間的時(shí)間延遲谨究,單位:milliseconds
*/
private static final int KEY_REPEAT_DELAY = 50;
/**
* 如果用戶需要觸發(fā)全局對話框,例如:關(guān)機(jī)泣棋,鎖屏等胶哲,需要按下按鈕所持續(xù)的事件,單位:milliseconds
*/
private static final int GLOBAL_ACTIONS_KEY_TIMEOUT = 500;
/**
* 定義一個(gè)觸摸事件是點(diǎn)擊還是滾動(dòng)的事件間隔潭辈,如果在這個(gè)事件內(nèi)沒有移動(dòng)鸯屿,就認(rèn)為這是一個(gè)點(diǎn)擊,否則就是滾動(dòng)把敢,單位:milliseconds
*/
private static final int TAP_TIMEOUT = 180;
/**
* 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.
*/
private static final int JUMP_TAP_TIMEOUT = 500;
/**
* 定義雙擊的時(shí)間間隔寄摆,如果在這個(gè)時(shí)間內(nèi),就認(rèn)為是雙擊
*/
private static final int DOUBLE_TAP_TIMEOUT = 300;
/**
* 定義雙擊最小的時(shí)間間隔
*/
private static final int DOUBLE_TAP_MIN_TIME = 40;
/**
* 定義一個(gè)觸摸板觸摸到釋放可認(rèn)為是一個(gè)點(diǎn)擊事件而不是一個(gè)觸摸移動(dòng)手勢的最大時(shí)間修赞,
* 也就是說在這個(gè)時(shí)間內(nèi)進(jìn)行一次觸摸和釋放操作就可以認(rèn)為是一次點(diǎn)擊事件婶恼,單位:milliseconds
*/
private static final int HOVER_TAP_TIMEOUT = 150;
/**
* 定義一個(gè)觸摸板在觸摸釋放之前可以移動(dòng)的最大距離桑阶,
* 如果在這個(gè)距離之內(nèi)就可以認(rèn)為是一個(gè)點(diǎn)擊事件,否則就是一個(gè)移動(dòng)手勢勾邦,單位:pixels
*/
private static final int HOVER_TAP_SLOP = 20;
/**
* 定義響應(yīng)顯示縮放控制的時(shí)間
*/
private static final int ZOOM_CONTROLS_TIMEOUT = 3000;
/**
* Inset in dips to look for touchable content when the user touches the edge of the screen
*/
private static final int EDGE_SLOP = 12;
/**
* 如果我們認(rèn)為用戶正在滾動(dòng)蚣录,這里定義一個(gè)觸摸事件可以滾動(dòng)的距離,單位:dips
* 注意:這個(gè)值在這里定義只是作為那些沒有提供上下文Context來決定密度和配置相關(guān)值的應(yīng)用程序的一個(gè)備用值眷篇。
*/
private static final int TOUCH_SLOP = 8;
/**
* 定義雙擊事件之間可以移動(dòng)的距離萎河,單位:dips
*/
private static final int DOUBLE_TAP_TOUCH_SLOP = TOUCH_SLOP;
/**
* 定義用戶嘗試翻頁滾動(dòng)的觸摸移動(dòng)距離,單位:dips
*
* 注意:這個(gè)值在這里定義只是作為那些沒有提供上下文Context來決定密度和配置相關(guān)值的應(yīng)用程序的一個(gè)備用值蕉饼。
*
*/
private static final int PAGING_TOUCH_SLOP = TOUCH_SLOP * 2;
/**
* 定義第一次點(diǎn)擊和第二次點(diǎn)擊可以認(rèn)為是一次雙擊之間的距離虐杯。單位:dips
*/
private static final int DOUBLE_TAP_SLOP = 100;
/**
* Distance in dips 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;
/**
* 一個(gè)fling最小的速度,單位:dips/s
*/
private static final int MINIMUM_FLING_VELOCITY = 50;
/**
* 一個(gè)fling最大的速度昧港,單位:dips/s
*/
private static final int MAXIMUM_FLING_VELOCITY = 8000;
/**
* 分發(fā)一個(gè)重復(fù)訪問事件的延遲事件擎椰,單位:milliseconds
*/
private static final long SEND_RECURRING_ACCESSIBILITY_EVENTS_INTERVAL_MILLIS = 100;
/**
* The maximum size of View's drawing cache, expressed in bytes. This size
* should be at least equal to the size of the screen in ARGB888 format.
*/
@Deprecated
private static final int MAXIMUM_DRAWING_CACHE_SIZE = 480 * 800 * 4; // ARGB8888
/**
* 滾動(dòng)和滑動(dòng)的摩擦系數(shù)
*/
private static final float SCROLL_FRICTION = 0.015f;
/**
* Max distance in dips to overscroll for edge effects
*/
private static final int OVERSCROLL_DISTANCE = 0;
/**
* Max distance in dips to overfling for edge effects
*/
private static final int OVERFLING_DISTANCE = 6;
private final int mEdgeSlop;
private final int mFadingEdgeLength;
private final int mMinimumFlingVelocity;
private final int mMaximumFlingVelocity;
private final int mScrollbarSize;
private final int mTouchSlop;
private final int mDoubleTapTouchSlop;
private final int mPagingTouchSlop;
private final int mDoubleTapSlop;
private final int mWindowTouchSlop;
private final int mMaximumDrawingCacheSize;
private final int mOverscrollDistance;
private final int mOverflingDistance;
private final boolean mFadingMarqueeEnabled;
private boolean sHasPermanentMenuKey;
private boolean sHasPermanentMenuKeySet;
static final SparseArray<ViewConfiguration> sConfigurations =
new SparseArray<ViewConfiguration>(2);
/**
* 這個(gè)方法被廢除了,使用ViewConfiguration.get(Context)}替代
*/
@Deprecated
public ViewConfiguration() {
mEdgeSlop = EDGE_SLOP;
mFadingEdgeLength = FADING_EDGE_LENGTH;
mMinimumFlingVelocity = MINIMUM_FLING_VELOCITY;
mMaximumFlingVelocity = MAXIMUM_FLING_VELOCITY;
mScrollbarSize = SCROLL_BAR_SIZE;
mTouchSlop = TOUCH_SLOP;
mDoubleTapTouchSlop = DOUBLE_TAP_TOUCH_SLOP;
mPagingTouchSlop = PAGING_TOUCH_SLOP;
mDoubleTapSlop = DOUBLE_TAP_SLOP;
mWindowTouchSlop = WINDOW_TOUCH_SLOP;
//noinspection deprecation
mMaximumDrawingCacheSize = MAXIMUM_DRAWING_CACHE_SIZE;
mOverscrollDistance = OVERSCROLL_DISTANCE;
mOverflingDistance = OVERFLING_DISTANCE;
mFadingMarqueeEnabled = true;
}
/**
* 使用給定的context來創(chuàng)建一個(gè)新的配置慨飘。這個(gè)配置依賴于context里面不同的參數(shù),例如顯示的尺寸或者密度
* @param context 用來初始化這個(gè)view配置的應(yīng)用上下文環(huán)境
*
* @see #get(android.content.Context)
* @see android.util.DisplayMetrics
*/
private ViewConfiguration(Context context) {
final Resources res = context.getResources();
final DisplayMetrics metrics = res.getDisplayMetrics();
final Configuration config = res.getConfiguration();
final float density = metrics.density;
final float sizeAndDensity;
if (config.isLayoutSizeAtLeast(Configuration.SCREENLAYOUT_SIZE_XLARGE)) {
sizeAndDensity = density * 1.5f;
} else {
sizeAndDensity = density;
}
mEdgeSlop = (int) (sizeAndDensity * EDGE_SLOP + 0.5f);
mFadingEdgeLength = (int) (sizeAndDensity * FADING_EDGE_LENGTH + 0.5f);
mMinimumFlingVelocity = (int) (density * MINIMUM_FLING_VELOCITY + 0.5f);
mMaximumFlingVelocity = (int) (density * MAXIMUM_FLING_VELOCITY + 0.5f);
mScrollbarSize = (int) (density * SCROLL_BAR_SIZE + 0.5f);
mDoubleTapSlop = (int) (sizeAndDensity * DOUBLE_TAP_SLOP + 0.5f);
mWindowTouchSlop = (int) (sizeAndDensity * WINDOW_TOUCH_SLOP + 0.5f);
// Size of the screen in bytes, in ARGB_8888 format
final WindowManager win = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
final Display display = win.getDefaultDisplay();
final Point size = new Point();
display.getRealSize(size);
mMaximumDrawingCacheSize = 4 * size.x * size.y;
mOverscrollDistance = (int) (sizeAndDensity * OVERSCROLL_DISTANCE + 0.5f);
mOverflingDistance = (int) (sizeAndDensity * OVERFLING_DISTANCE + 0.5f);
if (!sHasPermanentMenuKeySet) {
IWindowManager wm = WindowManagerGlobal.getWindowManagerService();
try {
sHasPermanentMenuKey = !wm.hasNavigationBar();
sHasPermanentMenuKeySet = true;
} catch (RemoteException ex) {
sHasPermanentMenuKey = false;
}
}
mFadingMarqueeEnabled = res.getBoolean(
com.android.internal.R.bool.config_ui_enableFadingMarquee);
mTouchSlop = res.getDimensionPixelSize(
com.android.internal.R.dimen.config_viewConfigurationTouchSlop);
mPagingTouchSlop = mTouchSlop * 2;
mDoubleTapTouchSlop = mTouchSlop;
}
/**
* 跟上面一個(gè)函數(shù)一樣译荞,只不過上面一個(gè)是創(chuàng)建一個(gè)ViewConfiguration對象瓤的,這里是直接通過這個(gè)靜態(tài)方法返回一個(gè)對象
*/
public static ViewConfiguration get(Context context) {
final DisplayMetrics metrics = context.getResources().getDisplayMetrics();
final int density = (int) (100.0f * metrics.density);
ViewConfiguration configuration = sConfigurations.get(density);
if (configuration == null) {
configuration = new ViewConfiguration(context);
sConfigurations.put(density, configuration);
}
return configuration;
}
/**
* @return 獲取水平滾動(dòng)條的寬帶和垂直滾動(dòng)條的高度
*
* 這個(gè)函數(shù)被廢除,使用getScaledScrollBarSize()來代替
*/
@Deprecated
public static int getScrollBarSize() {
return SCROLL_BAR_SIZE;
}
/**
* @return 獲取水平滾動(dòng)條的寬帶和垂直滾動(dòng)條的高度
*/
public int getScaledScrollBarSize() {
return mScrollbarSize;
}
/**
* @return 滾動(dòng)條褪去的持續(xù)時(shí)間
*/
public static int getScrollBarFadeDuration() {
return SCROLL_BAR_FADE_DURATION;
}
/**
* @return 滾動(dòng)條褪去的延遲時(shí)間
*/
public static int getScrollDefaultDelay() {
return SCROLL_BAR_DEFAULT_DELAY;
}
/**
* @return 褪去邊緣的長度
*
* 這個(gè)方法已經(jīng)廢棄吞歼,用getScaledFadingEdgeLength()替代.
*/
@Deprecated
public static int getFadingEdgeLength() {
return FADING_EDGE_LENGTH;
}
/**
* @return 褪去邊緣的長度圈膏,單位:pixels
*/
public int getScaledFadingEdgeLength() {
return mFadingEdgeLength;
}
/**
* @return 在子控件上按住狀態(tài)的持續(xù)時(shí)間
*/
public static int getPressedStateDuration() {
return PRESSED_STATE_DURATION;
}
/**
* @return 按住狀態(tài)轉(zhuǎn)變?yōu)殚L按狀態(tài)需要的時(shí)間
*/
public static int getLongPressTimeout() {
return AppGlobals.getIntCoreSetting(Settings.Secure.LONG_PRESS_TIMEOUT,
DEFAULT_LONG_PRESS_TIMEOUT);
}
/**
* @return 重新按鍵時(shí)間
*/
public static int getKeyRepeatTimeout() {
return getLongPressTimeout();
}
/**
* @return 重復(fù)按鍵延遲時(shí)間
*/
public static int getKeyRepeatDelay() {
return KEY_REPEAT_DELAY;
}
/**
* @return 判斷用戶是單擊還是滾動(dòng)的時(shí)間,在這個(gè)時(shí)間內(nèi)沒有移動(dòng)則是單擊篙骡,否則是滾動(dòng)
*/
public static int getTapTimeout() {
return TAP_TIMEOUT;
}
/**
* @return the duration in milliseconds we will wait to see if a touch event
* is a jump tap. If the user does not move within this interval, it is
* considered to be a tap.
*/
public static int getJumpTapTimeout() {
return JUMP_TAP_TIMEOUT;
}
/**
* @return 得到雙擊間隔時(shí)間稽坤,在這個(gè)時(shí)間內(nèi),則是雙擊糯俗,否則就是單擊
*/
public static int getDoubleTapTimeout() {
return DOUBLE_TAP_TIMEOUT;
}
/**
* @return the minimum duration in milliseconds between the first tap's
* up event and the second tap's down event for an interaction to be considered a
* double-tap.
*
* @hide
*/
public static int getDoubleTapMinTime() {
return DOUBLE_TAP_MIN_TIME;
}
/**
* @return the maximum duration in milliseconds between a touch pad
* touch and release for a given touch to be considered a tap (click) as
* opposed to a hover movement gesture.
* @hide
*/
public static int getHoverTapTimeout() {
return HOVER_TAP_TIMEOUT;
}
/**
* @return the maximum distance in pixels that a touch pad touch can move
* before being released for it to be considered a tap (click) as opposed
* to a hover movement gesture.
* @hide
*/
public static int getHoverTapSlop() {
return HOVER_TAP_SLOP;
}
/**
* @return Inset in dips to look for touchable content when the user touches the edge of the
* screen
*
* @deprecated Use {@link #getScaledEdgeSlop()} instead.
*/
@Deprecated
public static int getEdgeSlop() {
return EDGE_SLOP;
}
/**
* @return Inset in pixels to look for touchable content when the user touches the edge of the
* screen
*/
public int getScaledEdgeSlop() {
return mEdgeSlop;
}
/**
* @return Distance in dips a touch can wander before we think the user is scrolling
*
* @deprecated Use {@link #getScaledTouchSlop()} instead.
*/
@Deprecated
public static int getTouchSlop() {
return TOUCH_SLOP;
}
/**
* @return Distance in pixels a touch can wander before we think the user is scrolling
*/
public int getScaledTouchSlop() {
return mTouchSlop;
}
/**
* @return Distance in pixels the first touch can wander before we do not consider this a
* potential double tap event
* @hide
*/
public int getScaledDoubleTapTouchSlop() {
return mDoubleTapTouchSlop;
}
/**
* @return Distance in pixels a touch can wander before we think the user is scrolling a full
* page
*/
public int getScaledPagingTouchSlop() {
return mPagingTouchSlop;
}
/**
* @return Distance in dips between the first touch and second touch to still be
* considered a double tap
* @deprecated Use {@link #getScaledDoubleTapSlop()} instead.
* @hide The only client of this should be GestureDetector, which needs this
* for clients that still use its deprecated constructor.
*/
@Deprecated
public static int getDoubleTapSlop() {
return DOUBLE_TAP_SLOP;
}
/**
* @return Distance in pixels between the first touch and second touch to still be
* considered a double tap
*/
public int getScaledDoubleTapSlop() {
return mDoubleTapSlop;
}
/**
* Interval for dispatching a recurring accessibility event in milliseconds.
* This interval guarantees that a recurring event will be send at most once
* during the {@link #getSendRecurringAccessibilityEventsInterval()} time frame.
*
* @return The delay in milliseconds.
*
* @hide
*/
public static long getSendRecurringAccessibilityEventsInterval() {
return SEND_RECURRING_ACCESSIBILITY_EVENTS_INTERVAL_MILLIS;
}
/**
* @return Distance in dips a touch must be outside the bounds of a window for it
* to be counted as outside the window for purposes of dismissing that
* window.
*
* @deprecated Use {@link #getScaledWindowTouchSlop()} instead.
*/
@Deprecated
public static int getWindowTouchSlop() {
return WINDOW_TOUCH_SLOP;
}
/**
* @return Distance in pixels a touch must be outside the bounds of a window for it
* to be counted as outside the window for purposes of dismissing that window.
*/
public int getScaledWindowTouchSlop() {
return mWindowTouchSlop;
}
/**
* @return Minimum velocity to initiate a fling, as measured in dips per second.
*
* @deprecated Use {@link #getScaledMinimumFlingVelocity()} instead.
*/
@Deprecated
public static int getMinimumFlingVelocity() {
return MINIMUM_FLING_VELOCITY;
}
/**
* @return 得到滑動(dòng)的最小速度, 以像素/每秒來進(jìn)行計(jì)算
*/
public int getScaledMinimumFlingVelocity() {
return mMinimumFlingVelocity;
}
/**
* @return Maximum velocity to initiate a fling, as measured in dips per second.
*
* @deprecated Use {@link #getScaledMaximumFlingVelocity()} instead.
*/
@Deprecated
public static int getMaximumFlingVelocity() {
return MAXIMUM_FLING_VELOCITY;
}
/**
* @return 得到滑動(dòng)的最大速度, 以像素/每秒來進(jìn)行計(jì)算
*/
public int getScaledMaximumFlingVelocity() {
return mMaximumFlingVelocity;
}
/**
* The maximum drawing cache size expressed in bytes.
*
* @return the maximum size of View's drawing cache expressed in bytes
*
* @deprecated Use {@link #getScaledMaximumDrawingCacheSize()} instead.
*/
@Deprecated
public static int getMaximumDrawingCacheSize() {
//noinspection deprecation
return MAXIMUM_DRAWING_CACHE_SIZE;
}
/**
* The maximum drawing cache size expressed in bytes.
*
* @return the maximum size of View's drawing cache expressed in bytes
*/
public int getScaledMaximumDrawingCacheSize() {
return mMaximumDrawingCacheSize;
}
/**
* @return The maximum distance a View should overscroll by when showing edge effects (in
* pixels).
*/
public int getScaledOverscrollDistance() {
return mOverscrollDistance;
}
/**
* @return The maximum distance a View should overfling by when showing edge effects (in
* pixels).
*/
public int getScaledOverflingDistance() {
return mOverflingDistance;
}
/**
* The amount of time that the zoom controls should be
* displayed on the screen expressed in milliseconds.
*
* @return the time the zoom controls should be visible expressed
* in milliseconds.
*/
public static long getZoomControlsTimeout() {
return ZOOM_CONTROLS_TIMEOUT;
}
/**
* The amount of time a user needs to press the relevant key to bring up
* the global actions dialog.
*
* @return how long a user needs to press the relevant key to bring up
* the global actions dialog.
*/
public static long getGlobalActionKeyTimeout() {
return GLOBAL_ACTIONS_KEY_TIMEOUT;
}
/**
* The amount of friction applied to scrolls and flings.
*
* @return A scalar dimensionless value representing the coefficient of
* friction.
*/
public static float getScrollFriction() {
return SCROLL_FRICTION;
}
/**
* Report if the device has a permanent menu key available to the user.
*
* <p>As of Android 3.0, devices may not have a permanent menu key available.
* Apps should use the action bar to present menu options to users.
* However, there are some apps where the action bar is inappropriate
* or undesirable. This method may be used to detect if a menu key is present.
* If not, applications should provide another on-screen affordance to access
* functionality.
*
* @return true if a permanent menu key is present, false otherwise.
*/
public boolean hasPermanentMenuKey() {
return sHasPermanentMenuKey;
}
/**
* @hide
* @return Whether or not marquee should use fading edges.
*/
public boolean isFadingMarqueeEnabled() {
return mFadingMarqueeEnabled;
}
}