文章目录

  • 1. `View`和`ViewGroup`的关系
  • 2. 坐标获取
  • 3. View的滑动
    • 3.1 案例`layout()`
    • 3.2 案例`offsetLeftAndRight & offsetTopAndBottom`
    • 3.3 案例`LayoutParams(改变布局参数)`
    • 3.4 动画
      • 3.4.1 属性动画
        • 组合动画 `AnimatorSet`
        • `XML`中定制属性动画
    • 3.5 `scrollTo`与`scollBy`

1. ViewViewGroup的关系

    ViewAndroid所有控件的基类,我们平常用的布局控件 LinearLayout,它继承自 ViewGroupViewGroup 可以理解为 View的组合,它可以包含很多 View 以及ViewGroup。如下图:

需要注意的是ViewGroup也继承自ViewViewGroup派生了多种布局控件子类,比如LinearLayoutRelativeLayout等。

2. 坐标获取

  • getTop(),获取View自身顶边到其父布局顶边的距离;
  • getLeft(),获取View自身左边到其父布局左边的距离。
  • getRight(),获取View自身右边到其父布局左边的距离。
  • getBottom(),获取View自身底边到其父布局顶边的距离。
  • View获取自身宽度:width = getRight() - getLeft();。当然系统提供了getWidth()来直接获取;
  • View获取自身高度:height=getBottom() - getTop();。同样的系统提供了getHeight()来直接获取;
  • 触控事件中,使用getRawX()和触控事件中,使用getRawX()getRawY()方法获得的坐标也是
    Android坐标系的坐标。getRawY()方法获得的坐标也是Android坐标系的坐标。
  • 在触摸事件中,使用onTouchEvent(MotionEvent event)方法来处理,MotionEvent也提供了获取焦点坐标的各种方法。
  • getX():获取点击事件距离控件左边的距离,即视图坐标。
  • getY():获取点击事件距离控件顶边的距离,即视图坐标。
  • getRawX():获取点击事件距离整个屏幕左边的距离,即绝对坐标。
  • getRawY():获取点击事件距离整个屏幕顶边的距离,即绝对坐标。

3. View的滑动

View的滑动是Android实现自定义控件的基础,同时在开发中我们也难免会遇到View的滑动处理。其实不管是哪种滑动方式,其基本思想都是类似的:当点击事件传到View时,系统记下触摸点的坐标,手指移动时系统记下移动后触摸的坐标并算出偏移量,并通过偏移量来修改View的坐标。实现View滑动有很多种方法,在这里主要讲解6种滑动方法,分别是layout()offsetLeftAndRight()
offsetTopAndBottom()LayoutParams、动画、scollToscollBy,以及Scroller

3.1 案例layout()

首先自定义View,如:

package com.example.myapplication;import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;import androidx.annotation.Nullable;public class MyView extends View {private int lastX;private int lastY;public MyView(Context context) {super(context);}public MyView(Context context, @Nullable AttributeSet attrs) {super(context, attrs);}public MyView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);}// 复写onTouchEvent,以使用坐标@Overridepublic boolean onTouchEvent(MotionEvent event) {// 获取手指坐标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方法来重新放置它的位置layout(getLeft()+offsetx, getTop()+offsety, getRight()+offsetx, getBottom()+offsety);break;}return true;}
}

然后,在主布局文件中使用即可,如:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:orientation="vertical" android:layout_width="match_parent"android:layout_height="match_parent"><com.example.myapplication.MyViewandroid:id="@+id/myView"android:layout_width="50dp"android:layout_height="50dp"android:background="@color/colorPrimary"/>
</LinearLayout>

效果:

拖动可移动。

3.2 案例offsetLeftAndRight & offsetTopAndBottom

case MotionEvent.ACTION_MOVE:int offsetx = x - lastX;int offsety = y - lastY;// 使用offsetLeftAndRight & offsetTopAndBottomoffsetLeftAndRight(offsetx);offsetTopAndBottom(offsety);break;

3.3 案例LayoutParams(改变布局参数)

