一:引言
Android的开发中,在移动设备上展示的所有内容,都是靠一个一个具体的视图控件,按照一定的排列规则展示出来的,这些一个个控件,都是系统提供给我们的。但是我们看到,app商店上有些比较炫酷的页面展示,我们会发现,系统根本没有提供那些控件,那么这是怎么实现的呢?对就是通过我们的自定义控件去完成。那么什么是自定义控件呢,这里我个人理解可以分为三类:
- 自定义试图,——继承 View,然后自绘试图内容
- 自定义组合试图,——继承ViewGroup,然后对子类试图进行重新布局。
-
自定义已有试图,——继承已有的View,比如继承ImageView
自定义控件是android修行道路上的必经之路,也是得道升仙的必备能力。所以我们的跨过去,每天练习一点点,就一点点。
二:自定义View步骤
这里介绍下自定义试图的主要步骤
- 自定义属相
- 继承View重写构造方法
- 获取自定义属性
- 重写测量控件的宽高
- 绘制控件显示
- 提供自定义事件
三:自定义View
-
自定义属性
自定义属性一共有10中定义类型,String,boolean等,具体的类型
和使用对应如下代码
<?xml version="1.0" encoding="utf-8"?> <resources> <attr name="text" format="string"></attr> <!-- 定义:资源ID 使用:@drawable/图片ID --> <attr name="msrc" format="reference"></attr> <!-- 定义: 颜色值 使用: android:mcolor = "#00FF00" --> <attr name="mcolor" format="color"></attr> <!-- 定义:布尔类型 使用:android:misfocus = "true" --> <attr name="misfocus" format="boolean"></attr> <!-- 定义:尺寸 使用: android:msize = "42dip" --> <attr name="msize" format="dimension"></attr> <!-- 定义:浮点值 使用: android:malpha = "0.1" --> <attr name="malpha" format="float"></attr> <!-- 定义:整形 使用:android:mcount = "12" --> <attr name="mcount" format="integer"></attr> <!-- 定义:字符串 使用:android:apiKey = "2223" --> <attr name="apikey" format="string"></attr> <!-- 定义:百分数 使用:100% --> <attr name="mcurrent" format="fraction"></attr> <!-- 定义:枚举 使用:type:1 --> <attr name="type"> <enum name="cycle" value="1"></enum> <enum name="round" value="2"></enum> </attr> <declare-styleable name="customView"> <attr name="text"/> <attr name="mcolor"/> <attr name="msize"/> </declare-styleable> </resources>
- 编写自定义控件,使用自定义属性
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" > <com.zhang.zs.customviewdemo.MyCuntomView android:layout_centerInParent="true" android:layout_width="wrap_content" android:layout_height="wrap_content" android:padding="20dp" app:mcolor="#ff0" app:msize="18sp" app:text="你好" /> </RelativeLayout>
-
创建自定义View继承View(重写构造方法)
在创建View的时候,需要重写构造方法,一般重写前三个构造方法就可以了,但是如果我们的自定控件是通过布局文件的形式加载,则第二个构造必须重写,不然会报错。
public MyCuntomView(Context context) { this(context, null); } public MyCuntomView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public MyCuntomView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); //获取自定义属性 initViewAtrr(context, attrs, defStyleAttr); }
-
获取自定属性的值
在获取自定义属性值的时候,我们通过循环的方式来获取值,这样获取到属性值,就是我们xml文件中使用到的,没有使用到的就获取不到。而并获取我们所有自定义的属性。
private void initViewAtrr(Context context, AttributeSet attrs, int defStyleAttr) { TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.customView, defStyleAttr, 0); //获取有几个自定义属相 final int count = a.getIndexCount(); Log.e("TAG", "=====" + count); for (int i = 0; i < count; i++) { int type = a.getIndex(i); switch (type) { case R.styleable.customView_text: text = a.getString(type); if (TextUtils.isEmpty(text)) { text = "我是文本"; } break; case R.styleable.customView_mcolor: corlor = a.getColor(type, Color.RED); break; case R.styleable.customView_msize: msize = a.getDimensionPixelSize(type, 15); break; } } a.recycle(); paint = new Paint(); //抗锯齿 paint.setAntiAlias(true); }
-
测量控件的大小(重写onMeasure方法)
测量之前先了解MeasureSpec的specMode,mode共有三种情况,取值分别为MeasureSpec.UNSPECIFIED, MeasureSpec.EXACTLY, MeasureSpec.AT_MOST。
MeasureSpec.EXACTLY是精确尺寸,当我们将控件的layout_width或layout_height指定为具体数值时如andorid:layout_width=”50dip”,或者为FILL_PARENT是,都是控件大小已经确定的情况,都是精确尺寸。
MeasureSpec.AT_MOST是最大尺寸,当控件的layout_width或layout_height指定为WRAP_CONTENT时,控件大小一般随着控件的子空间或内容进行变化,此时控件尺寸只要不超过父控件允许的最大尺寸即可。因此,此时的mode是AT_MOST,size给出了父控件允许的最大尺寸。
MeasureSpec.UNSPECIFIED是未指定尺寸,这种情况不多,一般都是父控件是AdapterView,通过measure方法传入的模式。
知道以上概念之后我们重写测量就容易的多了
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int mode = MeasureSpec.getMode(widthMeasureSpec); int size = MeasureSpec.getSize(widthMeasureSpec); bounds = new Rect(); if (mode == MeasureSpec.EXACTLY) { mwidth = size; } else { paint.setTextSize(msize); paint.getTextBounds(text, 0, text.length(), bounds); mwidth = getPaddingLeft() + getPaddingRight() + bounds.width(); } mode = MeasureSpec.getMode(heightMeasureSpec); size = MeasureSpec.getSize(heightMeasureSpec); if (mode == MeasureSpec.EXACTLY) { mheight = size; } else { paint.getTextBounds(text, 0, text.length(), bounds); mheight = getPaddingBottom() + getPaddingTop() + bounds.height(); } r=Math.max(mwidth,mheight); setMeasuredDimension(r, r); }
- 绘制控件显示(重写onDraw方法)
@Override protected void onDraw(Canvas canvas) { paint.setColor(corlor); canvas.drawCircle(r/2,r/2,r/2,paint); paint.setColor(Color.BLACK); canvas.drawText(text,r/2-bounds.width()/2,r/2+bounds.height()/2,paint); }
- 定义事件
@Override public boolean onTouchEvent(MotionEvent event) { if(event.getAction()==MotionEvent.ACTION_DOWN){ if(changeColor!=null){ changeColor.change(this,text); } } return super.onTouchEvent(event); } public interface ChangeColor{ public void change(MyCuntomView view,String name); } public ChangeColor changeColor; public void setChangeColor(ChangeColor changeColor) { this.changeColor = changeColor; }
MainActivity代码
public class MainActivity extends AppCompatActivity { private MyCuntomView name; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); name = (MyCuntomView) findViewById(R.id.name); name.setChangeColor(new MyCuntomView.ChangeColor() { @Override public void change(MyCuntomView view,String text) { Toast.makeText(MainActivity.this,text,Toast.LENGTH_SHORT).show(); } }); }
以上就是自定义View的整个流程。
查看更多关于【Android开发】的文章