自定义TextView——解决ViewGroup不调用OnDraw方法

news/2024/7/3 17:12:18

首先绘制TextView继承于View:直接贴代码

public class TextView extends LinearLayout{

    private String mText;
    private int mTextSize = 18;
    private int mTextColor = Color.BLACK;
    private Paint mPaint;//文字的画笔

    // 构造函数会在代码里面new的时候调用
    // TextView tv = new TextView(this);
    public TextView(Context context) {
        this(context, null);
    }

    // 在布局layout中使用(调用)

    public TextView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    // 在布局layout中使用(调用),但是会有style
    public TextView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        // 获取自定义属性
        TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.TextView);

        mText = array.getString(R.styleable.TextView_yijiaText);
        mTextColor = array.getColor(R.styleable.TextView_yijiaTextColor, mTextColor);
        // 18 18px 18sp
        mTextSize = array.getDimensionPixelSize(R.styleable.TextView_yijiaTextSize,
                sp2px(mTextSize));

        // 回收s
        array.recycle();
        //初始化画笔
        mPaint = new Paint();
        mPaint.setAntiAlias(true);//设置抗锯齿
        mPaint.setColor(mTextColor);//设置画笔颜色
        mPaint.setTextSize(mTextSize);

    }


    /**
     * 自定义View的测量方法
     *
     * @param widthMeasureSpec
     * @param heightMeasureSpec
     */
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        // 布局的宽高都是由这个方法指定
        // 指定控件的宽高,需要测量
        // 获取宽高的模式
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);

        //1.确定的值,这个时候不需要计算,给多少是多少
        int width = MeasureSpec.getSize(widthMeasureSpec);
        int height = MeasureSpec.getSize(heightMeasureSpec);
        //2.给的是不确定值
        if (widthMode == MeasureSpec.AT_MOST) {
            //计算的宽度 与字体的长度有关 与字体的大小 用画笔来测量
            Rect rect = new Rect();
            //获取文本的矩形
            mPaint.getTextBounds(mText, 0, mText.length(), rect);
            width = rect.width();
        }

        //2.给的是不确定值
        if (heightMode == MeasureSpec.AT_MOST) {
            //计算的宽度 与字体的长度有关 与字体的大小 用画笔来测量
            Rect rect = new Rect();
            //获取文本的矩形
            mPaint.getTextBounds(mText, 0, mText.length(), rect);
            height = rect.height();
        }
        setMeasuredDimension(width, height);
    }

    /**
     * 用于绘制
     *
     * @param canvas
     */
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        Rect rect = new Rect();
        mPaint.getTextBounds(mText, 0, mText.length(), rect);
        int dx = getWidth() / 2 - rect.width() / 2;

        //获取中心(fontMetrics.bottom - fontMetrics.top) / 2
        Paint.FontMetricsInt fontMetrics = mPaint.getFontMetricsInt();

        int dy = (fontMetrics.bottom - fontMetrics.top) / 2 - fontMetrics.bottom;
        int baseLine = getHeight() / 2 + dy;
        canvas.drawText(mText, dx, baseLine, mPaint);
        Log.e("TAG", "dy==" + dy + ",centerY==" + getHeight() / 2+",baseLine=="+baseLine);
    }


    private int sp2px(int spValue) {
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, spValue,
                getResources().getDisplayMetrics());
    }
}

源码分析为什么ViewGroup不调用onDraw,而设置背景后又可以显示自定义的TextView

  • 首先看View中调用onDraw的源码
 // Step 3, draw the content
            if (!dirtyOpaque) onDraw(canvas);

            // Step 4, draw the children
            dispatchDraw(canvas);

            // Overlay is part of the content and draws beneath Foreground
            if (mOverlay != null && !mOverlay.isEmpty()) {
                mOverlay.getOverlayView().dispatchDraw(canvas);
            }

            // Step 6, draw decorations (foreground, scrollbars)
            onDrawForeground(canvas);