LayoutParams主要保存了一个View的布局参数,因此我们可以通过LayoutParams来改变View的布局参数从而达到改变View位置的效果。

case MotionEvent.ACTION_MOVE:int offsetx = x - lastX;int offsety = y - lastY;// 使用LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) getLayoutParams();layoutParams.leftMargin = getLeft() + offsetx;layoutParams.topMargin = getTop() + offsety;setLayoutParams(layoutParams);break;

这里使用LinearLayout.LayoutParams的原因是主文件我们使用的布局是LinearLayout。如果父控件是RelativeLayout,则要使用RelativeLayout.LayoutParams。还可以使用ViewGroup.MarginLayoutParams来实现:

case MotionEvent.ACTION_MOVE:int offsetx = x - lastX;int offsety = y - lastY;// 使用ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) getLayoutParams();layoutParams.leftMargin = getLeft() + offsetx;layoutParams.topMargin = getTop() + offsety;setLayoutParams(layoutParams);break;

3.4 动画

可以使用动画来进行移动。
布局文件不变,然后删除自定义控件中的onTouchEvent方法,在res文件下新建anim文件夹,在其下新建translate.xml文件,内容如下:

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

属性android:fillAfter="true"用于设置运动后停留在该位置。
MainActivity.java中设置关联,即:

public class MainActivity extends AppCompatActivity {private RecyclerView recyclerView;private MyAdapter adapter;private List<String> mList;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.recycleview);MyView myView = findViewById(R.id.myView);// 设置关联,加载定义的动画myView.setAnimation(AnimationUtils.loadAnimation(this, R.anim.translate));}
}

    需要注意的是,View动画并不能改变View的位置参数。如果对一个Button进行如上的平移动画操作,当Button平移300像素停留在当前位置时,我们点击这个Button并不会触发点击事件,但在我们点击这个Button的原始位置时却触发了点击事件。在Android 3.0时出现的属性动画解决了上述问题:

public class MainActivity extends AppCompatActivity {private RecyclerView recyclerView;private MyAdapter adapter;private List<String> mList;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.recycleview);MyView myView = findViewById(R.id.myView);// 从0变化到300ObjectAnimator.ofFloat(myView, "translationX", 0, 300).setDuration(1000).start();}
}

关于属性动画,这里做一个简单的介绍

3.4.1 属性动画

Android中动画分类:

  • 帧动画
  • View动画,它提供了AlphaAnimationRotateAnimationTranslateAnimationScaleAnimation4种动画方式,并提供了AnimationSet动画集合来混合使用多种动画。但不具有交互性,即:当某个元素发生View动画后,其响应事件的位置依然在动画进行前的地方,所以View动画只能做普通的动画效果,要避免涉及交互操。但是它的优点也非常明显:效率比较高,使用也方便
    作。
  • 属性动画,由Android 3.0推出。Animator框架中使用最多的就是AnimatorSetObjectAnimator配合:使用 ObjectAnimator 进行更精细化的控制,控制一个对象和一个属性值,而使用多个ObjectAnimator组合到AnimatorSet形成一个动画。属性动画通过调用属性getset方法来真实地控制一个View的属性值,因此,强大的属性动画框架基本可以实现所有的动画效果

创建一个 ObjectAnimator 只需通过其静态工厂类直接返还一个ObjectAnimator对象。参数包括一个对象和对象的属性名字,但这个属性必须有getset方法,其内部会通过Java反射机制来调用set方法修改对象的属性值。
下面就是一些常用的可以直接使用的属性动画的属性值。

  • translationXtranslationY:用来沿着X轴或者Y轴进行平移。
  • rotationrotationXrotationY:用来围绕View的支点进行旋转。
  • PrivotXPrivotY:控制View对象的支点位置,围绕这个支点进行旋转和缩放变换处理。默认该支点
    位置就是View对象的中心点。
  • alpha:透明度,默认是1(不透明),0代表完全透明。
  • xy:描述View对象在其容器中的最终位置。

