开发过程经常需要我们控件。除了使用系统控件之外,我们也需要自定义控件实现特定的效果。
通常的自定义控件的步骤为:
同时如果为了扩展起见,我们 希1、 在attrs.xml文件中声明属性,由属性名:name和格式:format。如:<declare-styleable name=”MyToggleBtn”>
<attr name=”current_state” format=”boolean” />
</declare-styleable>
望支持自定义属性。通过参考系统自带控件,一般可划分为三步:
首先,在attrs.xml文件中声明属性,由属性名:name和格式:format。如:
<declare-styleablename=”MyToggleBtn”>
<attr name=”current_state”format=”boolean” />
</declare-styleable>
fromat常用的类型
reference |
引用 |
color |
颜色 |
boolean |
布尔值 |
dimension |
尺寸 |
float |
浮点值 |
integer |
整型值 |
string |
字符串 |
enum |
枚举类型 |
其次,在布局文件中使用新属性,使用之前必须先声明命名空间,如:xmlns:ldd="http://schemas.android.com/apk/res/com.example.togglebutton"(xmlns:自定义名称=http://schemas.android.com/apk/res/包名)。
最后在自定义view的构造方法中,通过解析attributeSet对象获得所需的属性值。
下面是完整的代码:
package com.example.togglebutton; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Paint; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; public class MyToggle extends View implements View.OnClickListener{ private int lastX; private int firstX; private boolean isDrag=false; private int backgroundId; private Bitmap backgroundBp; private Bitmap slideBp; private float slide_left; private Paint paint; private boolean currState=false; private int slideId; public MyToggle(Context context) { super(context); } public MyToggle(Context context,AttributeSet attrs) { super(context, attrs); //实际可用类型 TypedArray ta=context.obtainStyledAttributes(attrs, R.styleable.MyBtn); int taCount=ta.getIndexCount(); for(int i=0;i<taCount;i++){ int itId=ta.getIndex(i); switch (itId) { case R.styleable.MyBtn_my_background: backgroundId = ta.getResourceId(itId, -1); backgroundBp=BitmapFactory.decodeResource(this.getResources(), backgroundId); break; case R.styleable.MyBtn_my_slide_btn: slideId = ta.getResourceId(itId, -1); slideBp=BitmapFactory.decodeResource(this.getResources(), slideId); break; case R.styleable.MyBtn_cur_state: currState=ta.getBoolean(itId,false); break; } } initView(); } /** * * @Title: initView * @Description: 初始化 */ private void initView() { paint=new Paint(); paint.setAntiAlias(true);//开启抗锯齿,画笔平滑过渡 setOnClickListener(this); flushState(); } @Override /** *1、 测量尺寸的回调方法 */ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { // super.onMeasure(widthMeasureSpec, heightMeasureSpec); //设置view的大小 setMeasuredDimension(backgroundBp.getWidth(), backgroundBp.getHeight()); } @Override /** * 2、确定位置的时候的回调 * 自定义View没用 */ protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); } @Override /** *3、 绘画 方法,初始化或者刷新时自动调用 */ protected void onDraw(Canvas canvas) { canvas.drawBitmap(backgroundBp, 0, 0, paint); canvas.drawBitmap(slideBp, slide_left, 0, paint); } @Override /** * 为控件添加点击事件 */ public void onClick(View v) { if(!isDrag){//是否屏蔽click currState=!currState; flushState(); } } @Override /** * 添加触摸事件,支持左右拖拽 */ public boolean onTouchEvent(MotionEvent event) { super.onTouchEvent(event); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: firstX = lastX = (int) event.getX(); isDrag = false; break; case MotionEvent.ACTION_MOVE: if (Math.abs(event.getX() - firstX) > 6) { isDrag = true; } int dis = (int) (event.getX() - lastX); lastX = (int) event.getX(); slide_left = slide_left + dis; break; case MotionEvent.ACTION_UP: if (isDrag) { // 判断当前状态,并自动补全 int max = backgroundBp.getWidth() - slideBp.getWidth(); if (slide_left > max / 2) { currState = true; } else { currState = false; } flushState(); } break; } flushView(); return true; } /** * @Title: flushView * @Description: 刷新视图 */ private void flushView() { int max=backgroundBp.getWidth()-slideBp.getWidth(); slide_left=slide_left>0?slide_left:0;//控制左边界 slide_left=slide_left<max?slide_left:max;//控制右边界 invalidate(); } /** * * @Title: flushState * @Description: 刷新当前开关状态 */ private void flushState() { if(currState){ slide_left=backgroundBp.getWidth()-slideBp.getWidth(); }else{ slide_left=0; } flushView(); } }
自定义属性文件:
<resources> <declare-styleable name="MyBtn"> <attr name="my_background" format="reference" /> <attr name="my_slide_btn" format="reference" /> <attr name="cur_state" format="boolean" /> </declare-styleable> </resources>
控件使用:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:ldd="http://schemas.android.com/apk/res/com.example.togglebutton" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="${relativePackage}.${activityClass}" > <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/hello_world" /> <com.example.togglebutton.MyToggle android:id="@+id/my_btn" android:layout_width="wrap_content" android:layout_height="wrap_content" ldd:my_background="@drawable/switch_background" ldd:my_slide_btn="@drawable/slide_button" ldd:cur_state="false" android:layout_centerHorizontal="true" android:layout_centerVertical="true" Test="hello" /> </RelativeLayout>
最终效果是开关按钮。
总之,自定义控件参考系统自带控件的写法即可。完整 示例项目:http://download.csdn.net/download/dd864140130/8222541