android中初步自定义view(代码片段)

fanfan-公众号-码农修仙儿 fanfan-公众号-码农修仙儿     2022-11-29     782

关键词:

在研究了几个星期的view之后,打算自定义个view巩固检验一下最近学的知识,view知识相关博文

Android6.0源码分析之View(一)


Android6.0源码分析之View(二)--measure


Android中View研究自学之路

Chapter One,自定义一个显示文本的自定义view

首先,定义一个继承自view的子类Customview

public class CustomView extends View 


    private String mCustomTitle;//标题文本
    private int mTitleTxtSize;//标题字体大小
    private int mTitleTxtColor;//标题文本颜色

    private String mCustomCont;//正文文本
    private int mContTxtSize;//正文字体大小
    private int mContTxtColor;//正文文本颜色
    private Rect mBounds;

    private TextPaint mPaint;
    public CustomView(Context context) 
        super(context);
    

    public CustomView(Context context, AttributeSet attrs) 
        this(context, attrs,0);


    

    public CustomView(Context context, AttributeSet attrs, int defStyleAttr) 
        super(context, attrs, defStyleAttr);
        mPaint = new TextPaint();
        mBounds = new Rect();
        TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.CustomViewStyle);

        //从添加view的xml文件中获取到view的相关属性信息
        //标题相关属性
        mCustomTitle = array.getString(R.styleable.CustomViewStyle_customTitle);
        mTitleTxtSize = array.getDimensionPixelSize(R.styleable.CustomViewStyle_titleTxtSize,
                (int) getResources().getDimension(R.dimen.title_default_size));
        mTitleTxtColor= array.getColor(R.styleable.CustomViewStyle_titleTxtColor,
                Color.BLACK);

        //正文文本相关属性
        mCustomCont = array.getString(R.styleable.CustomViewStyle_customCont);
        mContTxtSize = array.getDimensionPixelSize(R.styleable.CustomViewStyle_contTxtSize,
                (int) getResources().getDimension(R.dimen.cont_default_size));
        mContTxtColor = array.getColor(R.styleable.CustomViewStyle_contTxtColor,Color.GRAY);

        array.recycle();

    

对于一个view在xml中相关的 属性有限,我们需要添加自己想要的属性,添加方式也很简单,

第一步那就是在values目录下创建一个resource为节点的资源文件,把想要的属性添加进去

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="CustomViewStyle">
        <attr name="customTitle" format="string"/>
        <attr name="customCont" format="string"/>
        <attr name="titleTxtSize" format="dimension"/>
        <attr name="contTxtSize" format="dimension"/>
        <attr name="titleTxtColor" format="color"/>
        <attr name="contTxtColor" format="color"/>
    </declare-styleable>
</resources>

第二步,在添加属性成功后,在构造方法中引用该styleable

第三步,现在,可以在xml文件中使用了

<com.fang.zrf.customview.widges.CustomView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        custom:customTitle="@string/custom_view_title"
        custom:customCont="@string/custom_view_cont"
        android:paddingLeft="50dp"/>

使用的时候是以custom开头,一般开发工具会提示你添加命名空间

xmlns:custom="http://schemas.android.com/apk/res-auto"

把这句话添加在根节点下,到现在,你就可以为你的view自定义属性了


属性添加成功后可以进行测量,布局和绘制了。

即需要重写onMeasure和onDraw方法。

这样整体来看,其实自定义view也不是很麻烦。总结下来就是

第一,先定义自己的view类

第二,创建资源文件添加view的属性

第三,在onMeasure方法中测量view所需要显示的大小

第四,在onDraw中借助画笔和画布把view绘制出来。


恩~看着确实挺简单,实现起来真是问题层出不穷


Chapter Two,所遇到的问题

转载请注明出处 

Android中初步自定义view

问题1 ,Paint画笔对象为null的异常

 FATAL EXCEPTION: main
  Process: com.fang.zrf.mycustomview, PID: 12882
  java.lang.NullPointerException: Attempt to invoke virtual method 'int java.lang.String.length()' on a null object reference
  at android.graphics.Canvas.drawText(Canvas.java:1656)
  at com.fang.zrf.mycustomview.widgets.CustomViewSec.onDraw(CustomViewSec.java:76)
  at android.view.View.draw(View.java:16207)

刚开始怎么也想不明白,明明创建了Paint的对象,为什么还会为null。到最后发现真是自己的粗心大意。看看我的代码,你能看出来问题吗???

