Android中的View基础

View基础知识

什么是View

view是所有控件的基类,是界面层控件的一种抽象,他代表了一个控件。

ViewGroup也继承View,ViewGroup里面包含多个控件,即一组View。

View位置参数

view的宽高和坐标的关系:
width = right - left;
height = bottom - top;

left、top、right、bottom其中top和left是View的左上角坐标,right和bottom是右下角坐标。这些坐标都是相对于父容器来说的,因此说它是一种相对坐标。

如何得到View的四个参数:left、top、right、bottom

  • Left = getLeft();
  • Right = getRight();
  • Top = getTop();
  • Bottom = getBottom();

除此之外3.0之后View还提供了四个比较重要的位置参数信息,X、Y、translationX、translationY,和基本位置信息一样,也提供了get/set方法。

X、Y也比较好理解,是View左上角在父容器中的坐标(在View没有平移的情况下X=left,Y=top)

translationX,translationY则是View**当前位置相对于初始化位置的偏移量**,也就是说,如果你的View创建之后,没有进行过相关平移操作,translationX和translationY的值是始终为0的。

实例:
布局文件

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/tv_test"
        android:layout_width="200dp"
        android:layout_height="200dp"
        android:layout_marginLeft="100dp"
        android:layout_marginTop="100dp"
        android:background="#8d13ca"
        android:text="Hello World" />
</RelativeLayout>

MainActivity

private void initViews() {
        tvTest = (TextView) findViewById(R.id.tv_test);
        tvTest.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        print();
        tvTest.setX(100);
        System.out.println("==============");
        print();
    }


    private void print() {
        int width = tvTest.getWidth();
        int height = tvTest.getHeight();
        float translationX = tvTest.getTranslationX();
        float translationY = tvTest.getTranslationY();
        float x = tvTest.getX();
        float y = tvTest.getY();
        int left = tvTest.getLeft();
        int top = tvTest.getTop();
        int bottom = tvTest.getBottom();
        int right = tvTest.getRight();


        System.out.println("width" + width);
        System.out.println("height" + height);
        System.out.println("translationX" + translationX);
        System.out.println("translationY" + translationY);
        System.out.println("x" + x);
        System.out.println("y" + y);
        System.out.println("left" + left);
        System.out.println("top" + top);
        System.out.println("bottom" + bottom);
        System.out.println("right" + right);
    }

结果:

结果你会发现,View位置改变了,但是View的四个参数:left、top、right、bottom没有变化。仅仅只是translationX和X的值发生了变化。

可以简单理解这几个参数,top、left、right、bottom主要负责View大小的控制,通过源码可以看到

public  final  int  getWidth() {
return mRight - mLeft;
 }
public  final  int  getHeight() {
return mBottom - mTop;
 }

View的宽高是有top、left、right、bottom参数决定的
而X,Y和translationX,和translationY则负责View位置的改变。
例子中可以看到X和translationX发生了变化。
translationX变成了-100;
X怎样变成100的呢?
换算关系:
X = left + translationX;
Y = top + translationY;

MotionEvent和TouchSlop

MotionEvent的一个重要概念,事件类型。事件类型就是指MotionEvent对象所代表的动作。

  int action = MotionEventCompat.getActionMasked(event);
    switch(action) {
        case MotionEvent.ACTION_DOWN:
            break;
        case MotionEvent.ACTION_MOVE:
            break;
        case MotionEvent.ACTION_UP:
            break;
    }

MotionEvent也可以获取也可以得到点击事件发生的x,y坐标;
两组方法:
* getX()/getY()
* getRawX()/getRawY()

getX()/getY()相对于当前View左上角的x和y坐标。
getRawX()/getRawY()相对于手机屏幕左上角的x和y坐标。

TouchSlop系统所识别出的,被认为是滑动最小的距离。
通过ViewConfiguration.get(getContext).getScaledTouchSlop();获取这个常量。

View的滑动

三中方式实现View滑动

使用scrollTo/scrollBy

mScrollX和mScrollY单位为像素。
scrollTo(x,y)移动到一个具体的坐标点(x,y)
scrollBy(dx,dy)表示移动的增量为dx,dy
当你点击View,这个View并没有移动,移动的是View里面的content;
如果在ViewGroup中,移动的将是所有的子View;
比如:TextView里面移动的是他的(content)文本,ImageView里面content是drawable对象

