自定义view - 面试题

1. 自定义TextView继承View文字可以显示,继承ViewGroup(LinearLayout)不显示?

继承LinearLayout,如果在 activity_mytextview 中设置background,文字可以显示,不设置background文字就不能显示;

ViewGroup默认不调用onDraw

原因是:LinearLayout继承ViewGroup,ViewGroup继承View,在view中有 draw(),所以自定义TextView的onDraw其实调用的是 draw中的方法,draw用的是模板设计模式,如下:

if (!dirtyOpaque) onDraw(canvas);
dispatchDraw(canvas);
onDrawForeground
public void draw(Canvas canvas){
    // 由这里可知,这里需要知道 mPrivateFlags 在哪里赋值的
    final int privateFlags = mPrivateFlags;
    // dirtyOpaque 
    final boolean dirtyOpaque = (privateFlags & PFLAG_DIRTY_MASK) == PFLAG_DIRTY_OPAQUE &&
                (mAttachInfo == null || !mAttachInfo.mIgnoreDirtyState);

            // dirtyOpaque为 false,会调用 onDraw
            if (!dirtyOpaque) onDraw(canvas);

            // Step 4, draw the children
            dispatchDraw(canvas);
            // Step 6, draw decorations (foreground, scrollbars)
            onDrawForeground(canvas);
}

dirtyOpaque为false,会调用onDraw

// View构造方法
public View(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
    computeOpaqueFlags();
}
protected void computeOpaqueFlags() {
        // Opaque if:
        //   - Has a background
        //   - Background is opaque
        //   - Doesn't have scrollbars or scrollbars overlay

        if (mBackground != null && mBackground.getOpacity() == PixelFormat.OPAQUE) {
            mPrivateFlags |= PFLAG_OPAQUE_BACKGROUND;
        } else {
            mPrivateFlags &= ~PFLAG_OPAQUE_BACKGROUND;
        }

        final int flags = mViewFlags;
        if (((flags & SCROLLBARS_VERTICAL) == 0 && (flags & SCROLLBARS_HORIZONTAL) == 0) ||
                (flags & SCROLLBARS_STYLE_MASK) == SCROLLBARS_INSIDE_OVERLAY ||
                (flags & SCROLLBARS_STYLE_MASK) == SCROLLBARS_OUTSIDE_OVERLAY) {
            mPrivateFlags |= PFLAG_OPAQUE_SCROLLBARS;
        } else {
            mPrivateFlags &= ~PFLAG_OPAQUE_SCROLLBARS;
        }
    }
// ViewGroup构造方法中的 initViewGroup() 
private void initViewGroup() {
    if (!debugDraw()) {
            // 这个是View中的 setFlag,默认不画任何东西
            setFlags(WILL_NOT_DRAW, DRAW_MASK);
        }
}

在draw()构造方法中调用 computeOpaqueFlags,ViewGroup中不能显示TextView,因为在ViewGroup中的构造方法中会调用 initViewGroup,这个方法中的 setFlags(WILL_NOT_DRAW, DRAW_MASK) 中的 WILL_NOT_DRAW默认不画任何东西,进不去 if (!dirtyOpaque) onDraw(canvas),所以不能显示;

在布局文件中设置background,就能显示,是因为在 xml文件中设置 background后,会调用 setBackgroundDrawable(),在该方法中会调用 computeOpaqueFlags重新计算,所以TextView会显示;

    // 在xml中设置 background后,会调用 setBackgroundDrawable
    @Deprecated
    public void setBackgroundDrawable(Drawable background) {
        // 重新调用 computeOpaqueFlags,会去重新计算
        computeOpaqueFlags();
    }

TextView继承LinearLayout,在xml中,不设置background,还想要让TextView显示,可以用如下方式:
1>:把 onDraw 改为 dispatchDraw;
2>:如果在xml中没有设置 background,直接在 第三个构造方法中设置 透明背景;
3>:在第三个构造方法中写 setWillNotDraw(false);

代码已上传至github
https://github.com/shuai999/View_day02.git

2. Activity的启动流程及有时候获取TextView高度获取不到原因分析?

public class MainActivity extends AppCompatActivity {

    private TextView text_view;

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

        text_view = (TextView) findViewById(R.id.text_view);
        Log.e("TAG" , "height1 -> "+text_view.getMeasuredHeight()) ;   //height1 -> 0


        //这种情况是不能获取view高度的 , 因为传递的null,父布局为null
        View view = View.inflate(this , R.layout.activity_main , null) ;

        //这种情况是可以获取view高度的 , 需要传递ViewGroup ,
//        View view = View.inflate(this , R.layout.activity_main , text_view) ;


        text_view.post(new Runnable() {
        //只是把Runnable保存到queue<队列>中,什么都没干,会在dispatchAttachedToWindow中执行,而这个方法会在测量完毕后调用,然后在这个方法的下边会执行executeActions()
            @Override
            public void run() {
               Log.e("TAG" , "height2 -> "+text_view.getMeasuredHeight()) ; //height2 -> 1710
            }
        }) ;
    }

    @Override
    protected void onResume() {
        super.onResume();
        Log.e("TAG" , "height3 -> "+text_view.getMeasuredHeight()) ; //height3 -> 0
    }
}
结果如下:

height1 -> 高度为0:onCreate中调用 setContentView,而 setContentView中只是创建 DecorView对象,然后把 setContentView的布局 添加到 DecorView中,没有调用 onMeasure,所以高度为0;
height3 -> 高度为0:由Activity启动流程知:performLaunchActivity__onResume,没有调用 onMeasure,所以高度为0;

你可能感兴趣的:(自定义view - 面试题)