Android View 流程简读 <1>

上一篇大致介绍了如何更新主题风格之类的,没有谈及到一些本质的,这一篇大致介绍一下所有的UI归根结底的View类显示效果流程.


这里面以View背景动画产生流程为主线的.(即,我们经常会遇到各种点击效果,如material的水波纹,或者以前的按钮点击前后的背景效果变化)


看了android源码View.java类

public class View implements Drawable.Callback, KeyEvent.Callback,
        AccessibilityEventSource 

View实现三个接口,其中Drawable.Callback是View图像绘制更新接口:

public static interface Callback {
        /**
         * Called when the drawable needs to be redrawn.  A view at this point
         * should invalidate itself (or at least the part of itself where the
         * drawable appears).
         *
         * @param who The drawable that is requesting the update.
         */
        public void invalidateDrawable(Drawable who);

        /**
         * A Drawable can call this to schedule the next frame of its
         * animation.  An implementation can generally simply call
         * {@link android.os.Handler#postAtTime(Runnable, Object, long)} with
         * the parameters <var>(what, who, when)</var> to perform the
         * scheduling.
         *
         * @param who The drawable being scheduled.
         * @param what The action to execute.
         * @param when The time (in milliseconds) to run.  The timebase is
         *             {@link android.os.SystemClock#uptimeMillis}
         */
        public void scheduleDrawable(Drawable who, Runnable what, long when);

        /**
         * A Drawable can call this to unschedule an action previously
         * scheduled with {@link #scheduleDrawable}.  An implementation can
         * generally simply call
         * {@link android.os.Handler#removeCallbacks(Runnable, Object)} with
         * the parameters <var>(what, who)</var> to unschedule the drawable.
         *
         * @param who The drawable being unscheduled.
         * @param what The action being unscheduled.
         */
        public void unscheduleDrawable(Drawable who, Runnable what);
    }

至于KeyEvent.Callback是触摸按键点击事件回调的接口,用于监听按键消息事件,这个很容易理解,很多UI都会让使用者去点击的.不在贴代码了.

根据上面两个接口,可以这样理解,一个接口用于视图显示刷新和绘制(其实这个是Drawable的事),一个是让View响应按键event,View的视图变化和刷新通过按键事件去驱动,即当key event产生后,回调Drawable回调接口,对视图进行更新.

因为源代码又臭又长,所以我们可以自己写一个类似的开发demo,来简约说明大致工作流程:

<1> : 新建一个android工程:PumpKinCallBack工程:工程结构如下:

<2> : 具体代码如下:

package org.pumpkin.pumpkincallback;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;

import org.pumpkin.pumpkincallback.interfaces.KeyEvent;
import org.pumpkin.pumpkincallback.view.Button;
import org.pumpkin.pumpkincallback.view.TextView;
import org.pumpkin.pumpkincallback.view.View;

public class PumpKinMainActivity extends AppCompatActivity implements KeyEvent.CallBack {

    private static View mview;
    private static TextView textview;
    private static Button button;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_pump_kin_main);

        /*mview=new View();
        mview.raiseKeyDown();*/

        //res id : 1000001
        textview=new TextView();
        textview.setOnKeyDown(this);
        textview.raiseKeyDown();

        //res id : 1000002
        button=new Button();
        button.setOnKeyDown(this);
        button.raiseKeyDown();

    }

    @Override
    public void onKeyDown(int id) {
        Log.i("pumpkin","button keydown !");
        int resid=id;

        switch (resid){
            case 1000001:
                textview.onDraw();
                break;
            case 1000002:
                button.onDraw();
                break;
        }

    }
}

代码很简单,不具体研究了.

package org.pumpkin.pumpkincallback.interfaces;

/**
 * Project name : PumpKinCallBack
 * Created by zhibao.liu on 2016/5/3.
 * Time : 11:19
 * Email warden_sprite@foxmail.com
 * Action : durian
 */
public class KeyEvent {
    public interface CallBack{
        void onKeyDown(int id);
    }
}


package org.pumpkin.pumpkincallback.view;

import android.util.Log;

/**
 * Project name : PumpKinCallBack
 * Created by zhibao.liu on 2016/5/3.
 * Time : 11:29
 * Email warden_sprite@foxmail.com
 * Action : durian
 */
public class Button extends TextView {

    public void onDraw(){
        Log.i("pumpkin","button refresh status and ui !");
    }

}


package org.pumpkin.pumpkincallback.view;

import android.util.Log;

import org.pumpkin.pumpkincallback.interfaces.KeyEvent;

/**
 * Project name : PumpKinCallBack
 * Created by zhibao.liu on 2016/5/3.
 * Time : 11:22
 * Email warden_sprite@foxmail.com
 * Action : durian
 */
public class TextView extends View {

    public TextView(){

        Log.i("pumpkin","textview register ... ");
        //setOnKeyDown(this);

    }

    public void onDraw(){
        Log.i("pumpkin","textview refresh status and ui !");
    }

}


package org.pumpkin.pumpkincallback.view;

