Android 中自定义View,实现小球往复运动

简介: Android 中自定义View,实现小球往复运动

一、介绍如何实现小球的往复运动,实现原理

1、View 类定义了一组 invalidate()方法,该方法有好几个版本:

  1. public void invalidate()
  2. public void invalidate(int l, int t, int r, int b)
  3. public void invalidate(Rect dirty)

invalidate()用于重绘组件,不带参数表示重绘整个视图区域,带参数表示重绘指定的区域。

如果要去追溯该方法的源码,大概就是将重绘请求一级级往上交到 ViewRoot,调用 ViewRoot的 scheduleTraversals()方法重新发起重绘请求,scheduleTraversals()方法会发送一个异步消息,调用 performTraversals()方法执行重绘,而 performTraversals()方法最终调用 onDraw()方法。所以,简单来说,调用 View 的 invalidate()方法就相当于调用了 onDraw()方法,

而 onDraw()方法中就是我们编写的绘图代码。

如果要刷新组件或者让画面动起来,我们只需调用 invalidate()方法即可。通过改变数据来影响绘制结果,这是实现组件刷新或实现动画的基本思路。

invalidate()方法只能在 UI 线程中调用,如果是在子线程中刷新组件,View 类还定义了另一组名为 postInvalidate 的方法:

  1. public void postInvalidate()
  2. public void postInvalidate(int left, int top, int right, int bottom)

二、接下来我们就创建自定义View实现小球的往复运动

1、首先创建自定义View类BallMoveView

/**
 * 球往复运动
 */
public class BallMoveView extends View {
    //小球的水平位置
    private int x;
    //小球的垂直位置,固定为100
    private static final int Y = 100;
    //小球的半径
    private static final int RADIUS = 30;
    //小球的颜色
    private static final int COLOR = Color.RED;
    //声明画笔对象
    private Paint paint;
    //移动的方向
    private boolean direction;
    //该构造函数会在代码里面new的时候调用
    //当不需要使用xml声明或者不需要使用inflate动态加载的时候,实现此构造函数即可
    public BallMoveView(Context context) {
        super(context);
    }
    //在布局layout中使用时调用
    //在布局文件中定义了该组件,则会调用此构造方法来创建对象
    // 当需要在xml中声明此控件,则需要实现此构造函数。
    // 并且在构造函数中把自定义的属性与控件的数据成员连接起来。
    public BallMoveView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        //初始化画笔,参数表示抗锯齿
        paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        paint.setColor(COLOR);
        x = RADIUS;
    }
    //接受一个style资源
    public BallMoveView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //根据x,y的坐标值画一个小球
        canvas.drawCircle(x, Y, RADIUS, paint);
        //改变 x 坐标的值,调用 invalidate()方法后,
        //小球将因 x 的值发生改变而产生移动的效果
        int width = this.getMeasuredWidth();//获取组件的宽度
        //小球的水平位置 x 值小于等于小球的半径,说明小球已到达左边边
        //界
        if (x <= RADIUS) {
            direction = true;
        }
        //小球的水平位置 x 值大于等于组件的宽度减去小球的半径,
        // 说明小球已到达右边边界
        if (x >= width - RADIUS) {
            direction = false;
        }
        x = direction ? x + 5 : x - 5;
    }
}

2、在对应的activity_view1.xml布局文件中引用

<LinearLayout 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"
    android:orientation="vertical"
    tools:context=".View1Activity">
    <com.example.alertdialog.view.BallMoveView
        android:id="@+id/ballMoveView"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</LinearLayout>

3、在 View1Activity 中恰恰是通过定时器周期性调用了 invalidate()方法不断重绘组件,也就是不断调用 onDraw()方法,因为小球的位置由 x 来决定,onDraw()每调用一次,x 的值就会变化一次,小球绘制的位置自然也会跟着一起改变,最后形成了小球移动的效果。

具体代码如下:

public class View1Activity extends AppCompatActivity {
    private BallMoveView ballMoveView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_view1);
        ballMoveView = findViewById(R.id.ballMoveView);
        tv = findViewById(R.id.tv);
        new Timer().schedule(new TimerTask() {
            @Override
            public void run() {
                ballMoveView.postInvalidate();
            }
        },0,50); //参数二表示:任务执行前的延迟毫秒数,参数三表示:连续任务执行间的时间为50毫秒
      }
    }

注意:上面代码中,通过 Timer 类定义一个计时器,延时 0 毫秒开始计时,每隔 50 毫秒计时一次。

