自定义view - 面试题

作者: 世道无情 | 来源:发表于2019-02-12 14:31 被阅读277次
    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 - 面试题

        本文链接:https://www.haomeiwen.com/subject/rncelqtx.html