Invalidate和postInvalidate的区别

       在Android实际开发中,我们常常需要进行View的刷新(例如:自定义控件等),Android中为我们提供了两组刷新View的方法—-Invalidate和postInvalidate,前者是在UI线程中更新View,而后者是在非UI线程中更新VIew,两组方法的具体区别如下介绍:
       我们都知道,在Android中,更新UI的操作要在UI线程(也就是主线程)中进行,因为Android UI操作并不是线程安全的,但是这些操作必须在UI线程中调用(单线程模式,这里就不在详细介绍了,可以自己上网查阅)。

Invalidate()方法:

       对于初学者来说,我们常用的更新UI的方式不外乎就是利用Handler机制,在工作线程中执行耗时操作等,然后发消息给UI线程,进行UI操作。以下我们以自定义控件来介绍该用法(自定义控件,实现一个圆环不停的刷新大小,由小变大):
自定义控件,绘制一个圆环,并更新其大小“`

public class MyView extends View {
    /**
     * 画笔
     */
    private Paint mPaint ;
    /**
     * 自定义控件半径
     */
    private int radiu ;

    public MyView(Context context) {
        this(context, null);
    }   
    public MyView(Context context, AttributeSet attrs) {
        super(context, attrs);
        // 初始化画笔
        initPaint() ;
    }

    public MyView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    /**
     * 初始化画笔方法
     */
    private void initPaint() {
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG) ;
        mPaint.setStyle(Style.STROKE) ;
        mPaint.setColor(Color.RED) ;
        mPaint.setStrokeWidth(20) ;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        // 执行绘制
        canvas.drawCircle(150, 150, radiu, mPaint) ;
    }

    public synchronized void setRadiu(int radiu) {
        this.radiu = radiu ;    
        // 刷新,即重新绘制
        invalidate() ;
    }

}

然后在MainActivity中:

public class MainActivity extends Activity {
    /**
     * 自定义控件对象
     */
    private MyView myView ;
    /**
     * 自定义控件的半径
     */
    private int radiu ;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        myView = (MyView) findViewById(R.id.myview) ;

        // 开启线程
        new Thread(new Runnable() {

            @Override
            public void run() {

                try {
                    // 死循环,不停的刷新View
                    while(true) {
                        // 当半径小于200时,开始自增
                        if(radiu <= 200) {
                            radiu += 10 ;
                            // 发送消息给Handler处理
                              mHandler.obtainMessage().sendToTarget() ;
                        } else {
                            radiu = 0 ;
                        }
                        // 每隔40毫秒睡一次
                        Thread.sleep(40) ;
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }
        }).start() ;

    }

    @SuppressLint("HandlerLeak")
    private Handler mHandler = new Handler() {

        public void handleMessage(android.os.Message msg) {
            // 设置自定义控件的半径
            myView.setRadiu(radiu) ;
        };

    } ;

    protected void onDestroy() {
        super.onDestroy() ;
        // 界面销毁后移除Handler的引用
        mHandler.removeCallbacksAndMessages(null) ;
    };

}

       以上这种方式即为在非UI线程中执行逻辑代码,然后利用Handler机制发送消息通知UI线程进行更新UI的操作。

postInvalidate()方法:

       而postInvalidate()可以直接在非UI线程中刷新,不需要使用Handler机制,代码逻辑也相对比较简单:

public class MyView extends View implements Runnable {
    /**
     * 画笔
     */
    private Paint mPaint ;
    /**
     * 自定义控件半径
     */
    private int radiu ;

    public MyView(Context context) {
        this(context, null);
    }

    public MyView(Context context, AttributeSet attrs) {
        super(context, attrs);
        // 初始化画笔
        initPaint() ;
    }

    public MyView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }
    /**
     * 初始化画笔方法
     */
    private void initPaint() {
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG) ;
        mPaint.setStyle(Style.STROKE) ;
        mPaint.setColor(Color.RED) ;
        mPaint.setStrokeWidth(20) ;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        // 执行绘制
        canvas.drawCircle(150, 150, radiu, mPaint) ;
    }

    @Override
    public void run() {

        while(true) {

            try {
                if(radiu <= 150) {
                    radiu += 10 ;

                    postInvalidate() ;
                } else {
                    radiu = 0 ;
                }
                Thread.sleep(40) ;
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }

    }

}   

在MainActivity中,开启线程更新View:

public class MainActivity extends Activity {
    /**
     * 自定义控件对象
     */
    private MyView myView ;
    /**
     * 自定义控件的半径
     */
    private int radiu ;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        myView = (MyView) findViewById(R.id.myview) ;
        // 开启线程刷新界面
        new Thread(myView).start() ;

    }

       以上两种方式,是我在利用自定义控件时使用的,可能对于不熟悉自定义控件的初学者来说看着会有点乱,其实仔细阅读以下,就会发现,并不是那么难,第一种方式中,我们选择利用Handler机制来处理,利用消息机制来通知UI线程更新View操作,所以,调用Invalidate()的方法(即setRadiu())是运行在UI线程的,对于第二种方法,我们将更新View的方法放在了一个子线程中(即Runnable接口的run方法中),然后在调用该自定义控件时开启一个线程来操作,所以postInvalidate()方法是运行在子线程的。
       我们在学习Android时,要记住一点,代码千变万化,万变不离其宗,代码稍微一改就会不一样,但其调用的实质是一样的。希望这篇博客能给大家一点帮助,若有解释不合理的地方,希望大家提出宝贵的意见。

发布了32 篇原创文章 · 获赞 14 · 访问量 6万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章