揭秘 Android Graphics2D 实现动态效果之——invalidate()

最近在研读Android 自定义控件方面的知识,从刚开始的 创建一个简单的位图对象,在上面绘制图形,到今天的 如何通过绘图实现动态效果章节,突然感觉自己以前了解的Android 自定义view 仅仅皮毛而已。。。

就着今天所看的内容,大家可以和我重新认识下  invalidate() 方法 在整个View 绘制中到底扮演一个怎样的角色?以下内容参阅李赞红老师 自定义组件详解,如有纰漏,请多指教!

一、Android 如何通过绘图实现动态效果

使用 Graphics2D 实现动态效果,所说的动态效果主要包括两方面:1、让画面动起来,如实现游戏中的爆炸动画,地球仪的自转和公转,小鸟翅膀的摆动,手表时针分针秒针的转运等等,分析可知,可以通过过周期性重画实现;2、是实现和用户的互动,用户通过手指在手机屏幕上移动,在绘图区绘制曲线、矩形、圆、文字等图案;

在绘图过程中,双缓存技术是一项很重要的技术,一方面能大大提高绘图的效率,另一方面可以实现绘图过程与结果分离,拥有身临其境的用户体验。理解和掌握双缓存技术的作用和意义是我们真正掌握 Graphics2D 的关键性因素之一。

 了解更多,请移步:了解 Android 双缓冲技术绘图机制

定义一个继承自 View 的子类,重写 onDraw(),在该方法中绘图,当 View 显示时会回调 onDraw()方法,用于绘制组件的外观。

/**
 * Created by ${lxb} on 2019/5/15.
 * 邮箱:207***1410@qq.com
 * TIP:自定义view ,重写onDraw()绘图   小球左右移动
 */

public class MovingGraghicView extends View {

    /*小球的垂直位置,固定为 100*/
    private static final int Y = 100;
    /*小球的水平位置*/
    private int x;
    /*小球的半径*/
    private static final int RADIUS = 30;
    /*小球的颜色*/
    private static final int COLOR = Color.RED;
    /*小球移动的方向*/
    private boolean direction;
    private Paint paint;

    public MovingGraghicView(Context context) {//动态实例化view用到;
        super(context);
    }

    public MovingGraghicView(Context context, @Nullable AttributeSet attrs) {//在xml 用到;
        super(context, attrs);
        //初始化画笔 参数表示去锯齿
        paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        paint.setColor(COLOR);
        x = RADIUS;
    }

    public MovingGraghicView(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);
        //获取组件的宽度
        int measuredWidth = this.getMeasuredWidth();

        if (x <= RADIUS) {
            direction = true;
        }
        if (x >= measuredWidth - RADIUS) {
            direction = false;
        }
        x = direction ? x + 5 : x - 5;

    }
}

定义了有两个参数的构造方法,如果在布局文件中定义了该组件,则会调用此构造方法来创建对象。

activity_plotting_moving_graphic.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <!--左右移动的小球-->
    <angqin.myapplication.custom_view.MovingGraghicView
        android:id="@+id/mgv_ball"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

</LinearLayout>

 那么,小球是如何水平移动的呢?让动画动起来,通过周期性重画来实现,在 Activity 中恰恰是通过定时器周期性调用了 invalidate()方法不断重绘组件,也就是不断调用 onDraw()方法,因为小球的位置由 x 来决定,onDraw()每调用一次,x 的值就会变化一次,小球的位置自然也会跟着改变,小球也就动起来了。

/**
 * Created by ${lxb} on 2019/5/15.
 * 邮箱:207***1410@qq.com
 * TIP:如何通过绘图实现动态效果
 * 使用 Graphics2D 实现动态效果
 */

public class PlottingMovingGraphicAty extends AppCompatActivity {
    @Bind(R.id.mgv_ball)
    MovingGraghicView mgvBall;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_plotting_moving_graphic);
        ButterKnife.bind(this);

        //通过定时器,周期性的调用invlidate(),不断重绘小球,也就是不断调用onDraw()方法,每调一次,x 值会变化一次,自然小球也就移动起来了
        //通过 Timer 类定义一个计时器,延时 200 毫秒开始计时,每隔 50 毫秒计时一次。
        // 定时任务类 TimerTask 其实就是一个子线程,只能调用 postInvalidate()方法来重绘组件
        new Timer().schedule(new TimerTask() {
            @Override
            public void run() {
                mgvBall.postInvalidate();
            }
        },200,50);

    }
}

 

录屏
录屏

二、invalidate()方法 到底干了什么?

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()方法只能在 UI 线程中调用,如果是在子线程中刷新组件,View 类还定义了另一组名为 postInvalidate 的方法:
1、public  void  postInvalidate()
2、public  void  postInvalidate(int  left,  int  top,  int  right,  int  bottom)

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值