Android自定义view学习笔记02

来源:互联网 发布:ios11降级数据丢失 编辑:程序博客网 时间:2024/05/29 17:05

Android自定义view学习笔记02

本文代码来自于张鸿洋老师的博客之Android 自定义View (二) 进阶 学习笔记,对代码进行些许修改,并补充一些在coding过程中遇到的问题、学习的新东西。

相关代码

//CustomImageView.javapackage mmrx.com.myuserdefinedview.textview;import android.content.Context;import android.content.res.TypedArray;import android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.graphics.Rect;import android.text.TextPaint;import android.text.TextUtils;import android.util.AttributeSet;import android.util.Log;import android.util.TypedValue;import android.view.View;import mmrx.com.myuserdefinedview.R;/** * Created by mmrx on 2015/4/6. */public class CustomImageView extends View {    //文本内容    private String mTitleText;    //文本的颜色    private int mTitleColor;    //文本大小    private int mTitleTextSize;    //图片资源    private Bitmap mImage;    //图片缩放    private int mImageScale;    //文本时候的文字显示范围    private Rect mBound;    private Paint mPaint;    //图片和字外面的矩形    private Rect rect;    //高度和宽度    int mWidth,mHeight;    //在代码中调用    public CustomImageView(Context context){        this(context,null);    }    //在布局文件中调用    public CustomImageView(Context context,AttributeSet set){        this(context,set,R.attr.CustomImageView02Style);    }    //显示被调用    public CustomImageView(Context context,AttributeSet set,int defStyle){        super(context,set,defStyle);        TypedArray ta = context.obtainStyledAttributes(set, R.styleable.CustomImageView,                defStyle,R.style.CustomizeStyle02);        int count = ta.getIndexCount();        for(int i=0;i<count;i++){            int index = ta.getIndex(i);            switch (index){                case R.styleable.CustomImageView_image:                    /*                    * decodeResource(Resources res, int id)                    * res 是一个Resources类对象,用来读取res文件夹下的资源                    * 这个res是的来源是该view创建时传入的Context对象, context.getResources()                    * */                    mImage = BitmapFactory.decodeResource(getResources(),ta.getResourceId(index,0));//                    assert mImage!=null;                    break;                case R.styleable.CustomImageView_imageScaleType:                    mImageScale = ta.getInt(index,0);                    break;                case R.styleable.CustomImageView_titleColor:                    mTitleColor = ta.getColor(index, Color.BLACK);                    break;                case R.styleable.CustomImageView_titleSize:                    /*                    * TypedValue.applyDimension是转变为标准尺寸的函数                    * 第一个参数是 单位,第二个参数是要数值,第三个是DisplayMetircs 对象,用于获取屏幕分辨率                    * */                    mTitleTextSize = ta.getDimensionPixelSize(index,                            (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,                                    16,getResources().getDisplayMetrics()));                    break;                case R.styleable.CustomImageView_titleText:                    mTitleText = ta.getString(index);                    break;                default:                    break;            }//end switch        }//end for        ta.recycle();        rect = new Rect();        mPaint = new Paint();        mBound = new Rect();        mPaint.setTextSize(mTitleTextSize);        //计算字体所需范围        mPaint.getTextBounds(mTitleText,0,mTitleText.length(),mBound);    }//end method    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {//        super.onMeasure(widthMeasureSpec, heightMeasureSpec);        int hightMod = MeasureSpec.getMode(heightMeasureSpec);        int hightSize = MeasureSpec.getSize(heightMeasureSpec);        int widthMod = MeasureSpec.getMode(widthMeasureSpec);        int widthSize = MeasureSpec.getSize(widthMeasureSpec);        //高度有具体数值        if(hightMod == MeasureSpec.EXACTLY){            mHeight = hightSize;        }        //wrap_content & others        else{            //获得图片的高度            int imageHeight = mImage.getHeight();            //获得文字高度            int textHeight = mBound.height();            //总体高度            int totalHeight = imageHeight + textHeight + getPaddingTop() + getPaddingBottom();            if(hightMod == MeasureSpec.AT_MOST){                //warp_content                //获得相对较小的高度                mHeight = Math.min(hightSize,totalHeight);            }            else{                //多个自定义CustomImageView放入ScrollView中,只会显示出最后一个处理办法                mHeight = hightSize;            }        }        //宽度有具体数值        if(widthMod == MeasureSpec.EXACTLY){            mWidth = widthSize;        }        //wrap_content & others        else{            // 由图片决定的宽            int desireByImg = getPaddingLeft() + getPaddingRight() + mImage.getWidth();            // 由字体决定的宽            int desireByTitle = getPaddingLeft() + getPaddingRight() + mBound.width();            Log.w("CustomImageView","desireByTitle:"+desireByTitle+" desireByImg:"+desireByImg+" widthSize: "+widthSize);            if(widthMod == MeasureSpec.AT_MOST){                //warp_content                Log.w("CustomImageView","MeasureSpec.AT_MOST");                //获得相对较大的宽度                int desire = Math.max(desireByImg,desireByTitle);                mWidth = Math.min(widthSize,desire);            }            else{                Log.w("CustomImageView","NOT MeasureSpec.AT_MOST");                //多个自定义CustomImageView放入ScrollView中,只会显示出最后一个处理办法                mWidth = widthSize;            }        }        //将计算好的高度宽度放入,一定要记得这句话不要丢了...        setMeasuredDimension(mWidth, mHeight);    }    @Override    protected void onDraw(Canvas canvas) {//        super.onDraw(canvas);        //边框        //设置线宽        mPaint.setStrokeWidth(4);        //设置样式为空心        mPaint.setStyle(Paint.Style.STROKE);        mPaint.setColor(Color.RED);        /*开画,前四个参数是 左 上 右 下 边的位置(注意是边,不是点) 画笔         * left 矩形左边的x坐标 right 矩形右边的x坐标         * top 矩形上边的y坐标 bottom 矩形下边的y坐标         * */        canvas.drawRect(0,0,getMeasuredWidth(),getMeasuredHeight(),mPaint);        //获得绘制图片的矩形框        rect.left = getPaddingLeft();        rect.right = mWidth - getPaddingRight();        rect.top = getPaddingTop();        rect.bottom = mHeight - getPaddingBottom();        //设置画笔风格为 实心        mPaint.setStyle(Paint.Style.FILL);        mPaint.setColor(mTitleColor);        //当当前字体的宽度大于边框的宽度,截取字符串的,改为xxxx...        if(mBound.width()>mWidth){            //TextPaint 是 Paint 的一个子类            TextPaint paint = new TextPaint(mPaint);            /*TextUtils是处理字符串的工具类,ellipsize是用来截断给定长度的字符串的方法            * 参数 需要截断的字符串 显示的字符串的宽度 省略号的位置            * */            String msg = TextUtils.ellipsize(mTitleText,paint,                    (float)mWidth-getPaddingLeft()-getPaddingRight(),TextUtils.TruncateAt.END).toString();            /*            * 坐标参数 x,y x默认是字符串左边在屏幕的位置,如果设置了paint.setTextAlign(Paint.Align.CENTER);            * 那就是字符的中心,y是指定这个字符baseline在屏幕上的位置            * */            canvas.drawText(msg, getPaddingLeft(), mHeight - getPaddingBottom(), mPaint);        }        //正常情况,字体居中        else{            canvas.drawText(mTitleText,mWidth/2-mBound.width()*1.0f/2,                    mHeight-getPaddingBottom(),mPaint);        }        //这句话是什么意思?如果图片缩放选择的是FITXY,则需要将rect的bottom边向上提一下,防止把        //字覆盖掉,如果选择的是CENTER,rect的位置会重新定义就不用这句话了。我感觉这句话放到        //if(mImageScale == 0)语句块里比较好懂一些。        rect.bottom -= mBound.height();        //如果缩放为 FITXY        if(mImageScale == 0){            canvas.drawBitmap(mImage,null,rect,mPaint);        }        //如果为居中 CENTER        else{            //计算居中的矩形范围            rect.left = mWidth/2 - mImage.getWidth()/2;            rect.right = mWidth/2 + mImage.getWidth()/2;            rect.bottom = (mHeight-mBound.height())/2 + mImage.getHeight()/2;            rect.top = (mHeight-mBound.height())/2 - mImage.getHeight()/2;            canvas.drawBitmap(mImage,null,rect,mPaint);        }    }}

