android基础之自定义view

一、Custom View

1、view的继承关系

view继承关系.png

2、Android 如何绘制试图层次

当activity获取焦点时,它必须提供layout层次的根节点,然后android 系统开始视图的绘制过程。绘制是从layout的根节点开始的,按照从上往下的顺序,父元素优先子元素。
绘制的两个过程:

  • measuring pass:实现measure(int,int)方法,顺序也是从上往下,每个view保存它自己的测量值
  • layout pass:实现layout(int,int,int,int)方法,顺序从上往下,在这个阶段每个layout manager负责他们各自所有子元素的位置,通过上一步测量的值
    测量和绘制过程是交替进行的,layout manager可能运行 measure pass 若干次。例如 linearlayout需要支持weight属性,relativelayout需要测量子节点多次才能确定约束关系。
    view或activity可以再次触发测量和绘制过程。通过 requestLayout()
    在测量和布局计算完成后,视图就开始绘制自己。这个操作通过invalidate()触发。

3、view 截屏

每个view都支持创建当前显示状态的图片。

# Build the Drawing Cache
view.buildDrawingCache(); 
 
# Create Bitmap
Bitmap cache = view.getDrawingCache();
 
# Save Bitmap 
saveBitmap(cache);
view.destroyDrawingCache();

二、自定义view

1、创建自定义view

通过继承view或它的子类,可以创建自定义view
通过onDraw()方法绘制视图,如果需要重新绘制,调用invalidate()触发onDraw()
如果定义自己的view,确保参考ViewConfiguration 类,它包含了一些常亮定义

2、测量

必须调用 setMeasuredDimenstion(int,int)设置结果

3、定义自定义 layout managers

通过继承ViewGroup
自定义layout manager 可以重写 onMeasure() 和 onLayout(),并且计算孩子元素的测量结果
测量孩子元素的大小通过measureChildWithMargins();

三、生命周期

一个视图会在它依附到一个已依附到window的布局结构时显示。

  • onAttachedToWindow() ,当window可以时调用
  • onDetachedFromWindow(),当视图从父元素中移除时调用(父元素必须依附到window)。例如当
    activity被回收(finish()方法被调用)或者视图在listview中被回收。该方法可以用来停止动画和清理资源

四、定义自定义属性

<?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

  1. 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"

                     />
  1. 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"

                     />
  1. 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"
                    />
  1. dimension:尺寸值。

    (1)属性定义:

            <declare-styleable name = "名称">

                   <attr name = "layout_width" format = "dimension" />

            </declare-styleable>