定时任务类 TimerTask 其实就是一个子线程,所以,不能使用只能运行在 UI 线程中的invalidate()方法而只能调用 postInvalidate()方法来重绘组件。

具体效果如下:


目录
相关文章
|
1月前
|
数据可视化 Android开发 开发者
安卓应用开发中的自定义View组件
【10月更文挑战第5天】在安卓应用开发中,自定义View组件是提升用户交互体验的利器。本篇将深入探讨如何从零开始创建自定义View,包括设计理念、实现步骤以及性能优化技巧,帮助开发者打造流畅且富有创意的用户界面。
80 0
|
15天前
|
搜索推荐 前端开发 Android开发
安卓应用开发中的自定义视图实现
【10月更文挑战第30天】在安卓开发的海洋中,自定义视图是那抹不可或缺的亮色,它为应用界面的个性化和交互体验的提升提供了无限可能。本文将深入探讨如何在安卓平台创建自定义视图,并展示如何通过代码实现这一过程。我们将从基础出发,逐步引导你理解自定义视图的核心概念,然后通过一个实际的代码示例,详细讲解如何将理论应用于实践,最终实现一个美观且具有良好用户体验的自定义控件。无论你是想提高自己的开发技能,还是仅仅出于对安卓开发的兴趣,这篇文章都将为你提供价值。
|
16天前
|
Android开发 开发者 UED
安卓开发中自定义View的实现与性能优化
【10月更文挑战第28天】在安卓开发领域,自定义View是提升应用界面独特性和用户体验的重要手段。本文将深入探讨如何高效地创建和管理自定义View,以及如何通过代码和性能调优来确保流畅的交互体验。我们将一起学习自定义View的生命周期、绘图基础和事件处理,进而探索内存和布局优化技巧,最终实现既美观又高效的安卓界面。
28 5
|
24天前
|
缓存 数据处理 Android开发
在 Android 中使用 RxJava 更新 View
【10月更文挑战第20天】使用 RxJava 来更新 View 可以提供更优雅、更高效的解决方案。通过合理地运用操作符和订阅机制,我们能够轻松地处理异步数据并在主线程中进行 View 的更新。在实际应用中,需要根据具体情况进行灵活运用,并注意相关的注意事项和性能优化,以确保应用的稳定性和流畅性。可以通过不断的实践和探索,进一步掌握在 Android 中使用 RxJava 更新 View 的技巧和方法,为开发高质量的 Android 应用提供有力支持。
|
24天前
|
缓存 调度 Android开发
Android 在子线程更新 View
【10月更文挑战第21天】在 Android 开发中,虽然不能直接在子线程更新 View,但通过使用 Handler、AsyncTask 或 RxJava 等方法,可以实现子线程操作并在主线程更新 View 的目的。在实际应用中,需要根据具体情况选择合适的方法,并注意相关的注意事项和性能优化,以确保应用的稳定性和流畅性。可以通过不断的实践和探索,进一步掌握在子线程更新 View 的技巧和方法,为开发高质量的 Android 应用提供支持。
30 2
|
25天前
|
XML 前端开发 Android开发
Android面试高频知识点(3) 详解Android View的绘制流程
Android面试高频知识点(3) 详解Android View的绘制流程
Android面试高频知识点(3) 详解Android View的绘制流程
|
28天前
|
XML 前端开发 Android开发
Android面试高频知识点(3) 详解Android View的绘制流程
Android面试高频知识点(3) 详解Android View的绘制流程
24 2
|
1月前
|
XML 前端开发 Java
安卓应用开发中的自定义View组件
【10月更文挑战第5天】自定义View是安卓应用开发的一块基石,它为开发者提供了无限的可能。通过掌握其原理和实现方法,可以创造出既美观又实用的用户界面。本文将引导你了解自定义View的创建过程,包括绘制技巧、事件处理以及性能优化等关键步骤。
|
1月前
|
XML 前端开发 Android开发
Android View的绘制流程和原理详细解说
Android View的绘制流程和原理详细解说
35 3
|
3月前
|
XML 前端开发 Android开发
Android面试高频知识点(3) 详解Android View的绘制流程
View的绘制和事件处理是两个重要的主题,上一篇《图解 Android事件分发机制》已经把事件的分发机制讲得比较详细了,这一篇是针对View的绘制,View的绘制如果你有所了解,基本分为measure、layout、draw 过程,其中比较难理解就是measure过程,所以本篇文章大幅笔地分析measure过程,相对讲得比较详细,文章也比较长,如果你对View的绘制还不是很懂,对measure过程掌握得不是很深刻,那么耐心点,看完这篇文章,相信你会有所收获的。
116 3