android的基本控件很难满足用户的需求,因此有时候用户需要根据美工设置的一些图片来自定义一些控件,不多说,现在介绍一下,自定义一个开关控件的流程,让大家更了解控件的工作原理:
1、首先构建一个控件的类,继承view,这时候需要覆写view的构造方法。同时需要把美工美化好的图片拖到drawable目录下。在控件初始化的时候需要加载进美工的图片即:背景bg和开关滑动钮swicher。注意加载图片时候R.drawable时候有2个注意别选错了,否则找不到图片的id。
2、android的控件都是通过view下的Ondraw()方法画上去的,因此要覆写view的Oncreate方法将bg和swicher画上去。画画需要画纸和笔,因此在初始化的时候构建一支笔,画板系统提供,即参数里的canvas.里面的参数分别是图、上,左的坐标和笔。因此我们要设定的就是画的位置就是上左的坐标即可。
3、这时候我们可以在xml文件里可以添加这个控件看看效果了,注意添加空间时候一定加上包名和类名(不要带上class如:<com.example.togglebutton.ui.MytoggleButton即可,同时在最上面加上控件的命名空间:复制android的,并将android改成这个程序的包名即可。注意的是,默认下控件大小是屏幕的长宽,因此需要调用onMeasure()方法测量宽高和setMeasuredDimension()重新设定宽高。
4、为了让空间可以拖动,就必须覆写view的onTouchEvent()方法,注意所有的触摸事件的方法都是由这个方法来处理的,然后监听:落下、滑动、离开这3个动作(想更复杂的也可以监听别的事件),通过打印发现只能监听到“落下”的事件,原来这个方法默认是系统处理的,若想用户自己处理就必须将return surper.onTouchEvent()修改成return true;(ps:当你的控件继承其他的具体控件时候,若继承的空间有自己的监听器接口时候,这里就不能改为true,并且已经可监听到,因为继承的控件已经覆写了View的OntouchEvent事件了,若该为true时候继承的操作就失效了)。
5、设定事件处理,我们根据ondraw方法画图,里设定坐标时候将左设定为类的成员变量,上为0,因此当我们改变left的值,然后调用invalidate()刷新操作调用ondraw()方法来重新画图即可。
6、设定控件的属性,这里就添加一个状态state当作事例,首先在values里创建一个xml文件,然后在xml文件里声明、定义属性atrrs。如果大家不记得就可以参考安装的sdk目录下android自带属性的定义:sdkplatformsandroid-19data esvaluesattrs.xml。并且在构造方法:public MytoggleButton(Context context, AttributeSet attrs)里解析属性,好在配置文件中使用属性,并且里面设置默认属性值。
7、在类里给出设定state和得到state的函数,并将state作为函数的成员变量。
8、给空间设定监听事件,这里就定义一个监听state发生改变的接口,在接口里声明state发生变化的方法。然后将接口作为类的成员变量,为类设定一个设置监听器的函数,将接口作为参数,因此设置监听器必须实现这个接口,然后把实现额接口复制给类成员变量。
9在ontouchEvent()的up里先判断状态是否改变,若改变就利用实例化了接口的类来调用接口的onchange方法就完成了状态放生改变的监听事件。
下面给出完整的demo:
main.xml文件
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:myview="http://schemas.android.com/apk/res/com.example.togglebutton.ui" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context="com.example.togglebutton.MainActivity$PlaceholderFragment" android:background="#ffff00" > <com.example.togglebutton.ui.MytoggleButton android:layout_width="wrap_content" android:layout_height="wrap_content" myview:state="true" android:id="@+id/togglebutton" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/text" android:layout_centerVertical="true" android:layout_centerHorizontal="true" /> </RelativeLayout>
values 下的属性定义文件atrrs.xml文件
<?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable name="myview"> <attr name="state" format="boolean" /> </declare-styleable> </resources>
开关控件类的定义
package com.example.togglebutton.ui; import com.example.togglebutton.R; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.drawable.BitmapDrawable; import android.text.Editable.Factory; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; public class MytoggleButton extends View { private Context context; private Bitmap bg; private Bitmap sw; private Paint paint; private int maxleft; private int left; private boolean state; private OnMytoggleButtonStateChangeLitener onMytoggleButtonStateChangeLitener; public void setOnMytoggleButtonStateChangeLitener(OnMytoggleButtonStateChangeLitener l) { onMytoggleButtonStateChangeLitener=l; } public MytoggleButton(Context context) { super(context); this.context=context; init(); } //这里是解析属性的AttributeSet attrs,可以在配置文件中使用这些属性 public MytoggleButton(Context context, AttributeSet attrs) { super(context, attrs); this.context=context; init(); //解析属性 String namespace="http://schemas.android.com/apk/res/com.example.togglebutton"; state=attrs.getAttributeBooleanValue(namespace, "state", true);//默认为关 if(state==false) left=0; else left=maxleft; } public MytoggleButton(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); this.context=context; init(); } //初始化 public void init() { paint=new Paint(); sw=BitmapFactory.decodeResource(getResources(), R.drawable.slide_button_background); bg=BitmapFactory.decodeResource(getResources(), R.drawable.switch_background); maxleft=bg.getWidth()-sw.getWidth(); } public void setState(boolean state) { if(state=true) { left=maxleft; state=true; } else { state=false; left=0; } invalidate(); } public boolean getState() { return state; } @Override//这是空间开始创建时会运行一次 protected void onDraw(Canvas canvas) { canvas.drawBitmap(bg, 0, 0, paint); canvas.drawBitmap(sw, left, 0,paint); super.onDraw(canvas); } @Override//限制空间高宽 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); setMeasuredDimension(bg.getWidth(), bg.getHeight()); } int startX=0; int currentX=0; @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: startX=(int) event.getX(); System.out.println("down"+state); break; case MotionEvent.ACTION_MOVE: currentX=(int) event.getX(); int d=currentX-startX; left=d+left; if(left>=maxleft) left=maxleft; else if(left<=0) left=0; System.out.println("move"+state); break; case MotionEvent.ACTION_UP: boolean stateOlder=state; System.out.println("up"+state); if(left>maxleft/2) { left=maxleft; state=true; } else { left=0; state=false; } if(state!=stateOlder&&onMytoggleButtonStateChangeLitener!=null) onMytoggleButtonStateChangeLitener.stateChange(getState()); break; default: break; } invalidate();//刷新画图,调用空间的ondrow()方法 return true;//super.onTouchEvent(event); } }
activity文件:
public class MainActivity extends Activity { private MytoggleButton togglebutton; private TextView tx; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); togglebutton=(MytoggleButton) findViewById(R.id.togglebutton); tx=(TextView) findViewById(R.id.text); tx.setText("State:"+(togglebutton.getState()?"开":"关")); togglebutton.setOnMytoggleButtonStateChangeLitener(new OnMytoggleButtonStateChangeLitener(){ @Override public void stateChange(boolean state) { tx.setText("State:"+(togglebutton.getState()?"开":"关")); } } ); } }