View的基础与滑动

View的基础与滑动

1.View的基础知识

1.1View是什么

View是一种界面层的控件的一种抽象

,我们知道的大多数控件都是直接或间接继承自View

EditText,TextView等等

注意:

EditText在compose中并没有相关控件,而是通过TextField来进行组合

Android中View本身就可以是单个控件也可以是由多个控件组合成的一组控件

如下面

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-P09FNiCp-1684507503967)(../../assets/QQ图片20230518203409.png)]

1.2View的位置参数

这个分为两种情况,一种是相对于ViewGroup的,一种相对于Screen的

先说相对于ViewGroup的

1.2.1ViewGroup中的位置参数

getLeftview的最左边相对于viewGroup最左边的距离
getRightview的最右边相对于ViewGroup最左边的距离
getTopview的最上面相对于ViewGroup最上面的距离
getBottomview的最下面相对于ViewGroup最上面的距离
getX你触碰的那个点相对于view的X距离
getY你触碰的那个点相对于view的Y距离

1.2.2Screen中的位置参数

getRawX你触碰的那个点相对于Screen的x距离
getRawY你触碰的那个点相对于Screen的y的距离

1.2.3图解

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-F6NrhMaO-1684507503968)(../../assets/QQ图片20230518214430.png)]

说一说getX与getLeft的关系

我们要先将一个概念叫做偏移量:

你在屏幕按动的那个到你父容器(即view)的距离,这个图里面我是用touch表示的,

就是偏移量,translation

getX = getLeft+translationX,正值表示向右平移,负值表示向左平移。

getY = getTop+translationY,正值表示向下平移,负值表示向上平移。

1.3MotionEvent

当你手指接触到屏幕的时候,MotionEvent有4个典型事件如下:

  1. ACTION_DOWN ,这个是手指刚接触到屏幕的时候
  2. ACTION_MOVE,这个是手指在屏幕上移动
  3. ACTION_UP,这个是手指从屏幕上松开
  4. ACTION_CANCEL,这个是触摸事件被取消或中断的动作类型。

前面的还好理解,那么后面两个怎么区分呢?

chatPGT中说遇到如下事件,会执行后者

  1. 手指移出了视图范围:当用户在触摸一个视图时,如果手指移动到视图范围之外,系统会生成一个 ACTION_CANCEL 的触摸事件,表示触摸事件被取消。这种情况下,ACTION_CANCEL 会被执行,以便你可以在代码中相应地处理触摸事件的取消。
  2. 系统事件中断触摸:当触摸事件正在进行时,如果系统发生某些中断事件,例如电话呼入、弹出对话框等,系统会生成一个 ACTION_CANCEL 的触摸事件,表示触摸事件被中断。这样可以确保用户在触摸过程中不会发生意外的操作。在这种情况下,你可以通过处理 ACTION_CANCEL 事件来恢复到触摸事件之前的状态或进行必要的清理工作。

虽然我没实现出来

ACTION_UP这个就是指头一离开,就会触发

1.4TouchSlop

这个是系统能识别出来的滑动的最小距离,如果两次滑动的距离小于它便不会被识别出来

ViewConfiguration.get(getContext().getScaledTouchSlop());

获得

2.View的滑动

《Android开发艺术探索》中只有3种滑动,分别是

1.动画

2.scrollTo/scrollBy

3.LayoutParams

艺术开发探索把后面的这种称为改变布局参数

而《进阶之光》在以上的基础上还说了

1.layout

2.offsetLeftAndRight/offsetTopAndBottom

而这两种其实也就属于改变布局参数

所以就分成3个部分讲

2.1动画

《进阶之光》讲了2种动画,

一种是属性动画,一种是View动画

先讲视图动画

2.1.1View动画

我们在res中创建anim文件,在这里面创建translate.xml文件

注意是set类型哦

以下是里面的一些属性

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5bAywKm9-1684507503970)(../../assets/QQ图片20230518230859.png)]

set不用说了,translate是进行移动的,可以进行x,y轴的移动。alpha是控件的透明度,rotate是实现旋转的,scale是扩大多少倍或者缩小至几分1

比如我用translate

<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
    android:fromXDelta="0"
    android:duration="1000"
    android:toXDelta="200"/>
</set>
ima = findViewById(R.id.what);
ima.setAnimation(AnimationUtils.loadAnimation(this,R.anim.translate));

这时候我的ima会在1000ms内从x = 0移动到x = 200,但是执行完后又会退回去

我们只需要在set里面加一句

android:fillAfter = "true"

就行了

但是还是有一个问题,比如你移动的是一个button,你让它移动到x = 200的位置后,你再点击它,它并不会有相应,你必须点击x = 0的时候它才会有响应