import android.util.Log;

import org.pumpkin.pumpkincallback.interfaces.KeyEvent;

/**
 * Project name : PumpKinCallBack
 * Created by zhibao.liu on 2016/5/3.
 * Time : 11:20
 * Email warden_sprite@foxmail.com
 * Action : durian
 */
public class View implements KeyEvent.CallBack {

    private KeyEvent.CallBack callBack;
    public View(){
        Log.i("pumpkin","view register ... ");
        //setOnKeyDown(this);
    }

    public void raiseKeyDown(){
        callBack.onKeyDown(0);
    }

    public void setOnKeyDown(KeyEvent.CallBack callback){
        Log.i("pumpkin","callback : "+callback);
        callBack=callback;
    }

    @Override
    public void onKeyDown(int id) {

        Log.i("pumpkin","view callback start!");
        //Thread.sleep(2500);
        onDraw();
        Log.i("pumpkin","view callback end!");

    }

    public void onDraw(){
        Log.i("pumpkin","view refresh status and ui !");
    }

}


其中raiseKeyDown是模拟人触摸产生KeyEvent事件.这样运行上面的测试程序,就可以发现,Button继承TextView,然后TextView又是继承View.View类实现一个CallBack回调.当调用raiseKeyDown模拟KeyEvent消息时,触发onKeyDown事件,再通过这个事件去更新视图界面.这个Demo是View最简单的一个说明模型.

同理也可以实现Drawable.CallBack接口.这里不再同类模仿了,在后面的程序只给出如何使用这些接口.


然后根据上面的Demo可知,View的视图更新通过KeyEvent驱动,那么这里关于View的构造函数就不过去介绍,基本上和前一篇完全类似.

直接从这里开始:

public boolean onKeyDown(int keyCode, KeyEvent event) {
        boolean result = false;

        switch (keyCode) {
            case KeyEvent.KEYCODE_DPAD_CENTER:
            case KeyEvent.KEYCODE_ENTER: {
                if ((mViewFlags & ENABLED_MASK) == DISABLED) {
                    return true;
                }
                // Long clickable items don't necessarily have to be clickable
                if (((mViewFlags & CLICKABLE) == CLICKABLE ||
                        (mViewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) &&
                        (event.getRepeatCount() == 0)) {
                    setPressed(true);
                    checkForLongClick(0);
                    return true;
                }
                break;
            }
        }
        return result;
    }

然后继续看setPressed方法:

public void setPressed(boolean pressed) {
        final boolean needsRefresh = pressed != ((mPrivateFlags & PFLAG_PRESSED) == PFLAG_PRESSED);

        if (pressed) {
            mPrivateFlags |= PFLAG_PRESSED;
        } else {
            mPrivateFlags &= ~PFLAG_PRESSED;
        }

        if (needsRefresh) {
            refreshDrawableState();
        }
        dispatchSetPressed(pressed);
    }


然后继续:默认pressed是有效的流程:

public void refreshDrawableState() {
        mPrivateFlags |= PFLAG_DRAWABLE_STATE_DIRTY;
        drawableStateChanged();

        ViewParent parent = mParent;
        if (parent != null) {
            parent.childDrawableStateChanged(this);
        }
    }


发现:drawableStateChanged();方法,这个可是Drawable.Callback的其中的一个回调方法:

protected void drawableStateChanged() {
        Drawable d = mBackground;
        if (d != null && d.isStateful()) {
            d.setState(getDrawableState());
        }
    }

上面mBackground即是将要作为View的背景的,可能是动画,可能不是.

那么下面看看mBackground是如何来的:从View构造体开始看,因为构造体是往往要解析style xml文件信息,同时也就设置了背景:

if (background != null) {
            setBackground(background);
        }


public void setBackground(Drawable background) {
        //noinspection deprecation
        setBackgroundDrawable(background);
    }

主要看书上面的方法都是public的,也就是说设置View背景不仅可以通过xml设置也可以通过程序设置.

至于setBackgroundDrawable这个方法有点长,抽出部分来看看:

if (mBackground != null) {
            mBackground.setCallback(null);
            unscheduleDrawable(mBackground);
        }

上面setCallback是上面的回调的反注册.下面un***Drawable取消正在更新的背景效果.

background.setCallback(this);
            if (background.isStateful()) {
                background.setState(getDrawableState());
            }

重新注册回调,并且设置状态,而这些状态是什么,这个状态是xml中配置的那些如:

android.R.attr.state_pressed;

而整个View需要更新变化,就需要更具这些状态判断后进行.


接着后面即把值赋给mBackground:

background.setVisible(getVisibility() == VISIBLE, false);
            mBackground = background;

这样就可以从上面看出,整个View视图的更新是KeyEvent事件进行推动,(当然也可以是其他的事件).

后面再根据上面的提供一个demo来进一步说明View背景在点击效果下是如何更新的.

这样就更加使人能够理解通顺.如果直接完全看源码的话,可能只能够理解,如果能够根据理解做出demo的话,理解才会更加深刻.

后面的继续





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值