如果一个属性没有getset方法,也可以通过自定义一个属性类或包装类来间接地给这个属性增加getset方法。如:
自定义view:

public class MyView extends View {private int lastX;private int lastY;public MyView(Context context) {super(context);}public MyView(Context context, @Nullable AttributeSet attrs) {super(context, attrs);}public MyView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);}
}

对应布局文件:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"><com.example.myapplication.MyViewandroid:layout_height="50dp"android:layout_width="50dp"android:id="@+id/myView"android:background="@color/colorPrimary"/></LinearLayout>

主文件:

public class MainActivity extends AppCompatActivity {private RecyclerView recyclerView;private MyAdapter adapter;private List<String> mList;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);MyView myView = findViewById(R.id.myView);PackageView packageView = new PackageView(myView);ObjectAnimator.ofInt(packageView, "width", 300).setDuration(1000).start();}// 包装类public class PackageView{private View view;PackageView(View view){Log.d("HELLO", "PackageView: "+Boolean.toString(view==null));this.view = view;}public int getWidth(){return view.getLayoutParams().width;}public void setWidth(int width){Log.d("HELLO", "setWidth: "+Boolean.toString(this.view==null));view.getLayoutParams().width = width;// 重新绘制Viewview.requestLayout();}}
}

最终效果,宽度缓慢扩充到300

组合动画 AnimatorSet

上面的ObjectAnimator针对单个属性动画,往往动画比较复杂,不单单涉及一个属性。就需要使用组合动画。
AnimatorSet.Builder中包括以下4个方法。

  • after(Animator anim):将现有动画插入到传入的动画之后执行。
  • after(long delay):将现有动画延迟指定毫秒后执行。
  • before(Animator anim):将现有动画插入到传入的动画之前执行。
  • with(Animator anim):将现有动画和传入的动画同时执行。

如:

public class MainActivity extends AppCompatActivity {private RecyclerView recyclerView;private MyAdapter adapter;private List<String> mList;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);MyView myView = findViewById(R.id.myView);ObjectAnimator animator1 = ObjectAnimator.ofFloat(myView, "translationX", 300);ObjectAnimator animator2 = ObjectAnimator.ofFloat(myView, "scaleX", 1.0f, 2.0f);AnimatorSet set = new AnimatorSet();set.setDuration(1000);set.play(animator1).after(animator2);set.start();}
}

View动画一样,属性动画也可以直接写在XML中。

XML中定制属性动画

前面提到了View也可以指定XML动画,但是其改变后点击事件还是在原来位置上,故而这里的XML属性动画还是ObjectAnimator版本。
同样的,在animator中定义test.xml文件,内容如下:

<?xml version="1.0" encoding="utf-8"?>
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="wrap_content"android:layout_height="wrap_content"android:duration="1000"android:propertyName="scaleX"android:valueFrom="1.0"android:valueTo="3.0"android:valueType="floatType">
</objectAnimator>

主文件:

public class MainActivity extends AppCompatActivity {private RecyclerView recyclerView;private MyAdapter adapter;private List<String> mList;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);MyView myView = findViewById(R.id.myView);myView.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {Toast.makeText(MainActivity.this, "Hello", Toast.LENGTH_SHORT).show();}});Animator animator = AnimatorInflater.loadAnimator(this, R.animator.test);animator.setTarget(myView);animator.start();}
}

3.5 scrollToscollBy

scrollTo(x,y)
表示移动到一个具体的坐标点,而scrollBy(dx,dy)则表示移动的增量为dxdy

case MotionEvent.ACTION_MOVE:int offsetx = x - lastX;int offsety = y - lastY;// 使用((View)getParent()).scrollBy(-offsetx, -offsety);break;

内容参考:

  • 《Android进阶之光》

