一个规范的自定义View——Android开发艺术探索笔记

欢迎转载,转载请注明原文链接http://blog.csdn.net/l664675249/article/details/50787973

一个不规范的自定义View

这个自定义的View很简单,就是画一个圆,实现一个圆形效果的自定义View。

先看一个不规范的自定义View是怎么做的

public class CircleView extends View {

    private int mColor = Color.RED;
    private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);

    public CircleView(Context context) {
        super(context);
        init();
    }

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

    public CircleView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {
        mPaint.setColor(mColor);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        int width = getWidth();
        int height = getHeight();
        int radius = Math.min(width, height) / 2;
        canvas.drawCircle(width / 2, height / 2, radius, mPaint);
    }
}

对应的xml

<com.ryg.chapter_4.ui.CircleView
    android:id="@+id/circleView1"
    android:layout_width="match_parent"
    android:layout_height="100dp"
    android:layout_margin="20dp"
    android:background="#000000"
    />

这样虽然也能画出一个圆来,但是这并不是一个规范的自定义View,主要存在以下问题:

  • android:padding属性是不能使用的
  • 使用wrap_content就相当于使用match_partent

一个规范的自定义View

为了解决以上问题需要重写View的onMeasure和onDraw方法。

完整代码如下:

public class CircleView extends View {

    private int mColor = Color.RED;
    private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);

    public CircleView(Context context) {
        super(context);
        init();
    }

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

    public CircleView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CircleView);
        mColor = a.getColor(R.styleable.CircleView_circle_color, Color.RED);
        a.recycle();
        init();
    }

    private void init() {
        mPaint.setColor(mColor);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
        if (widthSpecMode == MeasureSpec.AT_MOST
                && heightSpecMode == MeasureSpec.AT_MOST) {
            setMeasuredDimension(200, 200);
        } else if (widthSpecMode == MeasureSpec.AT_MOST) {
            setMeasuredDimension(200, heightSpecSize);
        } else if (heightSpecMode == MeasureSpec.AT_MOST) {
            setMeasuredDimension(widthSpecSize, 200);
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        final int paddingLeft = getPaddingLeft();
        final int paddingRight = getPaddingRight();
        final int paddingTop = getPaddingTop();
        final int paddingBottom = getPaddingBottom();
        int width = getWidth() - paddingLeft - paddingRight;
        int height = getHeight() - paddingTop - paddingBottom;
        int radius = Math.min(width, height) / 2;
        canvas.drawCircle(paddingLeft + width / 2, paddingTop + height / 2,
                radius, mPaint);
    }
}

添加自定义属性

  1. 在values文件夹下添加attrs.xml

    <?xml version="1.0" encoding="utf-8"?>
    <resources>
        <declare-styleable name="CircleView">
            <attr name="circle_color" format="color" />
        </declare-styleable>
    </resources>

    自定义的属性集合CircleView,在这个属性集合里只定义了一个格式为color的属性circle_color。

  2. 在View的构造函数中解析自定义的属性
     public CircleView(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CircleView);
            mColor = a.getColor(R.styleable.CircleView_circle_color, Color.RED);
            a.recycle();
            init();
        }
  3. 在布局文件中使用自定义属性
       <com.ryg.chapter_4.ui.CircleView
            android:id="@+id/circleView1"
            android:layout_width="wrap_content"
            android:layout_height="100dp"
            android:layout_margin="20dp"
            android:background="#000000"
            android:padding="20dp"
            app:circle_color="@color/light_green" />

    在使用自定义的属性时,要在schemas声明:xmlns:app=”http://schemas.android.com/apk/res-auto”,使用时与普通属性类似,app:circle_color=”@color/light_green” 。

自定义View须知

  • 自定义的View中margin属性可以使用,因为它是由父容器控制的
  • 直接继承View或ViewGroup的需要自己处理wrap_content
  • View要在onDraw方法中要处理padding,而ViewGroup要在onMeasure和onLayout中处理padding和margin
  • View中的post方法可以取代handler
  • 在View的onDetachedFromWindow中停止动画,防止内存泄露
  • 有滑动嵌套情形时,注意滑动冲突处理
  • 关于上面涉及到的一些类和方法的详细解释请参考http://blog.csdn.net/l664675249/article/details/50774617

想要自定义出漂亮的View并不容易,只有多读,多写,多测,才能更好的掌握。自己造一个轮子,然后再对比成熟的轮子去找差距和不足。

欢迎转载,转载请注明原文链接http://blog.csdn.net/l664675249/article/details/50787973

时间: 2024-08-25 00:45:10

一个规范的自定义View——Android开发艺术探索笔记的相关文章

Android中View的事件分发机制——Android开发艺术探索笔记