(2)属性使用:
            <Button

                    android:layout_width = "42dip"
                    android:layout_height = "42dip"

                    />
  1. float:浮点值。

    (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"

                   />
  1. 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"

                   />
  1. 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"

                    />
  1. fraction:百分数。

    (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/动画ID"
                   android:fromDegrees = "0"
               android:toDegrees = "360"
                   android:pivotX = "200%"
                   android:pivotY = "300%"
               android:duration = "5000"
                   android:repeatMode = "restart"
                   android:repeatCount = "infinite"
                   />
  1. 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>
  1. flag:位或运算。

    (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>

特别要注意:

 属性定义时可以指定多种类型值。

(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的作用就是在控件进行初始化的时候,解析布局文件中该控件的属性(key eg:background)与该值(value eg:@drawable/icon)的信息封装在AttributeSet中,传递给该控件(View)的构造函数。对于非Android自带的属性,在View类中处理时是无法识别的,因此需要我们自己解析。所以这就要用到另外一个类TypedArray。在AttributeSet中我们有属性名称,有属性值,但是控件如何知道哪个属性代表什么意思呢?这个工作就由TypedArray来做了。TypedArray对象封装了/values/attrs.xml中的styleable里定义的每个属性的类型信息,通过TypedArray我们就可以知道AttributeSet中封装的值到底是干什么的了,从而可以对这些数据进行应用。

AttributeSet就相当于一盒糖,TypedArray就相当于这盒糖上的标签说明,告诉用户每个糖的口味等。这盒糖有什么口味是由用户自己的styleable文件里面的内容来决定的。

五、练习

在 res/values下创建文件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> 

创建布局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中所使用到的标准常量,像超时、尺寸、距离
 */
public class ViewConfiguration {
    /**
     * 定义了水平滚动条的宽度和垂直滚动条的高度,单位是dip
     */
    private static final int SCROLL_BAR_SIZE = 10;

    /**
     * 滚动条褪去所需要经历的时间,单位:milliseconds
     */
    private static final int SCROLL_BAR_FADE_DURATION = 250;

    /**
     * 滚动条褪去之前的默认时间延迟,单位:milliseconds
     */
    private static final int SCROLL_BAR_DEFAULT_DELAY = 300;

    /**
     * 定义褪去边缘的长度,单位:dip
     */
    private static final int FADING_EDGE_LENGTH = 12;

    /**
     * 按下状态在子控件上的持续时间,单位:milliseconds
     */
    private static final int PRESSED_STATE_DURATION = 64;

    /**
     * 定义一个按下状态转变成长按状态所需要持续的时间,单位:milliseconds
     */
    private static final int DEFAULT_LONG_PRESS_TIMEOUT = 500;

    /**
     * 定义连续重复按键间的时间延迟,单位:milliseconds
     */
    private static final int KEY_REPEAT_DELAY = 50;

    /**
     * 如果用户需要触发全局对话框,例如:关机,锁屏等,需要按下按钮所持续的事件,单位:milliseconds
     */
    private static final int GLOBAL_ACTIONS_KEY_TIMEOUT = 500;

    /**
     * 定义一个触摸事件是点击还是滚动的事件间隔,如果在这个事件内没有移动,就认为这是一个点击,否则就是滚动,单位: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;

    /**
     * 定义双击的时间间隔,如果在这个时间内,就认为是双击
     */
    private static final int DOUBLE_TAP_TIMEOUT = 300;

    /**
     * 定义双击最小的时间间隔
     */
    private static final int DOUBLE_TAP_MIN_TIME = 40;

    /**
     * 定义一个触摸板触摸到释放可认为是一个点击事件而不是一个触摸移动手势的最大时间,
     * 也就是说在这个时间内进行一次触摸和释放操作就可以认为是一次点击事件,单位:milliseconds
     */
    private static final int HOVER_TAP_TIMEOUT = 150;

    /**
     * 定义一个触摸板在触摸释放之前可以移动的最大距离,
     * 如果在这个距离之内就可以认为是一个点击事件,否则就是一个移动手势,单位:pixels
     */
    private static final int HOVER_TAP_SLOP = 20;

    /**
     * 定义响应显示缩放控制的时间
     */
    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;

    /**
     * 如果我们认为用户正在滚动,这里定义一个触摸事件可以滚动的距离,单位:dips
     * 注意:这个值在这里定义只是作为那些没有提供上下文Context来决定密度和配置相关值的应用程序的一个备用值。
     */
    private static final int TOUCH_SLOP = 8;

    /**
     * 定义双击事件之间可以移动的距离,单位:dips
     */
    private static final int DOUBLE_TAP_TOUCH_SLOP = TOUCH_SLOP;

    /**
     * 定义用户尝试翻页滚动的触摸移动距离,单位:dips
     *
     * 注意:这个值在这里定义只是作为那些没有提供上下文Context来决定密度和配置相关值的应用程序的一个备用值。
     *
     */
    private static final int PAGING_TOUCH_SLOP = TOUCH_SLOP * 2;

    /**
     * 定义第一次点击和第二次点击可以认为是一次双击之间的距离。单位: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;

    /**
     * 一个fling最小的速度,单位:dips/s
     */
    private static final int MINIMUM_FLING_VELOCITY = 50;

    /**
     * 一个fling最大的速度,单位:dips/s
     */
    private static final int MAXIMUM_FLING_VELOCITY = 8000;

    /**
     * 分发一个重复访问事件的延迟事件,单位: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

    /**
     * 滚动和滑动的摩擦系数
     */
    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);

    /**
     * 这个方法被废除了,使用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来创建一个新的配置。这个配置依赖于context里面不同的参数,例如显示的尺寸或者密度
     * @param context 用来初始化这个view配置的应用上下文环境
     *
     * @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;
    }

    /**
     * 跟上面一个函数一样,只不过上面一个是创建一个ViewConfiguration对象,这里是直接通过这个静态方法返回一个对象
     */
    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 获取水平滚动条的宽带和垂直滚动条的高度
     *
     * 这个函数被废除,使用getScaledScrollBarSize()来代替
     */
    @Deprecated
    public static int getScrollBarSize() {
        return SCROLL_BAR_SIZE;
    }

    /**
     * @return 获取水平滚动条的宽带和垂直滚动条的高度
     */
    public int getScaledScrollBarSize() {
        return mScrollbarSize;
    }

    /**
     * @return 滚动条褪去的持续时间
     */
    public static int getScrollBarFadeDuration() {
        return SCROLL_BAR_FADE_DURATION;
    }

    /**
     * @return 滚动条褪去的延迟时间
     */
    public static int getScrollDefaultDelay() {
        return SCROLL_BAR_DEFAULT_DELAY;
    }

    /**
     * @return 褪去边缘的长度
     *
     * 这个方法已经废弃,用getScaledFadingEdgeLength()替代.
     */
    @Deprecated
    public static int getFadingEdgeLength() {
        return FADING_EDGE_LENGTH;
    }

    /**
     * @return 褪去边缘的长度,单位:pixels
     */
    public int getScaledFadingEdgeLength() {
        return mFadingEdgeLength;
    }

    /**
     * @return 在子控件上按住状态的持续时间
     */
    public static int getPressedStateDuration() {
        return PRESSED_STATE_DURATION;
    }

    /**
     * @return 按住状态转变为长按状态需要的时间
     */
    public static int getLongPressTimeout() {
        return AppGlobals.getIntCoreSetting(Settings.Secure.LONG_PRESS_TIMEOUT,
                DEFAULT_LONG_PRESS_TIMEOUT);
    }

    /**
     * @return 重新按键时间
     */
    public static int getKeyRepeatTimeout() {
        return getLongPressTimeout();
    }

    /**
     * @return 重复按键延迟时间
     */
    public static int getKeyRepeatDelay() {
        return KEY_REPEAT_DELAY;
    }

    /**
     * @return 判断用户是单击还是滚动的时间,在这个时间内没有移动则是单击,否则是滚动
     */
    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 得到双击间隔时间,在这个时间内,则是双击,否则就是单击
     */
    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 得到滑动的最小速度, 以像素/每秒来进行计算
     */
    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 得到滑动的最大速度, 以像素/每秒来进行计算
     */
    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;
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,491评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,856评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,745评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,196评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,073评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,112评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,531评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,215评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,485评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,578评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,356评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,215评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,583评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,898评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,497评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,697评论 2 335

推荐阅读更多精彩内容