View与ViewGroup相关推荐

  1. Android精通:View与ViewGroup,LinearLayout线性布局,RelativeLayout相对布局,ListView列表组件...

    UI的描述 对于Android应用程序中,所有用户界面元素都是由View和ViewGroup对象构建的.View是绘制在屏幕上能与用户进行交互的一个对象.而对于ViewGroup来说,则是一个用于存放 ...

  2. 【Android 应用开发】自定义View 和 ViewGroup

    一. 自定义View介绍 自定义View时, 继承View基类, 并实现其中的一些方法. (1) ~ (2) 方法与构造相关 (3) ~ (5) 方法与组件大小位置相关 (6) ~ (9) 方法与触摸 ...

  3. View和ViewGroup常用方法

    2019独角兽企业重金招聘Python工程师标准>>> Android的UI界面都是由View和ViewGroup及其派生类组合而成的. 其中,View是所有UI组件的基类,而 Vi ...

  4. 精通android布局,Android精通:View与ViewGroup,LinearLayout线性布局,RelativeLayout相对布局,ListView列表组件...

    标题图 UI的描述 对于Android应用程序中,所有用户界面元素都是由View和ViewGroup对象构建的.View是绘制在屏幕上能与用户进行交互的一个对象.而对于ViewGroup来说,则是一个 ...

  5. Android(四)——View和ViewGroup

    文章目录 1. 用户界面概述 2. View类常用属性 3. ViewGroup类 4. UI组件的层次结构 1. 用户界面概述 在Android APP中,所有的用户界面元素都是由View和View ...

  6. View和ViewGroup的层次关系

    Layout也是一种View Widget - View Layout - ViewGroup

  7. android应用的界面编程----View与ViewGroup的概念

    1 UI OverView Android中所有的UI元素都是通过View与ViewGroup来构建的,View是指屏幕中一块可与用户进行交互的空白,类似于java界面编程中的JPanel.为了界面布 ...

  8. Android 应用开发(35)---View与ViewGroup的概念

    View与ViewGroup的概念 告别了第一章,迎来第二章--Android中的UI(User Interface)组件的详解, 而本节我们要学习的是所有控件的父类View和ViewGroup类!突 ...

  9. 【Android】Android自定义View和ViewGroup知识点汇总

    一.View的绘制流程 onMeasure()->onDraw(). 二.ViewGroup的绘制流程 onMeasure()->onLayout()->onDraw()(一般不重写 ...

最新文章

  1. CYQ.Data V4系列全面开源(2013-08-04)
  2. 如果产品经理去卖土豆粉
  3. 关于memcached
  4. 软件质量保证计划_如何做好软件项目的质量管理?
  5. java中int,char,string三种类型的相互转换
  6. cocos2dx[3.2](11)——新回调函数std::bind
  7. python 进位_蓝桥杯-Python-高精度加法
  8. mtk camera faq
  9. python自动语音电话_用 Python 实现自己的智能语音助理(百度语音 + 图灵机器人)...
  10. excel饼图按占比从大到小排列
  11. android——java.lang.IllegalStateException: Fatal Exception thrown on Scheduler
  12. 国家也补贴?有华为认证证书的你,就能拿它(附详细操作)
  13. 每日一言:愿你等待的,终有回答
  14. 安卓手机使用Termux软件进行Linux系统的安装
  15. 裴波那契数列(循环实现递归)
  16. 默克高性能材料业务正式更名为“电子科技(Electronics)”
  17. 山野村夫的总提纲!……还是羞于见人啦=////=
  18. 机械键盘Windows键失灵,解决办法
  19. 免疫算法求解多元函数论文
  20. 采集程序 -【开源项目】

热门文章

  1. 铁道部正在研究高铁月票 重申上座率120%准确可靠
  2. 云原生关乎文化,而不是容器
  3. Web3+品牌的大杀器:DAO如何实现对传统品牌彻底的降维打击
  4. Visual Assist 使用小结
  5. 训练神经网络gpu占用率低,外接gpu 训练神经网络
  6. SS2H框架搭建,基础上篇
  7. Android build.prop参数详解
  8. linux 支持7代cpu,Intel第七代cpu有哪些型号
  9. 判断TTS语音朗读是否结束
  10. Pycharm新建文件模板配置