欢迎转载,转载请注明出处http://blog.csdn.net/l664675249/article/details/50738102 介绍 点击事件的事件分发就是对MotionEvent事件的分发过程,当一个MotionEvent产生了以后,系统需要把这个事件传递给一个具体的View,而这个传递的过程就是分发的过程. 涉及到的三个方法 dispatchTouchEvent:用来进行事件的分发,如果事件能够传递给当前View,那么此方法一定会被调用,返回结果受当前View的onTouchEve

Android中View的弹性滑动——Android开发艺术探索笔记

欢迎转载,转载请注明出处http://blog.csdn.net/l664675249/article/details/50732132 介绍 弹性滑动也就是渐进式滑动,实现弹性滑动的方法有很多,但是他们都有一个共同的思想:将一次大的滑动分成若干次小的滑动并在一段时间内完成.本文主要介绍三种弹性滑动方式,Scroller.动画和Handler. 本文中的"滑动"是指View内容的滑动而非View本身位置的改变. 示例 点击屏幕任意地方,手指与屏幕接触时,触发ACTION_DOWN屏幕中

从源码的角度理解四大组件的工作过程——Android开发艺术探索笔记

原文链接http://sparkyuan.me/2016/03/14/四大组件的工作过程/ 转载注明出处 系统对四大组件的过程进行了很大程度的封装,日常开发中并不需要了解底层的工作原理,那么研究这些原理的意义在哪里呢? 如果你想在技术上更进一步,那么了解一些系统的工作原理是十分必要的,也是开发人员日后成长为高级工程师所必备的技术能力. Android作为一个优秀的基于Linux操作系统,其内部一定有很多值得我们学习的地方,通过对Android操作系统的学习对提高开发人员的内功有很大的好处. 如果

Android中IntentFilter匹配规则详解——Android开发艺术探索笔记

欢迎转载,转载请注明出处http://blog.csdn.net/l664675249/article/details/50640288 启动Activity的方式分为两种,显示和隐式调用.显示调用很简单,直接指明要启动的Activity就可以了,这里主要介绍一下隐式调用.隐式调用需要Intent能够匹配目标组件的IntentFilter中所设置的过滤信息.只有一个Intent同时匹配action,category和data才算匹配成功. 示例 <intent-filter> <acti

Activity在异常情况下的生命周期——Android开发艺术探索笔记

欢迎转载,转载请注明出处 http://blog.csdn.net/l664675249/article/details/50638398 Activity在异常情况下的生命周期 关于Activity正常情况下的生命周期请参考这篇文章,本文主要讲解Activity在异常情况下的生命周期. 情况1:资源相关的系统配置发生改变 资源相关的系统配置发生改变,举个栗子.当前Activity处于竖屏状态的时候突然转成横屏,系统配置发生了改变,Activity就会销毁并且重建,其onPause, onSto

【读书笔记】【Android 开发艺术探索】第4章 View 的工作原理

一.基础知识 1.ViewRoot 和 DecorView ViewRoot 对应 ViewRootImpl 类,它是连接 WindowManager 和 DecorView 的纽带,View 的三大流程都是通过 ViewRoot 来完成的.在ActivityThread 中,当 Activity 对象被创建完毕后,会将 DecorView 添加到 Window 中,同时会创建 ViewRoot 对象. DecorView 添加到窗口 Window 的过程. 图片来自https://yq.ali

Android开发艺术探索——第四章View的工作原理

Android开发艺术探索--第四章View的工作原理 4.1 (一)初识ViewToot和DecorView 基本概念 ViewRoot对应于ViewRootImpl类,是连接WindowManager和DecorView的纽带,View的三大流程均是通过ViewRoot来完成的.在ActivityThread中,当Activity对象被创建完成后,会将DecorView添加到View中.同时,会创建ViewRootImpl对象,并将ViewTootImpl对象和DecorView建立关联.

Android开发艺术探索——第七章:Android动画深入分析

Android开发艺术探索--第七章:Android动画深入分析 Android的动画可以分成三种,view动画,帧动画,还有属性动画,其实帧动画也是属于view动画的一种,,只不过他和传统的平移之类的动画不太一样的是表现形式上有点不一样,view动画是通过对场景的不断图像交换而产生的动画效果,而帧动画就是播放一大段图片,很显然,图片多了会OOM,属性动画通过动态的改变对象的属性达到动画效果,也是api11的新特性,在低版本无法使用属性动画,但是我们依旧有一些兼容库,OK,我们还是继续来看下详细

Android开发艺术探索——第一章:Activity的生命周期和启动模式

Android开发艺术探索--第一章:Activity的生命周期和启动模式 怀着无比崇敬的心情翻开了这本书,路漫漫其修远兮,程序人生,为自己加油! 一.序 作为这本书的第一章,主席还是把Activity搬上来了,也确实,和Activity打交道的次数基本上是最多的,而且他的内容和知识点也是很多的,非常值得我们优先把他掌握,Activity中文翻译过来就是"活动"的意思,但是主席觉得这样翻译有些生硬,直接翻译成"界面"可能更好,的确,Activity主要也是用于UI效