View小知识

**内容导视:**

   1. 先了解基本的api
   2. 3种方式实现全屏随手滑动的自定义view

基本的API 针对于View的子类View的
view.getX( ),view.getLeft( ),view.getTranslationX( ),e.getX( ),e.getRawX( )
view.getY( ),view.getTop( ),view.getTranslationY(( ),e.getY( ),e.getRawY( )


布局:注意这里view设置了margin的

<?xml version="1.0" encoding="utf-8"?>
<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">
    <Button android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentRight="true"
            android:background="#34495e"
            android:onClick="click"
            android:text="移动view的位置"
            android:textColor="#fff"/>
    <com.docwei.democeshi.MyTextView
        android:id="@+id/view"
        android:layout_width="60dp"
        android:layout_height="60dp"
        android:layout_marginLeft="60dp"
        android:layout_marginTop="50dp"
        android:background="#1abc9c"
        android:text="我是内容"
        android:textColor="#fff"
        android:textStyle="bold"/>
</RelativeLayout>

这里写图片描述

最初显示view的位置
这里写图片描述
点击移动View的位置之后:
这里写图片描述

mView.setTranslationX(getResources().getDimensionPixelSize(R.dimen.move_x)); mView.setTranslationY(getResources().getDimensionPixelSize(R.dimen.move_y)) 这里写图片描述 这里写图片描述 得出的结论是:
view.getX( )=view.getLeft( )+view.getTranslationX( )
view.getY( )=view.getTop( )+view.getTranslationY( )

可以把view.getLeft( )与view.getTop( )看成一个final的常量(最初布局完成之后就不会再改变的)
想要获取移动的控件左上角坐标的位置是不能通过getLeft()或者getTop(),因为大部分情况都不是我们想要的结果。
也不能轻易的把view.getX( )或者view.getY( )来作为view左上角的坐标,要考虑margin的情况。


图解:白点是用户按下的点


e.getRawX( )可以看做是按下的点在在屏幕的X坐标
e.getRawY( )可以看做是按下的点在屏幕的Y坐标,注意(包括了导航栏和actionBar的高度)
e.getX( )是按下的点相对于控件的X的位置
e.getY( )是按下的点相对于控件的Y的位置


接下来:做一个随手滑动的view(类似于launcer上图标可以拖动的效果)来验证这些值
到底有几种方式实现随手滑动的自定义view ?
实现:
方式一:通过按下的点的移动变化来计算控件(左上角坐标)的变化,这里view的移动使用view属性平移动画,传入控件左上角的坐标的上一个值和下一个值,启动动画达到效果。那关键就是获取控件左上角的坐标,从图上就可以看出。
方式二:通过按下的点的移动变化来计算控件(左上角坐标)的变化,获取view.setX( value)来做,能用setTranslationX()方法实现那一定可以通过这个setX()来实现,只是传入值的问题。
方式三:比较简单,直接获取leftMargin和topMargin确定view的位置,然后直接setLayoutParams(params)实现。

方式一:具体代码:


public class MyTextView extends TextView {
    private static final String TAG = "MyTextView";
    private int mScreenWidth;
    private int mScreenHeight;
    private final int mTouchSlop;
    private int mOffsetY;

    public MyTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
        mScreenWidth=context.getResources().getDisplayMetrics().widthPixels;
        mScreenHeight=context.getResources().getDisplayMetrics().heightPixels;
        mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
    }

    //***************************方式一start***************************************
    private int leftMargin;
    private int topMargin;
    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        if(oldw!=w){
            CoordinatorLayout.MarginLayoutParams params= (CoordinatorLayout.MarginLayoutParams) getLayoutParams();
            leftMargin=params.leftMargin;
            topMargin=params.topMargin;
        }
    }
    int downPointX;
    int downPointY;
    int moveLastPointX;
    int moveLastPointY;
    int downX;
    int downY;
    int moveLastX;
    int moveLastY;

    boolean flag=false;

    public void setOffsetY(int offsetY) {
        //mOffsetY就是导航栏和actionbar的高度之和
        mOffsetY = offsetY;
    }
    @Override
    public boolean onTouchEvent(MotionEvent e) {
        switch (e.getAction()){
            case MotionEvent.ACTION_DOWN:
                //根据按下点的位置获取view的左上角的坐标
                downPointX = (int) (e.getRawX()-e.getX());
                downPointY = (int) (e.getRawY()-e.getY());
                downX= (int) e.getRawX();
                downY= (int) e.getRawY();
                flag=true;
                break;
            case MotionEvent.ACTION_MOVE:
                if(flag){
                    moveLastPointX = downPointX;
                    moveLastPointY = downPointY;
                    moveLastX=downX;
                    moveLastY=downY;
                    flag=false;
                }

                int moveX = (int) e.getRawX();
                int moveY = (int) e.getRawY();

                int realdx=moveX-moveLastX;
                int realdy=moveY-moveLastY;
                if(Math.abs(realdx)<mTouchSlop&&Math.abs(realdy)<mTouchSlop){
                    return true;
                }
                //获取移动后的坐标
                int movePointX=moveLastPointX +realdx;
                int movePointY=moveLastPointY +realdy;

                //边界值限定------start---------
                    if(movePointX<0){
                        movePointX=0;
                    }
                    if(movePointX>mScreenWidth-getMeasuredWidth()){
                        movePointX=mScreenWidth-getMeasuredWidth();
                    }
                    if(movePointY<0){
                        movePointY=0;
                    }
                    if(movePointY>mScreenHeight-getMeasuredHeight()){
                        movePointY=mScreenHeight-getMeasuredHeight();
                    }
                //边界值限定------end------------

                //这里使用属性动画,其实就是左上角坐标的变化,,
                //最后两个参数,一个是之前的坐标,再一个就是平移之后的坐标,这个Y坐标要是相对于父布局的坐标,而不是屏幕的坐标
                //Y要减去的是状态栏和导航栏的高度,
                //使用属性动画还要考虑leftMargain和topMargin
                ObjectAnimator animX=ObjectAnimator.ofFloat(this,"TranslationX", moveLastPointX-leftMargin, movePointX-leftMargin);
                ObjectAnimator animY=ObjectAnimator.ofFloat(this,"TranslationY", moveLastPointY-mOffsetY-topMargin, movePointY-mOffsetY-topMargin);
                animX.setDuration(10);
                animY.setDuration(10);
                animX.start();
                animY.start();

                moveLastPointX = movePointX;
                moveLastPointY = movePointY;
                moveLastX=moveX;
                moveLastY=moveY;
                break;
            case MotionEvent.ACTION_UP:
                break;
        }
        return true;
    }

}