可以看到当dirtyOpaque为false时调用OnDraw

  • dirtyOpaque源码
 final int privateFlags = mPrivateFlags;
        final boolean dirtyOpaque = (privateFlags & PFLAG_DIRTY_MASK) == PFLAG_DIRTY_OPAQUE &&
                (mAttachInfo == null || !mAttachInfo.mIgnoreDirtyState);
        mPrivateFlags = (privateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DRAWN;

可以看到是由privateFlags控制,即mPrivateFlags

  • 看view的构造方法
 computeOpaqueFlags();
  • 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构造方法源码
 super(context, attrs, defStyleAttr, defStyleRes);
        initViewGroup();
        initFromAttributes(context, attrs, defStyleAttr, defStyleRes);
  • 查看initViewGroup源码
  if (!debugDraw()) {
            setFlags(WILL_NOT_DRAW, DRAW_MASK);
        }

可以看到最后因为setFlags的原因导致viewGrop中的OnDraw不可见。

为什么:viewgroup中设置背景后又可以显示

  • view中setBackDrawable的源码
   computeOpaqueFlags();

        if (background == mBackground) {
            return;
        }

此时发现又重新计算了

个人觉得解决方法可以有三

  • ①onDraw换成dispatchDraw
  • ②设置背景透明
  • ③看源码可以知道设置setFlags的值即可
public void setWillNotDraw(boolean willNotDraw) {
        setFlags(willNotDraw ? WILL_NOT_DRAW : 0, DRAW_MASK);
    }

http://www.niftyadmin.cn/n/3648869.html

相关文章

Android Material Design-Maintaining Compatibility(保持兼容性)

一些materialdesign中的功能像material主题和自定义activity的过渡只能在Android 5.0(API级别21)或以上的系统版本中才能使用。但是你可以设计你的app去使用那些功能,无论在支持materialdesign的设备上亦或是早期的Android版本上。 定义可选的样式 你可以配置你的ap…

[Regex]Greta不支持“Named Groups”特性

微软研究院出品的Greta(http://research.microsoft.com/projects/greta/)是一款非常优秀的正则表达式模板类库,GRETA的匹配速度比boost(http://www.boost.org)正则表达式库大约快7倍,比ATL7的CATLRegExp快10倍。你可以通过http://…

盖茨比乔布斯_在盖茨比中使用React Hooks

盖茨比乔布斯JavaScript treats functions as first-class citizens. And we can see this in React now more than ever with the introduction of Hooks in version 16.8. They allow for state manipulation and side-effects on functional components. JavaScript将函数视…

android热修复——自己做个热修复

类的加载机制 需要注意的地方 1.每次生成之后一定要测试;2.尽量的不要分包,不要分多个dex3.混淆的时候,设计到NDK AndFix.java 不要混淆4.生成包之后一般会加固什么的,这个时候生成的差分包,一定要在之前去生成。5.…

Android系统权限配置详解

Android 权限控制代码分析 前在文章介绍过android系统管理层次:http://blog.csdn.net/andyhuabing/article/details/7030212 ,这里就核心代码分析一下 android系统充分利用了linux的用户权限管理方法,所以如果需要移植到其它系统&#xff0c…

自己动手搭建数据库框架

首先IDaoSupport接口&#xff0c;有查询&#xff0c;插入单条数据&#xff0c;批量插入&#xff0c;删除&#xff0c;更新 public interface IDaoSupport<T> {void init(SQLiteDatabase sqLiteDatabase, Class<T> clazz);// 插入数据public long insert(T t);// 批…

关于软件的架构设计

好的开始相当于成功一半 开始之初的架构设计决定着软件产品的生死存亡。“好的开始相当于成功一半”。 开始的架构设计也是最难的&#xff0c;需要调研同类产品的情况以及技术特征&#xff0c;了解当前世界上对这种产品所能提供的理论支持和技术平台支持。再结合自己项目的特点…

javascript递归_通过JavaScript了解递归和记忆

javascript递归In this article, you’re going to learn how to use recursive programming, what it is, and how to optimize it for use in algorithms. We’ll be using JavaScript as our programming language of choice to understand the concept of recursion. 在本文…