美文网首页
View体系与自定义View 【1】

View体系与自定义View 【1】

作者: 做梦枯岛醒 | 来源:发表于2017-09-27 22:13 被阅读4次

    一.View

    View是庞大的,对于一个APP来说,与用户的交互、将内容展现给用户的部分是重要而且必要的,View也是微小的,屏幕上的一个小部分,都可能是一个View,而我们UI学习的第一步,也是从最简单的Textview,Button开始的,到后来,我们可以试着去写一些自定义的View来实现效果,这就是需要我们很好的掌握View的机制和工作流程。

    二.ViewGroup

    平时我们用的布局空间XXXLayout是继承自ViewGroup,那么这个ViewGroup可以理解为View的组合,他可以包含很多ViewGroup和View,这也是布局嵌套的本质。

    三.坐标系

    Android系统中有两种坐标系,Android坐标系和View坐标系。

    1.Android坐标系

    屏幕左上角为原点,原点向右为X正方向,向下是Y正方向

    2.View坐标系

    View的坐标系与Android的坐标系并不冲突,它们是共同存在的,它可以用来更好的控制View


    那么从图中我们可以看到计算一个View的宽和高的方法

      int width = button.getRight() - button.getLeft();
      int height = button.getBottom() - button.getTop();
    

    注意这个代码不能在onCreate里面写,因为当时View还没有做好初始化,是测量不到宽高的,其中一个方法如下

    
        @Override
        public void onWindowFocusChanged(boolean hasFocus) {
            super.onWindowFocusChanged(hasFocus);
            int width = button.getRight() - button.getLeft();
            int height = button.getBottom() - button.getTop();
        }
    

    onWindowFocusChanged含义就是view已经初始化完毕,这个时候去获取宽高就没问题了,但是要注意的是此方法会被调用很多次,当Activity的窗口失去或者得到焦点都会调用一次,也就是Activity onResume或onPause时都会调用。
    那么这样做的思路是对的,不过不必要这么麻烦了,系统已经为我们做好这步工作了。

    我们可以直接用getWidth()和getHeight()来获取view宽高,得到的效果是一样的,我们可以直接看一下SDK源码。

    AS查看源代码的方法:http://blog.csdn.net/glc_csdn/article/details/53993903

     /**
         * Return the width of the your view.
         *
         * @return The width of your view, in pixels.
         */
        @ViewDebug.ExportedProperty(category = "layout")
        public final int getWidth() {
            return mRight - mLeft;
        }
    
        /**
         * Return the height of your view.
         *
         * @return The height of your view, in pixels.
         */
        @ViewDebug.ExportedProperty(category = "layout")
        public final int getHeight() {
            return mBottom - mTop;
        }
    

    同时我们看到还有好多方法
    getTop() : 获取View顶部到父布局顶距离
    getLeft() : 获取View左部到父布局左距离
    getRight() : 获取View右部到父布局左距离
    getBottom() : 获取View底部到父布局底距离

    MotionEvent是为我们触摸点提供的方法,
    getX():点击事件距离控件左边的距离
    getY():点击事件距离控件顶边的距离
    getRawX():点击事件距离父布局左边的距离
    getRawY():点击事件距离父布局顶边的距离

    View滑动

    滑动实现思路是:当点击事件传到View后,系统记录触摸点坐标,手指移动系统记录移动后坐标并计算偏移量,通过偏移量来修改View坐标。
    下面是几种View滑动方式

    1.layout() 方法

    我们来实现一个view滑动的效果
    实现自定义View并在xml里面引用它

    public class CustomView extends View {
        private int lastX;
        private int lastY;
        public CustomView(Context context) {
            super(context);
        }
    
        public CustomView(Context context, @Nullable AttributeSet attrs) {
            super(context, attrs);
        }
    
        public CustomView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
        }
    
        @TargetApi(Build.VERSION_CODES.LOLLIPOP)
        public CustomView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
            super(context, attrs, defStyleAttr, defStyleRes);
        }
    
        //触摸事件
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            //获取事件的横纵坐标(相对于View)
            int x = (int) event.getX();
            int y = (int) event.getY();
    
            //事件的监听
            switch (event.getAction()){
                //按下的时候记录触摸点的值
                case MotionEvent.ACTION_DOWN:
                    lastX = x;
                    lastY = y;
                    break;
                //移动过程中不断重绘
                case MotionEvent.ACTION_MOVE:
                    //计算偏移量
                    int offsetX = x - lastX;
                    int offsetY = y - lastY;
                    //重新布局
                    layout(getLeft()+offsetX,getTop()+offsetY,getRight()+offsetX,getBottom()+offsetY);
                    break;
            }
    
            return true;
        }
    }
    
      <com.surine.viewlearn.CustomView
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:background="@color/colorPrimary"
            />
    

    具体效果图,这里我放了4个自定义的view里面


    Screenshot_2017-09-27-21-52-33-179_com.surine.vie.png
    2.offsetLeftAndRight()与offsetTopAndBottom()

    这两个方法的效果是和layout一样的,但是他们可以直接使用偏移量

      //偏移操作
      offsetLeftAndRight(offsetX);
     offsetTopAndBottom(offsetY);
    
    3.LayoutParams(布局参数)

    这个东西在调整view的宽高度,margin等中经常用到,那么我们换个思维,移动也就是在改变view的margin(对于父布局),具体的代码:

     LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) getLayoutParams();
                    layoutParams.leftMargin = getLeft() + offsetX;
                    layoutParams.topMargin = getLeft() + offsetY;
                    setLayoutParams(layoutParams);
    

    这里用了LinearLayout.LayoutParams,这是跟你的父布局的类型来决定,所以相对其他方法比较麻烦一点。

    4.Animation

    一个不是属于触摸移动的方式,使用动画,这就相对好理解一些,给view设置一个位移的动画,实现思路如下。

    创建动画文件(res/anim/translate.xml)

    <?xml version="1.0" encoding="utf-8"?>
    <set xmlns:android="http://schemas.android.com/apk/res/android"
    android:fillAfter="true"
    >
        <translate
            android:fromXDelta="0"
            android:toXDelta="300"
            android:duration = "1000"
            />
    </set>
    

    启动动画

     custom_view = findViewById(R.id.custom_view);
            custom_view.setAnimation(AnimationUtils.loadAnimation(this,R.anim.translate));
    

    当然我们用的是普通的动画,改变view的显示但并没有改变view的位置参数,所以对于点击事件来说不是很友好,那么这个问题在Android3以后的版本得到了解决,那就是属性动画。具体在后面的文章哦。

    5.scrollTo与scrollBy

    scrollTo是移动到某个坐标点,而scrollby是移动增量,那么他们的源码如下。

     public void scrollBy(int x, int y) {
            scrollTo(mScrollX + x, mScrollY + y);
        }
    

    可以看到scrollby是调用了scrollTo方法的,传的参数是一个当前值+增量

    public void scrollTo(int x, int y) {
            if (mScrollX != x || mScrollY != y) {
                int oldX = mScrollX;
                int oldY = mScrollY;
                mScrollX = x;
                mScrollY = y;
                invalidateParentCaches();
                onScrollChanged(mScrollX, mScrollY, oldX, oldY);
                if (!awakenScrollBars()) {
                    postInvalidateOnAnimation();
                }
            }
        }
    

    这里是scrollTo的方法体,mScrollX和mScrollY用于描述View的内容在水平方向或垂直方向滚动的距离。 那么在这里注意一下View的内容仅仅是指其中的东西,而不是View全部,例如textview的文字

    那么我们要写具体的代码就是下面的这一行(在 case MotionEvent.ACTION_MOVE:标签内写)

     ((View)getParent()).scrollBy(-offsetX,-offsetY);
    

    这里设置负数是因为视图与我们坐标轴的移动方向是相反的,可以想想中学实验显微镜的波片的移动和视野内的显示的例子。

    View是Android中主要的一部分,掌握了View的基本操作才能更好的进阶!

    相关文章

      网友评论

          本文标题:View体系与自定义View 【1】

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