xml文件的相关内容

<!--attrs.xml-->    <attr name="titleText" format="string"/>    <attr name="titleColor" format="color"/>    <attr name="titleSize" format="dimension"/>    <attr name="CustomImageView02Style" format="reference"/>    <attr name="image" format="reference"/>    <attr name="imageScaleType">        <enum name="fillXY" value="0"/>        <enum name="center" value="1"/>    </attr>        <declare-styleable name="CustomImageView">        <attr name="titleText" />        <attr name="titleColor" />        <attr name="titleSize" />        <attr name="imageScaleType"/>        <attr name="image"/>    </declare-styleable>
<!-- style.xml--><resources>    <!-- Base application theme. -->    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">        <!-- Customize your theme here. -->        <item name="CustomView01Style">@style/CustomizeStyle01</item>        <item name="CustomImageView02Style">@style/CustomizeStyle02</item>    </style>    <style name="CustomizeStyle01">        <item name="titleText">@string/hello_world</item>        <item name="titleColor">#ff0000</item>        <item name="titleSize">14dp</item>    </style>    <style name="CustomizeStyle02">        <item name="titleText">@string/hello_world</item>        <item name="titleColor">#ff0000</item>        <item name="titleSize">14dp</item>        <item name="imageScaleType">center</item>        <item name="image">@drawable/ic_launcher</item>    </style></resources>
<!--activity_main.xml-->    <mmrx.com.myuserdefinedview.textview.CustomImageView        android:layout_marginTop="20dp"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        customview:imageScaleType="center"        customview:titleText="androidandroidandroidandroid"        customview:image="@drawable/ic_launcher"        android:padding="20dp"/>    <mmrx.com.myuserdefinedview.textview.CustomImageView        android:layout_marginTop="20dp"        android:layout_width="150dp"        android:layout_height="200dp"        customview:imageScaleType="fillXY"        customview:titleText="板娘"        customview:image="@drawable/hello"        android:padding="5dp"/>