获取actionBar和到导航栏的高度,可以在activity的oncreate方法里面获取

 private void initData() {
       mView = (MyTextView) findViewById(R.id.view);
       mView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
           @Override
           public void onGlobalLayout() {
               Rect rectgle= new Rect();
               Window window= getWindow();
               window.getDecorView().getWindowVisibleDisplayFrame(rectgle);
               int StatusBarHeight= rectgle.top;
               int contentViewTop=
                       window.findViewById(Window.ID_ANDROID_CONTENT).getTop();
               int TitleBarHeight= contentViewTop - StatusBarHeight;
               mView.setOffsetY(contentViewTop);
             mView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
           }
       });
    }
方法二:
 public boolean onTouchEvent(MotionEvent e) {
        switch (e.getAction()){
            case MotionEvent.ACTION_DOWN:
                //根据按下点的位置获取view的左上角的坐标
                downPointX = (int) (e.getRawX()-e.getX());
                downPointY = (int) (e.getRawY()-e.getY());
                downX= (int) e.getRawX();
                downY= (int) e.getRawY();
                flag=true;
                break;
            case MotionEvent.ACTION_MOVE:
                if(flag){
                    moveLastPointX = downPointX;
                    moveLastPointY = downPointY;
                    moveLastX=downX;
                    moveLastY=downY;
                    flag=false;
                }

                int moveX = (int) e.getRawX();
                int moveY = (int) e.getRawY();

                int realdx=moveX-moveLastX;
                int realdy=moveY-moveLastY;
                if(Math.abs(realdx)<mTouchSlop&&Math.abs(realdy)<mTouchSlop){
                    return true;
                }
                //获取移动后的坐标
                int movePointX=moveLastPointX +realdx;
                int movePointY=moveLastPointY +realdy;

                //边界值限定------start---------
                    if(movePointX<0){
                        movePointX=0;
                    }
                    if(movePointX>mScreenWidth-getMeasuredWidth()){
                        movePointX=mScreenWidth-getMeasuredWidth();
                    }
                    if(movePointY<0){
                        movePointY=0;
                    }
                    if(movePointY>mScreenHeight-getMeasuredHeight()){
                        movePointY=mScreenHeight-getMeasuredHeight();
                    }


                //边界值限定------end------------

                setX(movePointX);
                setY(movePointY-mOffsetY);


                moveLastPointX = movePointX;
                moveLastPointY = movePointY;
                moveLastX=moveX;
                moveLastY=moveY;
                break;
            case MotionEvent.ACTION_UP:
                break;
        }
        return true;
    }

方式三:
public class MyTextView extends TextView {
    private static final String TAG = "MyTextView";
    private int mScreenWidth;
    private int mScreenHeight;
    private final int mTouchSlop;

    public MyTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
        mScreenWidth=context.getResources().getDisplayMetrics().widthPixels;
        mScreenHeight=context.getResources().getDisplayMetrics().heightPixels;
        mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
    }

    int downPointX;
    int downPointY;
    int moveLastPointX;
    int moveLastPointY;
    private boolean flag=false;
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        CoordinatorLayout.MarginLayoutParams layoutParams = (CoordinatorLayout.MarginLayoutParams) getLayoutParams();

        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN: {
                //根据按下点的位置
                downPointX = (int) event.getRawX();
                downPointY = (int) event.getRawY();
                flag=true;
                return true;
            }
            case MotionEvent.ACTION_MOVE: {
                if(flag){
                    moveLastPointX = downPointX;
                    moveLastPointY = downPointY;
                    flag=false;
                }
                //获取变化的差值
                int moveX = (int) event.getRawX();
                int moveY = (int) event.getRawY();
                int realdx=moveX-moveLastPointX;
                int realdy=moveY-moveLastPointY;
                //根据变化的差值来设置左上方向的margin确定控件的位置
                int left = layoutParams.leftMargin +realdx;
                int top = layoutParams.topMargin + realdy;

                //边界值限定------start---------
                if(left<0){
                    left=0;
                }
                if(left>mScreenWidth-getMeasuredWidth()){
                    left=mScreenWidth-getMeasuredWidth();
                }
                if(top<0){
                    top=0;
                }
                if(top>mScreenHeight-getMeasuredHeight()){
                    //有必要的话还需要考虑虚拟键的高度
                    top=mScreenHeight-getMeasuredHeight();
                }
                //边界值限定------end------------

                layoutParams.leftMargin = left;
                layoutParams.topMargin = top;
                setLayoutParams(layoutParams);
                //记住最后的位置,把这赋给上一个点
                moveLastPointX=moveX;
                moveLastPointY=moveY;
                break;
            }
            case MotionEvent.ACTION_UP: {
                break;
            }
        }

        return true;
    }


}

代码其实都是很简单,只是用来让大家验证
demo源码地址:
http://download.youkuaiyun.com/download/doc__wei/9895669

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值