View动画并不能改变View的位置参数

ima = findViewById(R.id.what);
Log.d("TAG",""+ima.getLeft());
ima.setAnimation(AnimationUtils.loadAnimation(this,R.anim.translate));
ima.getX();
Log.d("TAG",""+ima.getLeft());

打印出来

D/TAG: 0.0
D/TAG: 0.0

另一种讲的是

2.1.2属性动画

ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(mButton,"translationX",200);
objectAnimator.setDuration(2000);
objectAnimator.start();

这样之后mButton便会在x轴上移动移动2000ms移动200处

并且运行完之后没有返回

这时候,我们点击button发现它可以跳转到第二个Activity中,成功。

这时候我们打印以下getLeft()

发现也全是

TAG: 0
TAG: 0

View动画最后打印出来的一模一样

这是因为start();为异步方法

在start方法后写上

 Handler handler = new Handler();
    Runnable runnable = new Runnable() {
        @Override
        public void run() {
            Log.d("TAG",""+ima.getLeft());
        }
    };
    handler.postDelayed(runnable, 2000);

打印出来结果了

TAG: 492

或者

objectAnimator.addListener(new AnimatorListenerAdapter() {
    @Override
    public void onAnimationEnd(Animator animation) {
        Log.d("TAG",""+mButton.getTranslationX());
        Log.d("TAG",""+mButton.getLeft());
    }
});

也会发现getLeft改变了

但是你延迟2s打印view动画的getLeft它不会改变

2.2scrollTo/scrollBy

这个主要就是滑动,其中scrollTo是移动到某个坐标

而scrollBy是移动了多少坐标

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();
            }
        }
    }

scrollBy就是调用了个scrollTo就没了

所以看源码就直接就看scrollTo的源码

if语句判断的是:滑动的位置是否和原位置相同,不相同才会滑动

用两个新变量保存原来的位置,再把新位置赋值给mScrollXmScrollY

通过

 invalidateParentCaches();

来清除之前父布局的缓存

onScrollChanged(mScrollX, mScrollY, oldX, oldY);

用于监听与回调

最后再第二个if语句中

 if (!awakenScrollBars()) {
                postInvalidateOnAnimation();
            }

这段代码的目的是在滚动条没有被唤醒(显示)时,请求视图在下一帧进行刷新。这样做的目的是在需要显示滚动条时,确保滚动条能够正确地显示在视图上。

如果你要使用scrollTo/scrollBy有点需要

注意

在ACTION_MOVE

中,你想让它移动到(x,y)

正常情况下是

((View)getParent()),scrollTo(x,y)

但是你会发现它移动的位置不太对劲,移动到了(-x,-y)了

把代码改成

((View)getParent()),scrollTo(-x,-y)

便成功移动到(x,y)

因为scrollTo(-x, -y)方法中的参数是负值。scrollTo()方法中的参数表示要滚动到的目标位置相对于视图左上角的偏移量。通过传入负值,实际上是在将视图向相反的方向滚动,从而实现了将视图移动到目标位置的效果。

2.3 改变布局参数

2.3.1layout

 case MotionEvent.ACTION_MOVE:
                Log.d("TAG","MOVE");
                int x1 = x - lastX;
                int y1 = y - lastY;
                // 处理移动事件
                layout(getLeft() + x1,getTop() + y1,getRight() + x1,getBottom() + y1);
                break;

2.3.2setLeftAndRight

case MotionEvent.ACTION_MOVE:    
			Log.d("TAG", "MOVE");    
			int x1 = x - lastX;    // 处理移动事件  
			int left = view.getLeft() + x1;    
			int right = view.getRight() + x1;    
			view.setLeftAndRight(left, right);    
			break;

setTopAndBottom差不多

2.3.3LayoutParams

case MotionEvent.ACTION_MOVE:
    Log.d("TAG", "MOVE");
    int x1 = x - lastX;
    int y1 = y - lastY;
    // 处理移动事件
    ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) view.getLayoutParams();
    layoutParams.leftMargin = layoutParams.leftMargin + x1;
    layoutParams.topMargin = layoutParams.topMargin + y1;
    view.setLayoutParams(layoutParams);
    break;
case MotionEvent.ACTION_MOVE:
    Log.d("TAG", "MOVE");
    int x1 = x - lastX;
    int y1 = y - lastY;
    // 处理移动事件
    ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) view.getLayoutParams();
    layoutParams.leftMargin = layoutParams.leftMargin + x1;
    layoutParams.topMargin = layoutParams.topMargin + y1;
    view.setLayoutParams(layoutParams);
    break;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值