public CustomView(Context context) 
        super(context);
    

    public CustomView(Context context, AttributeSet attrs) 
        super(context, attrs);

       
    

    public CustomView(Context context, AttributeSet attrs, int defStyleAttr) 
        super(context, attrs, defStyleAttr);
        mPaint = new Paint();
        TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.CustomViewStyle);

        //从添加view的xml文件中获取到view的相关属性信息
        //标题相关属性
        mCustomTitle = array.getString(R.styleable.CustomViewStyle_customTitle);
        mTitleTxtSize = array.getDimensionPixelSize(R.styleable.CustomViewStyle_titleTxtSize,
                (int) getResources().getDimension(R.dimen.title_default_size));
        mTitleTxtColor= array.getColor(R.styleable.CustomViewStyle_titleTxtColor,
                Color.BLACK);

        //正文文本相关属性
        mCustomCont = array.getString(R.styleable.CustomViewStyle_customCont);
        mContTxtSize = array.getDimensionPixelSize(R.styleable.CustomViewStyle_contTxtSize,
                (int) getResources().getDimension(R.dimen.cont_default_size));
        mContTxtColor = array.getColor(R.styleable.CustomViewStyle_contTxtColor,Color.GRAY);

        array.recycle();
    


我自定义view时实现了三个构造方法,使用的是as中的快捷键创建的,以至于第三个构造方法根本就没调用,所以做什么都是错的,解决方案很简单,那就是把第二个构造方法的方法体改一下即可


 public CustomView(Context context, AttributeSet attrs) 
        this(context, attrs,0);
    

利用this自己手动调用一下第三个构造方法即可。问题解决


问题2,绘制上发现所自定义的 view进行了全屏显示,打开手机上显示布局边界的功能之后可以发现我所自定义的view进行了全屏显示,占据了一个界面上父view所剩余的所有空间。





通过前几篇对于view的分析可以得出,这个draw绘制出来的大小跟onMeasure方法是分不开的,所以呢,重点是对所测量的宽和高进行重新计算

利用paint画笔对象可以直接对文本的宽高进行计算:

