Creating Custom Views--自定义View

虽然Android为我们提供了很多的内置控件,但是有时候我们的独特的业务需求可能这些内置控件仍然无法满足,那么此时,就需要我们自定义控件。这系列文章将向你展示如何创建一个功能强大且可重复的View控件。

目录:

自定义View

一个良好的自定义View应该能和Android内置的View控件一样,具有良好的表现:具有自己的属性能与用户进行交互占据有效的内存空间。因此一个自定义的View的Class应该具备以下几个特征:

  1. 继承自View或者是View的子类,在Android中所有的View控件比如:Button,TextView都是View的子类。
  2. 符合Android的标准
  3. 通过xml文件提供自定义的属性
  4. 能与用户交互,能够响应事件,传递事件
  5. 兼容Android多个平台和版本

有了以上的标准,那么接下来就学习如何构造一个功能完整的自定义View吧:
大体上分为以下若干步骤:

  • 继承一个View或者View子类
  • 在res/values/attrs.xml文件中自定义属性
  • 应用我们的自定义属性
  • 添加属性和事件
  • 供外界调用
  1. 在Android的所有View体系中,所有的控件都是继承自View的,所以我们自定义控件的时候也应该是继承View或者是View的子类,比如Button,TextView。需要注意的是:大部分时候,我们自定义View是为了想在布局文件中xml调用,此时,我们的自定义View需要覆盖如下的构造方法来能在xml中被实例化,否则会报错。
    <pre>
    class PieChart extends View {
    public PieChart(Context context, AttributeSet attrs) {//必须有此构造函数才能在xml文件中被实例化
    super(context, attrs); //调用父类的构造函数
    }
    }
    </pre>
    另外,我们之所以自定义View大多数是因为想实现业务需求的View,此时基本是需要在重写onDraw()方法来绘制我们的View。
    <pre>
    @Override
    protected void onDraw(Canvas canvas) { //自定义View中都会重写此onDraw()方法
    super.onDraw(canvas); //调用父类的onDraw();
    // Draw the shadow
    canvas.drawOval(mShadowBounds, mShadowPaint);

// Draw the label text
if (getShowText()) {
canvas.drawText(mData.get(mCurrentItem).mLabel, mTextX, mTextY, mTextPaint);
}
// If the API level is less than 11, we can’t rely on the view animation system to
// do the scrolling animation. Need to tick it here and call postInvalidate() until the scrolling is done.
if (Build.VERSION.SDK_INT < 11) {
tickScrollAnimation();
if (!mScroller.isFinished()) {
postInvalidate();
}
}
}
</pre>

  1. 自定义属性
    自定义属性后就能像Android内置的View控件一样通过属性标签来控制,我们自定义属性的时候需要在res/values/attrs.xml文件下以<declare-styleable>为根标签属性名和类型以键值对的方式存储。
    <pre>
    <resources>
    <declare-styleable name=”PieChart”>
    <attr name=”showText” format=”boolean” />
    <attr name=”labelPosition” format=”enum”>
        <enum name=”left” value=”0″/>
       <enum name=”right” value=”1″/>
    </attr>
    </declare-styleable>
    </resources>
    </pre>
    需要注意的地方是:我们在布局文件中使用自定义属性的时候需要加上命名空间,命名空间可以加在根标签也可以加在自定义View标签内,一般直接加在根标签里。命名空间的格式如下:
    http://schemas.android.com/apk/res/[yourpackagename],比如:
    xmlns:custom=”http://schemas.android.com/apk/res/com.example.android.customviews
    还有特殊的情况:在你自定义的View中有一个内部的自定义innerView,如果希望在xml文件中使用innerView的属性,比如PieChart内有个PieView的内部类,那么应该这样:
    com.example.customviews.charting.PieChart$PieView。在xml引用自定义View布局需要使用全名,代码如下:
    <pre>
    <?xml version=”1.0″ encoding=”utf-8″?>
    <LinearLayout
    xmlns:android=”http://schemas.android.com/apk/res/android
    xmlns:custom=”http://schemas.android.com/apk/res/com.example.customviews“> //自定义属性命名空间
    <com.example.customviews.charting.PieChart
    custom:showText=”true”
    custom:labelPosition=”left”
    />
    </LinearLayout>
    </pre>

  2. 在代码中应用我们自定义的属性,使xml中的属性能够产生效果
    当一个view从xml布局中创建后,其使用的所有属性都会读取并传递到view 构造器函数的AttributeSet中。但是倘若直接使用AttributeSet会有一些缺点比如:styles不能生效,引用的资源不能解析。这时,我们使用另外一种方法会好很多,使用[obtainStyledAttributes()](http://developer.android.com/reference/android/content/res/Resources.Theme.html#obtainStyledAttributes(android.util.AttributeSet, int[], int, int))将在xml定义的属性封装到 TypedArray中去。代码如下:
    <pre>
    public PieChart(Context context, AttributeSet attrs) {
    super(context, attrs);
    TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.PieChart, 0, 0);
    try {
    mShowText = a.getBoolean(R.styleable.PieChart_showText, false);
    mTextPos = a.getInteger(R.styleable.PieChart_labelPosition, 0);
    } finally {
    a.recycle(); //释放资源
    }
    }
    </pre>

  3. 添加属性事件
    添加属性事件暴露方法,使view可以动态改变
    <pre>
    public boolean isShowText() {
    return mShowText;
    }
    public void setShowText(boolean showText) {
    mShowText = showText;
    invalidate();//重绘
    requestLayout();
    }
    </pre>
    注意:invalidate()和requestLayout
    requestLayout:当view确定自身已经不再适合现有的区域时,该view本身调用这个方法要求parent view重新调用他的onMeasure onLayout来对重新设置自己位置。
    特别的当view的layoutparameter发生改变,并且它的值还没能应用到view上,这时候适合调用这个方法。invalidate:View本身调用迫使view重画。
    另外:一个好的自定义View应该暴露接口,提供对业务有用的部分监听机制供自己调用。比如:OnCurrentItemChanged(),onColorChanged(),onSizeChanged().

小结:

  • 对于自定义View需要注意的大概就是这些,但是在实际自己写的时候仍然会有很多的问题,需要多实践,多学习别人的代码,再结合自己的业务区自定义。当然如果有第三方的控件可以满足我们的业务需求,我们亦可以引用。

Google给出的一个完整的自定义view的例子:点击下载Customview.zip

    原文作者:jackLee
    原文地址: https://www.jianshu.com/p/c3c92957dac6
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