遇到的问题

  • 启动过程中logcat打印以下错误后程序崩溃
02-06 14:25:08.383  30804-30804/mmrx.com.myuserdefinedview E/AndroidRuntime﹕ FATAL EXCEPTION: main    java.lang.IllegalStateException: onMeasure() did not set the measured dimension by calling setMeasuredDimension()            at android.view.View.measure(View.java:15561)            at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5109)            at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1396)            at android.widget.LinearLayout.measureVertical(LinearLayout.java:681)            at android.widget.LinearLayout.onMeasure(LinearLayout.java:574)            at android.view.View.measure(View.java:15556)            at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5109)            at android.widget.FrameLayout.onMeasure(FrameLayout.java:310)            at android.view.View.measure(View.java:15556)            at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5109)            at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1396)            at android.widget.LinearLayout.measureVertical(LinearLayout.java:681)            at android.widget.LinearLayout.onMeasure(LinearLayout.java:574)            at android.view.View.measure(View.java:15556)            at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5109)            at android.widget.FrameLayout.onMeasure(FrameLayout.java:310)            at com.android.internal.policy.impl.PhoneWindow$DecorView.onMeasure(PhoneWindow.java:2397)            at android.view.View.measure(View.java:15556)            at android.view.ViewRootImpl.performMeasure(ViewRootImpl.java:1987)            at android.view.ViewRootImpl.measureHierarchy(ViewRootImpl.java:1228)            at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1401)            at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1121)            at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:4598)            at android.view.Choreographer$CallbackRecord.run(Choreographer.java:725)            at android.view.Choreographer.doCallbacks(Choreographer.java:555)            at android.view.Choreographer.doFrame(Choreographer.java:525)            at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:711)            at android.os.Handler.handleCallback(Handler.java:615)            at android.os.Handler.dispatchMessage(Handler.java:92)            at android.os.Looper.loop(Looper.java:137)            at android.app.ActivityThread.main(ActivityThread.java:4921)            at java.lang.reflect.Method.invokeNative(Native Method)            at java.lang.reflect.Method.invoke(Method.java:511)            at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1038)            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:805)            at dalvik.system.NativeStart.main(Native Method)

一长串的错误信息。。。看了下代码,是onMeasure方法中没有调用setMeasuredDimension(int, int)方法。

  • 类似的还有一些比如说变量名用错…case后的标签用错…导致上次coding的时候心烦意乱的,事隔半周后重新拿起来再看,错误都是一些及其低级的错。

补充的知识点

  • Bitmap decodeResource(Resources res, int id)这个方法属于类BitmapFactory。在示例代码中的调用语句为mImage = BitmapFactory.decodeResource(getResources(),ta.getResourceId(index,0));这第一个参数,当时有点不理解…查询到的资料和通过看源码的收获如下:

    res 是一个Resources类对象,用来读取res文件夹下的资源。
    每一个View类型的对象都拥有一个Resources实例mResources,而这个实例又是通过实例化View类型对象时传入的Context对象获取的。
    说明一个Activity的View共有一个Resources对象的引用。

  • TypedValue.applyDimension是转变为标准尺寸的函数,在示例代码中有相关的注释。

  • 关于canves.drawRect()方法的坐标参数问题,我找到了一篇写的很不错的博客android Draw Rect 坐标图示,里面有一张很生动的图来表示坐标的位置,清晰易懂。需要注意的是坐标原点在左上方,x轴(横轴)向右递增,y轴(纵轴)向下递增。于是也解决了代码中这些运算的具体含义了。

rect.left = mWidth/2 - mImage.getWidth()/2;rect.right = mWidth/2 + mImage.getWidth()/2;rect.bottom = (mHeight-mBound.height())/2 +      mImage.getHeight()/2;rect.top = (mHeight-mBound.height())/2 - mImage.getHeight()/2;
  • canvas.drawText(@NonNull String text, float x, float y, @NonNull Paint paint)这个方法里面的坐标参数也是很奇怪…坐标参数 x,y x默认是字符串左边在屏幕的位置,如果设置了paint.setTextAlign(Paint.Align.CENTER);那就是字符的中心,y是指定这个字符baseline在屏幕上的位置。这里有一篇博文讲的很不错android中canvas.drawText参数的介绍以及绘制一个文本居中的案例

  • 偶然找到几篇很棒的介绍canves绘图的博文,这是第四篇android Graphics(四):canvas变换与操作,有空得好好学习一下。

Android自定义view学习笔记01
Android自定义view学习笔记03
Android自定义view学习笔记04
源码同步到gitHub

0 0
原创粉丝点击