文章来自:Android程序员日记
作者:贤榆的鱼
参考阅读时间:5 min 15s
导读语:自定义控件只看这一篇,是不够的!
前言
在之前我先后写了"自定义View之扩展式"、"自定义View之复合式"、"自定义View之完全自定义",在这三篇文章中我都分别给出了一个例子。当然了,例子其实是相对比较简单的,主要原因可能是个人水平有限吧!但尽管如此,我也尽可能把我要讲的内容表述的更清晰一些!那么为了更好的在我们的脑海里构建这个知识框架(这句话太专业,让我写起来都感到了满满的压力!其实就是让某些重要的或是会用到的内容,能够在我们大脑里留下个更深刻的印象,并让他们之间有更多的联系。)以便于在我们需要的时候,更容易提取出来用!为此,写个总结也是一个不错的方法!这个观点是来自《暗时间》!
正文
我们暂且将自定义View分为了扩展式、复合式和完全自定义三种类型(当然我们也可以按别的分)!他们看起来确实有一定的从易到难的梯度关系!不管怎样,我们将这前面三遍中的知识点和自定义View的一些知识点做一个总结!好处就是——可能会有跟多人关注我的“Android程序员日记”的公众号吧!
都说程序员都是从零开始计数的,那我们开始吧!
[ 0 ]关于自定义View的三个构造方法
- 一个参数的构造方法:在用代码动态的添加我们的自定义view时调用。
- 两个参数的构造方法:在使用xml +inflate的方法添加控件时会调用,里面多了一个AttributeSet类型的值
- 三个参数的构造方法:这个构造方法系统是不调用的,需要我们显示调用并给defStyleAttr传值,多了一个defStyleAttr参数,这是这个view引用style资源的属性参数,也就是我们可以在style中为自定义View定义一个默认的属性样式然后添加进来!
[ 1 ]关于三种自定义View
-
扩展式:
扩展式自定义View继承自Android原生特定的View如:TextView,ImageView等等。我们通过重写onDrow()等回调方法对其进行扩展!使其实现我们想要的更能或样式!
注:该方法实现的自定义View控件不需要自己支持wrap_content和padding。
-
组合式:
组合式自定义View继承自ViewGrop的子View如:LinearLayout、RelativieLayout等。当某种效果看起来像几种View组合在一起的时候,都可以使用这种方式实现。
注:该方式实现自定义View不需要自己处理ViewGroup的测量和布局这两个过程。
-
完全自定义:
完全自定义View继承自View(android中所有控件的基类),通常实现一些不方便布局的组合方式来达到的,需要静态或动态地显示一些不规则的控件或图形!注:该方法实现的自定义View控件需要自己支持wrap_content和padding。
[ 2 ]常用的回调方法
- onFinishInflate():加载完XML组件后回调
- onSizeChanged():组件大小改变时回调
- onMeasure():回调该方法来进行测量(在该方法中实现对wrap_content支持的代码)
- onLayout():回调该方法来显示位置
- onTouchEvent():监听到触摸事件回调,也是实现交互非常重要的回调方法
- onDraw():回调该方法对我们的控件进行绘制
[ 3 ]为自定义View添加并使用自定义属性的过程
Step 1 : 在Values下创建attrs.xml(当然也可以以别的名字命名,无限制),然后在该文件中添加自定义View的自定义属性!
Step 2 : 在自定义View的构造方法中获取自定义属性值,并将值配予相应的位置!
Step 3 :在xml中使用自定义控件及配置其自定义属性
注:在xml使用自定义控件时一定要加 :
xmlns:custom="http://schemas.android.com/apk/res-auto"
当然你也可以写成
xmlns:custom="http://schemas.android.com/apk/res/com.timen4.t3"(com.timen4.t3是应用的报名)
这两种方式没有本质上的区别。至于custom随便你喜欢命名什么都可以。
[ 4 ]完全自定义控件中我们自己支持wrap_content和padding的代码
-
MeasureSpec
-
简介:MeasureSpec代表一个32位int值,高2位代表SpecMode(测量模式),低30位代表SpecSize(指定模式下的规格大小)。
-
三种SpecMode与SpecSize
1.UNSPECIFIED:父容器自容器无限制,要多大给多大,这种情况一般用于系统内部。一般我们不关注盖模式。
2.EXACTLY:父容器已检测出了View所需要的精确大小,这时V接我的最终大小就是SpecSize所给定的值。它对应于LayoutParams中的match_parent和具体的数值两种模式
3.AT_MOST:父容器制定了一个可用大小即SpecSize,View的大小不能大于这个值,具体值要看不同View的具体实现。它对应于LayoutParams中的wrap_content。
-
MeasureSpec和LayoutParams的对应关系
普通的View(即非顶层View)的MeasureSpec由父容器的MeasureSpec和自身的LyoutParams来共同决定的,MeasureSpec一旦确定后,onMeasure中就可以确定View的测量宽高。但对于顶层View(即)
-
-
对wrap_content的支持
在onMeasure()方法中实现对wrap_content的支持
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec); int widthSpecSize= MeasureSpec.getSize(widthMeasureSpec); int heightSpectMode = MeasureSpec.getMode(heightMeasureSpec); int heightSpecSize=MeasureSpec.getSize(heightMeasureSpec); //这里就是对wrap_content的支持 if(widthSpecMode==MeasureSpec.AT_MOST&&heightSpectMode==MeasureSpec.AT_MOST){ //这里设定的根据你自己自定义View的情况而定 setMeasuredDimension(200,200); }else if(widthSpecMode==MeasureSpec.AT_MOST){ setMeasuredDimension(200,heightSpecSize); }else if (heightSpectMode==MeasureSpec.AT_MOST){ setMeasuredDimension(widthSpecSize,200); } }
-
对padding的支持
在onDraw()方法中实现对padding的支持,其实就是在或控件时考虑到就padding就好了。如果不自己实现那么你对该自定义View设置padding将是无效的! @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); //这里是对画一个圆形的View的padding支持 final int paddingLeft = getPaddingLeft(); final int paddingRight = getPaddingRight(); final int paddingTop = getPaddingTop(); final int paddingBottom=getPaddingBottom(); int width = getWidth()-paddingLeft-paddingRight; int height = getHeight()-paddingBottom-paddingTop; int radius = Math.min(width,height)/2; canvas.drawCircle(paddingLeft+width/2,paddingTop+height/2,radius,mPaint_while); }
[ 5 ]为自定义View定义并实现接口回调的过程
本段示例代码来自"自定义View之复合式",为了总结的需要,做了一些注释上的修改,并简化为实现一个回调方法!
Step 1: 定义回调接口(自定义View类中操作)
public interface TopBarClickListener {
void leftClick();
}
Step 2: 暴露回调接口(自定义View类中操作)
public void setOnTopbarClickListener(TopBarClickListener mListener){
this.mListener=mListener;
}
Step 3: 调用回调接口(自定义View类中操作)
private void bindEvents() {
mLeftButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if(mListener!=null){
mListener.leftClick();
}
}
});
}
Step 4: 实现回调回调接口及方法(在实例化自定义View对象的类中)
mtopBar.setOnTopbarClickListener(new TopBar.TopBarClickListener() {
@Override
public void leftClick() {
Toast.makeText(MainActivity.this,"上一张",Toast.LENGTH_SHORT).show();
if (index<=0){
index=3;
}else{
index--;
}
iv_image.setImageResource(images.get(index));
}
后记
好了,这篇总结就这么多了!我必须再次强调一次,本文最最重要的一句话,就是放在最前面的导读语——自定义控件只看这一篇,是不够的!这句话我没开玩笑,你不信也没关系,反正我是不会在哪我的美貌做赌注了!
其实,不只是我对自己写的东西不自信,而是无论是我这一篇,还是某个大牛写的某一篇。我都觉得自定义View看某一篇是不够的!对于初学者或新手而言更是如此。因为正如我在我在前言中写道的一样,我们都在构造自己的知识架构。我们需要从更多的角度,去理解我们学到的或是要学习的内容!这样才更有助于我们去理解和记忆!也才更有助于我们去实际应用!
最后总结一句话吧——“我们学习的任何事物”看“某一篇”都是不够的!
喜欢可以关注我微信公众号
分享绝不止于Android!
喜欢请关注公众Android程序员日记
网友评论