scrollBy移动需注意:
用scrollBy,你这样理解:手机屏幕相当于一个中间空的盖子,下面是一个很大的布,布里面是图像,当用scrollBy滑动时候,相当于手机(这个盖子在动),所以当你scrollBy(20,10)的时候,就相当于(手机)盖子向下走20、向左10.这时候相当于View向上减20,向右减10.

期是可以从源码看:

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

从左向右滑动,mScrollX为负值反之为正。
从上往下滑动,mScrollY为负值反之为正。

使用动画

属性动画比较好,位置也跟着动了,能够响应点击事件。
使用动画主要操作的是View的translationX和translationY属性。

改变布局参数LayoutParams

LayoutParams保存了一个View的布局参数,改变LayoutParams动态修改一个布局的位置参数。
首先通过这个View的getLayoutParams()获取这个View的LayoutParams。
再LayoutParams的leftMargin和rightMargin加上偏移量
最后setLayoutParams(layoutParams)就行了。

 RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) tvTest.getLayoutParams();
        layoutParams.leftMargin += 100;
        tvTest.setLayoutParams(layoutParams);
三种滑动总结
  • scrollTo/scrollBy:操作简单,适合对View的内容滑动
  • 动画:操作简单,适用于没有交互的View和实现复杂的动画效果
  • 改变布局参数:操作稍微复杂,适用于有交互的View

弹性滑动

View的滑动不实现弹性滑动,这时候会很生硬,用户体验很差。

实现思想:一次大的滑动分成若干次小的滑动,并在一定时间内完成。
实现方式很多:Scroller、Handler#postDelayed以及Thread#sleep

Scroller

Scroller是弹性滑动对象,用于实现View的弹性滑动,但是Scroller本身无法让View弹性滑动,需要和View 的computeScroll方法配合才能实现

使用:
* 创建一个Scroller对象
* 重写computrScroll(),实现模拟滑动

public class DragView5 extends View {

    private int lastX;
    private int lastY;
    private Scroller mScroller;

    public DragView5(Context context) {
        super(context);
        ininView(context);
    }

    public DragView5(Context context, AttributeSet attrs) {
        super(context, attrs);
        ininView(context);
    }

    public DragView5(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        ininView(context);
    }

    private void ininView(Context context) {
        setBackgroundColor(Color.BLUE);
        // 初始化Scroller
        mScroller = new Scroller(context);
    }

    @Override
    public void computeScroll() {
        super.computeScroll();
        // 判断Scroller是否执行完毕
        if (mScroller.computeScrollOffset()) {
            ((View) getParent()).scrollTo(
                    mScroller.getCurrX(),
                    mScroller.getCurrY());
            // 通过重绘来不断调用computeScroll
            invalidate();
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        int x = (int) event.getX();
        int y = (int) event.getY();
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                lastX = (int) event.getX();
                lastY = (int) event.getY();
                break;
            case MotionEvent.ACTION_MOVE:
                int offsetX = x - lastX;
                int offsetY = y - lastY;
                ((View) getParent()).scrollBy(-offsetX, -offsetY);
                break;
            case MotionEvent.ACTION_UP:
                // 手指离开时,执行滑动过程
                View viewGroup = ((View) getParent());
                mScroller.startScroll(
                        viewGroup.getScrollX(),
                        viewGroup.getScrollY(),
                        -viewGroup.getScrollX(),
                        -viewGroup.getScrollY());
                invalidate();
                break;
        }
        return true;
    }
}
通过动画

比如让一个View的内容在100ms向左移动100像素

ObjectAnimator.ofFloat(tvTest, "translationX", 0, 100).setDuration(100).start();
使用延时策略

具体说可以使用Handler或者View的postDelayed方法,也可以使用线程的sleep。

  • postDelayed可以通过它来延时发送一个消息,然后在消息中进行View滑动(接连不断的发送这种延时消息,就可以实现弹性滑动)

  • sleep通过while循环中不断地滑动View和sleep,就可以实现弹性滑动。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值