View体系与自定义View 【1】

一.View

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

二.ViewGroup

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

三.坐标系

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

1.Android坐标系

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

View体系与自定义View 【1】_第1张图片
2.View坐标系

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


View体系与自定义View 【1】_第2张图片

那么从图中我们可以看到计算一个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;
    }
}
  

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


View体系与自定义View 【1】_第3张图片
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)



    

启动动画

 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】)