private Rect mBounds;

 mBounds = new Rect();


 @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) 
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        int measuredWidth;
        int measuredHeight;
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);//获取测量规范,对于这些不懂得可参考博文
        int width = MeasureSpec.getSize(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int height = MeasureSpec.getSize(heightMeasureSpec);

        mPaint.getTextBounds(mCustomTitle,0,mCustomTitle.length(),mBounds);


        if (widthMode != MeasureSpec.EXACTLY)
            measuredWidth = mBounds.width();

        else//在xml文件中规定了准确的值
            measuredWidth = width;
        
        if (heightMode != MeasureSpec.EXACTLY)
            Log.i("fang","---");


            measuredHeight = mBounds.height();
        else
            measuredHeight = height;
        

        setMeasuredDimension(measuredWidth + getPaddingLeft() + getPaddingRight(),
                measuredHeight + getPaddingTop() + getPaddingBottom());
    


然后进行绘制:


@Override
    protected void onDraw(Canvas canvas) 
        super.onDraw(canvas);


        mPaint.setTextSize(mTitleTxtSize);
        mPaint.setColor(mTitleTxtColor);
        canvas.drawText(mCustomTitle,getPaddingLeft(),
                getHeight()/2 + mBounds.height()/2,mPaint);


    


绘制时第二个和第三个参数决定了内容的x和y的坐标

绘制后结果如下图



          


第一张图是每次重新oncreate界面时的效果,(在此声明一下设置的view是paddingleft为50dp)

第二张图是每次onPause然后onResume之后的效果。

由以上这两张图可以发现两个问题

第一,每次oncreate时view所绘制出的大小并不正确

第二,view没有自动换行,view的绘制已经超出了父view的边界

是不是发现问题层出不穷??慢慢来吧


问题3,在oncreate时view所绘制的大小不正确

问题4,view需要换行


问题3和问题4待解决中,估计需要点儿时间,有解决方案的请留言,谢谢,也欢迎各位分享你自定义view时所遇到的问题



android开发自定义view(代码片段)

  Android中View组件的作用类似于Swing变成中的JPanel,它只是一个空白的矩形区域,View组件中没有任何内容。对于Android应用的其他UI组件来说,它们都继承了View组件,然后在View组件提供的空白区域绘制外观。  当Android系统提... 查看详情

android自定义view学习二---流程(代码片段)

Android自定义View学习二参考:安卓自定义View进阶-分类与流程自定义View绘制流程函数调用链(简化版)构造函数构造函数有四种重载://一般在直接New一个View的时候调用publicvoidSloopView(Contextcontext)//一般在layout文件中使用的时... 查看详情

android——自定义view(代码片段)

自定义View画一个实心圆效果图创建attrs.xml文件初始化样式属性支持Padding属性支持wrap_content属性布局文件中的应用画一个带外圆环的圆效果图创建attrs.xml文件初始化样式属性内圆与外圆环的绘制布局文件中的应用画一个外圆环可... 查看详情

android自定义view中使用spannable(代码片段)

我们都知道Android中使用Spannable可以实现TextView富文本的显示,但是在自定义控件中如何使用Spannable绘制不同样式的文字呢?例如这种效果,标题中的分数字61是粗体,分是常规字体,并且相对于61更小些。第一... 查看详情

android自定义view系列-measurespec(代码片段)

在【Android】自定义View系列-绘制流程一文中,在测量过程中,是通过一定的规则得出最后我们测量的宽高,然后通过setMeasuredDimension()保存结果。那么这里说到的规则主要就是---MeasureSpec和LayoutParamsMeasureSpec是?ÿ... 查看详情

android自定义view视差动画(代码片段)

本系列自定义View全部采用kt**系统:**macandroidstudio:4.1.3**kotlinversion:**1.5.0gradle:gradle-6.5-bin.zip废话不多说,先来看今天要完成的效果:在上一篇:androidsetContentView()解析中我们介绍了,如何通过Factory2来自己解析View,那么我们就通过这个机... 查看详情

自定义view水印布局watermark前景色(代码片段)

...样式:背景色样式:布局:<com.bqt.lock.MarkFrameLayoutxmlns:android="http://schemas.android.c 查看详情

一篇文章带你走近android自定义view(代码片段)

系列文章目录一篇文章带你走近Android自定义view文章目录系列文章目录前言一、为什么要自定义view二、先看看一个超级简单的自定义view(三个构造函数)三、了解手机的坐标系四、使用Canvas画一个折线图(重写onDraw&#... 查看详情

android--每日一问:如何实现自定义view?(代码片段)

...,所以有些人没有做过自定义View并不能否决这个人在Android开 查看详情

android自定义view进阶-xfermode(代码片段)

在Android自定义控件中,Xfermode知识点占有很重要的地位,它能帮助我们实现很多炫酷的效果。例如,实现各种形状的图片控件;结合属性动画实现渐变效果。Xfermode介绍Xfermode主要是通过paint.setXfermode(Xfermodexfermode)... 查看详情

android自定义view(代码片段)

...:http://blog.csdn.net/lmj623565791/article/details/24252901很多的Android入门程序猿来说对于Android自定义View,可能都是比较恐惧的,但是这又是高手进阶的必经之路,所有准备在自定义View上面花一些功夫,多写一些文章。... 查看详情

android自定义view学习一---基础(代码片段)

Android自定义View学习一自定义view三点:布局绘制触摸反馈基础坐标系参考:安卓自定义View基础-坐标系View的坐标系注意:View的坐标系统是相对于父控件而言的.getTop();//获取子View左上角距父View顶部的距离getLeft();//获取... 查看详情

android自定义view之实现流式布局(代码片段)

Android自定义View之实现流式布局运行效果流式布局把子控件从左到右摆放,如果一行放不下,自动放到下一行自定义布局流程1.自定义属性:声明,设置,解析获取自定义值在attr.xml中声明2.测量:在onMeasure... 查看详情

android使用kotlin来实现自定义view之雷达图(代码片段)

本篇文章讲的是Kotlin自定义view之实现雷达图。按照惯例,我们先来看看效果图一、先总结下自定义View的步骤:1、自定义View的属性2、在View的构造方法中获得我们自定义的属性3、重写onMesure4、重写onDraw其中onMesure方法不... 查看详情

android自定义view系列-measurespec(代码片段)

在【Android】自定义View系列-绘制流程一文中,在测量过程中,是通过一定的规则得出最后我们测量的宽高,然后通过setMeasuredDimension()保存结果。那么这里说到的规则主要就是---MeasureSpec和LayoutParamsMeasureSpec是?ÿ... 查看详情

android自定义view系列-measurespec(代码片段)

在【Android】自定义View系列-绘制流程一文中,在测量过程中,是通过一定的规则得出最后我们测量的宽高,然后通过setMeasuredDimension()保存结果。那么这里说到的规则主要就是---MeasureSpec和LayoutParamsMeasureSpec是?ÿ... 查看详情

android自定义view-四个构造函数(代码片段)

View的构造函数有四个:publicView(Contextcontext)publicView(Contextcontext,@NullableAttributeSetattrs)publicView(Contextcontext,@NullableAttributeSetattrs,intdefStyleAttr)publicView(Contextcontext,@NullableAttributeSetattrs,intdefStyleAttr,intdefStyleRes)我们在自定义Vi... 查看详情

android自定义view-四个构造函数(代码片段)

View的构造函数有四个:publicView(Contextcontext)publicView(Contextcontext,@NullableAttributeSetattrs)publicView(Contextcontext,@NullableAttributeSetattrs,intdefStyleAttr)publicView(Contextcontext,@NullableAttributeSetattrs,intdefStyleAttr,intdefStyleRes)我们在自定义Vi... 查看详情