美文网首页
View基本知识

View基本知识

作者: 30cf443c3643 | 来源:发表于2018-10-10 15:26 被阅读11次

    在Android的视图控件中,有简单的文本TextView和按钮Button,还有复杂的RelativeLayout和ListView。但是不管是简单的View还是复杂的ViewGroup控件,他们的共同基类都是View。View和ViewGroup是一个非常经典的组合模式的关系,类似文件和文件夹的关系。那为什么ViewGroup有容器的功能呢?


    2018-10-08_153947.png

    可以看到ViewGroup实现了ViewManager接口,而ViewManager定义了addView,removeView等操作视图的方法,ViewParent则定义了刷新容器等方法。另外ViewGroup的onLayout是一个空实现,所以它的子类要自己去实现,例如LinearLayout

        @Override
        protected void onLayout(boolean changed, int l, int t, int r, int b) {
            if (mOrientation == VERTICAL) {
                layoutVertical(l, t, r, b);
            } else {
                layoutHorizontal(l, t, r, b);
            }
        }
    

    此外onMeasure和onDraw方法,ViewGroup都没有重写,只是增加了计算子View的方法,dispatchDraw方法中调用子view的onDraw方法。由此构成了一个控件树。


    687.jpg

    屏幕坐标系

    屏幕坐标系 屏幕左上角为原点,x轴向右为正,y轴向下为负
    view 的getTop()、getBottom(),getLeft(),getRight()指的的是相对于父容器的数值。


    005Xtdi2gw1f1qzqwvkkbj308c0dwgm9.jpg

    还有getWidth,getHeight。有一点要特别注意view的这几个值在Activity的onCreate和onStart里返回的都是0。因为view的测量measure过程跟activity的生命周期并不是同步的。如果View的Visible初始状态是gone,getWidth跟getHeight得到的数值为0;

    MotionEvent

    在view的事件体系里,我们可以通过MotionEvent得到点击事件发生的相对当前view的xy坐标以及相对屏幕的绝对坐标xy

    005Xtdi2jw1f1r2bdlqhbj308c0dwwew.jpg

    getLocationInWindow 和 getLocationOnScreen

    2018-09-07_160917.png

    还特意去验证了下,控件C在屏幕A所在的窗口里 getLocationInWindow和getLocationOnScreen数值一样。例如,c在dialog里就有差别了。但是在我把控件C改为gone状态时候,它的x为0,y的值却为210?

        public void getLocationOnScreen(@Size(2) int[] outLocation) {
            getLocationInWindow(outLocation);
    
            final AttachInfo info = mAttachInfo;
            if (info != null) {
                outLocation[0] += info.mWindowLeft;
                outLocation[1] += info.mWindowTop;
            }
        }
    

    ViewConfiguration

    这个类主要定义了UI中所使用到的标准常量,像超时、尺寸、距离,通过ViewConfiguration.get(context)获得实例。常用的方法有

    vc.getScaledTouchSlop() 系统能识别的被认为是滑动的最小距离

    其他还有

    // 获得允许执行fling (抛)的最大速度值
    int maximumVelocity = viewConfiguration.getScaledMaximumFlingVelocity();
    需要配合VelocityTracker速度追踪来使用

    在onTouchEvent中追踪当前点击事件的速度

        private void acquireVelocityTracker(final MotionEvent event) {
            if (null == mVelocityTracker) {
                mVelocityTracker = VelocityTracker.obtain();
            }
            mVelocityTracker.addMovement(event);
        }
    

    当我们想知道当前的滑动速度时

    mVelocityTracker.computeCurrentVelocity(1000); 单位是毫秒
    然后得到
    float y = mVelocityTracker.getYVelocity();//getYVelocity前必须调用computeCurrentVelocity
    表示1000毫秒内划过的像素数 比如10个像素,那速度就是10像素/1000毫秒
    

    最后重置回收 clear() recycle()
    ViewConfiguration api

    static float getScrollFriction()
    一个代表了摩擦系数的标量。它应用在flings 或 scrolls 状态。mScroll.setFriction

    static int getDoubleTapTimeout()
    返回一个 双击的毫秒超时时间。
    它在第一次点击的up事件开始记录,在第二次点击的down事件停止。
    两次点击之间的时间值小于等于getDoubleTapTimeout(),就表示是一个双击操作

    View的生命周期

    它的生命周期会随着Activity的生命周期进行变化,掌握View的生命周期对我们自定义View有着重要的意义。

    view_lifecycle.png
    图片来自beesAndroid下的视图框架view

    LayoutParams

    LayoutParams是什么?

    Every single ViewGroup (e.g. LinearLayout , RelativeLayout , CoordinatorLayout , etc.) needs to store information
    about its children's properties. About the way its children are being laid out in the ViewGroup . This information is
    stored in objects of a wrapper class ViewGroup.LayoutParams .
    

    LayoutParams继承于Android.View.ViewGroup.LayoutParams相当于一个Layout的信息包,它封装了Layout的位置、高、宽等信息。假设在屏幕上一块区域是由一个Layout占领的,如果将一个View添加到一个Layout中,最好告诉Layout用户期望的布局方式,也就是将一个认可的layoutParams传递进去。

    Most of ViewGroups reutilize the ability to set margins for their children, so they do not subclass
    ViewGroup.LayoutParams directly, but they subclass ViewGroup.MarginLayoutParams instead (which itself is a
    subclass of ViewGroup.LayoutParams ).
    

    大多数ViewGroup具有为children设置外边距的能力,所以他们不是ViewGroup.LayoutParams的直接子类,而是MarginLayoutParams的子类
    可以查看LinearLayout.LayoutParams的源代码

    public static class LayoutParams extends ViewGroup.MarginLayoutParams { ...}
    

    getLayoutParams

    Because the LayoutParams object is directly related to the enclosing ViewGroup , this method will return a non-null
    value only when View is attached to the ViewGroup . You need to bare in mind that this object might not be present
    at all times. Especially you should not depend on having it inside View's constructor.
    

    因为LayoutParams跟封闭的ViewGroup直接相关,getLayoutParams可能返回一个null值。必须牢记,只有当View依附到ViewGroup时才能使用。尤其不能在View的构造函数中使用

    错误代码

    public class ExampleView extends View {
       public ExampleView(Context context) {
          super(context);
          setupView(context);
    }
       public ExampleView(Context context, AttributeSet attrs) {
           super(context, attrs);
           setupView(context);
    }
       public ExampleView(Context context, AttributeSet attrs, int defStyle) {
           super(context, attrs, defStyle);
           setupView(context);
      } 
       private void setupView(Context context) {
           if (getLayoutParams().height == 50){ // DO NOT DO THIS!
          // This might produce NullPointerException
                doSomething();
          }
       }
    //...
    }
    

    而是在onAttachedToWindow中,跟得到view的宽高是一样的

    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();
        if (getLayoutParams().height == 50) { // getLayoutParams() will NOT return null here
              doSomething();
       }
    }
    

    getLayoutParams的正确示范

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
       android:id="@+id/outer_layout"
       android:layout_width="match_parent"
       android:layout_height="match_parent"
       android:orientation="vertical">
          <FrameLayout
               android:id="@+id/inner_layout"
              android:layout_width="match_parent"
              android:layout_height="50dp"
              android:layout_gravity="right"/>
    </LinearLayout>
    

    正确

    FrameLayout innerLayout = (FrameLayout)findViewById(R.id.inner_layout);
    LinearLayout.LayoutParams par = (LinearLayout.LayoutParams) innerLayout.getLayoutParams();
    // CORRECT! the enclosing layout is a LinearLayout
    

    错误

    FrameLayout innerLayout = (FrameLayout)findViewById(R.id.inner_layout);
    FrameLayout.LayoutParams par = (FrameLayout.LayoutParams) innerLayout.getLayoutParams();
    // INCORRECT! This will produce ClassCastException
    

    相关文章

      网友评论

          本文标题:View基本知识

          本文链接:https://www.haomeiwen.com/subject/erhjaftx.html