首先,自定义控件分为三类:
自定义的组合控件
继承View的自定义控件
继承ViewGroup的自定义控件
在这里,我要写的是第二种,也就是继承自View的自定义控件,第一种自定义的组合控件,我已经写过了,可以在我的博客中可以找到
现在来看一下继承View的自定义控件
首先,需要写一个类继承自View,那么,它也有三个构造方法,有一个参数的构造方法实在代码中new这个自定义控件时被调用;有两个参数的构造方法是在布局中使用这个自定义控件的时候调用,有三个参数的构造方法,实在使用到这个自定义控件的样式时被调用;同样,用到那个就重写那个
其次,需要重写onMeasure()方法和onDraw()方法
onMeasure()方法是为了测量控件自己的宽高,onDraw()方法是为了绘制的内容,如果你继承的是ViewGroup,那么你还需要重写onLayout()方法
最后,实现业务逻辑
在这里,我实现的是一个开关的效果
如下图:
这是可以滑动和点击的
自定义属性的步骤,具体请参考我的自定义的组合控件,哪里已经做了详细说明
1、先自定义一个类继承View
// 在代码中创建控件
public MyButton(Context context) {
super(context);
}
// 控件使用在xml布局文件中
public MyButton(Context context, AttributeSet attrs) {
super(context, attrs);
}
// 使用样式时
public MyButton(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
接下来,你需要在布局中使用这个控件,用全类名
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:abc="http://schemas.android.com/apk/res/com.abc.togglestate"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<com.wdj.togglestate.ui.MyButton
android:id="@+id/mybutton"
android:layout_width="match_parent"
android:layout_height="match_parent"
abc:state="false"
abc:backgroundRes="@drawable/switch_background"
abc:slideButtonRes="@drawable/slide_button_background"
/>
</RelativeLayout>
2、重写onMeasure()方法
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//把背景图片的宽高作为控件的宽高
setMeasuredDimension(mSwitchBackground.getWidth(), mSwitchBackground.getHeight());
}
3、重写onDraw()方法
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawBitmap(mSwitchBackground, 0, 0, null);
if(!isTouching){
//根据当前状态滑动图片
int left = 0;
if(currentToggleState){ //如果是打开状态,左边距等于背景的宽度-滑块的宽度
left = mSwitchBackground.getWidth() - mSlideButton.getWidth();
}else {
//关闭状态
left = 0;
}
canvas.drawBitmap(mSlideButton, left, 0, null);
}else{
//根据手指触摸的位置,绘制滑动的图片
int left = currentX - mSlideButton.getWidth() / 2; //为了让用户感觉手在图片中间滑动
if(left < 0){ //图片超出左边界,直接绘制到0位置
left = 0;
}else if(left >= mSwitchBackground.getWidth() - mSlideButton.getWidth()){ //图片超出右边界,直接绘制到右边
left = mSwitchBackground.getWidth() - mSlideButton.getWidth();
}
canvas.drawBitmap(mSlideButton, left, 0, null);
}
}
接下来就是处理自己的业务逻辑
在这里,我把代码全部放在这里了,这是MyButton .java
public class MyButton extends View {
private Bitmap mSwitchBackground;
private Bitmap mSlideButton;
private boolean currentToggleState; //记录当前开关的状态
private int currentX;
private boolean isTouching = false; //记录当前控件是否处于触摸中
private MyButtonOnStateChangedListener listener;
private String namespace = "http://schemas.android.com/apk/res/com.abc.togglestate";
/**
* 在代码中创建控件调用
* @param context,
*/
public MyButton(Context context) {
super(context);
}
/**
* 控件使用在xml布局中使用
* @param context
* @param attrs
*/
public MyButton(Context context, AttributeSet attrs) {
super(context, attrs);
//获取布局文件中的属性
int backgroundRes = attrs.getAttributeResourceValue(namespace, "backgroundRes",0);
setBackgroundRes(backgroundRes);
int slideButtonRes = attrs.getAttributeResourceValue(namespace, "slideButtonRes", 0);
setSlidButtonBackgroundRes(slideButtonRes);
currentToggleState = attrs.getAttributeBooleanValue(namespace, "state", false);
}
/**
* 使用样式时调用
* @param context
* @param attrs
* @param defStyle
*/
public MyButton(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
/**
* 测量自己的宽高
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//把背景图片的宽高作为控件的宽高
setMeasuredDimension(mSwitchBackground.getWidth(), mSwitchBackground.getHeight());
}
/**
* 绘制内容
*/
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawBitmap(mSwitchBackground, 0, 0, null);
if(!isTouching){
//根据当前状态滑动图片
int left = 0;
if(currentToggleState){ //如果是打开状态,左边距等于背景的宽度-滑块的宽度
left = mSwitchBackground.getWidth() - mSlideButton.getWidth();
}else {
//关闭状态
left = 0;
}
canvas.drawBitmap(mSlideButton, left, 0, null);
}else{
//根据手指触摸的位置,绘制滑动的图片
int left = currentX - mSlideButton.getWidth() / 2; //为了让用户感觉手在图片中间滑动
if(left < 0){ //图片超出左边界,直接绘制到0位置
left = 0;
}else if(left >= mSwitchBackground.getWidth() - mSlideButton.getWidth()){ //图片超出右边界,直接绘制到右边
left = mSwitchBackground.getWidth() - mSlideButton.getWidth();
}
canvas.drawBitmap(mSlideButton, left, 0, null);
}
}
/**
* 给控件设置背景图片
* @param switchBackground
*/
public void setBackgroundRes(int switchBackground) {
mSwitchBackground = BitmapFactory.decodeResource(getResources(),switchBackground);
}
/**
* 给控件设置滑块
* @param slideButtonBackground
*/
public void setSlidButtonBackgroundRes(int slideButtonBackground) {
mSlideButton = BitmapFactory.decodeResource(getResources(), slideButtonBackground);
}
/**
* 开关的状态(这里没有用到)
* 用于外部调用
* @param toggleState
*/
public void isState(boolean toggleState) {
currentToggleState = toggleState;
}
/**
* 处理触摸事件
* 触摸后,获取当前的触摸位置,根据位置,更新控件
*/
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
isTouching = true;
currentX = (int) event.getX();
break;
case MotionEvent.ACTION_MOVE:
currentX = (int) event.getX();
break;
case MotionEvent.ACTION_UP:
isTouching = false;
currentX = (int) event.getX();
//手抬起后,更改当前控件的状态,根据当前手触摸的 位置和背景图片的中间值进行比较
boolean tempState = currentX >= mSwitchBackground.getWidth() / 2;
//6.3、判断当前的状态是否发生变化
if(tempState != currentToggleState){
currentToggleState = tempState;
if(listener != null){
listener.OnStateChanged(currentToggleState);
}
}
break;
}
invalidate(); //重新绘制控件,自动触发onDraw(在主线程中绘制控件)
//postInvalidate(); //重新绘制控件,自动触发onDraw(在子线程中绘制控件)
return true; //自己消费事件
}
//6.1、对外提供开关监听
public interface MyButtonOnStateChangedListener{
public void OnStateChanged(boolean state);
}
//6.2、让外界把监听器传进来
public void setMyButtonOnStateChangedListener(MyButtonOnStateChangedListener listener){
this.listener = listener;
}
}
MainActivity.java代码如下
public class MainActivity extends Activity {
private MyButton mybutton;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mybutton = (MyButton) findViewById(R.id.mybutton);
mybutton.setMyButtonOnStateChangedListener(new MyButtonOnStateChangedListener() {
@Override
public void OnStateChanged(boolean state) {
//出来开关状态业务发生变化
Toast.makeText(getApplicationContext(), "" + state, 0).show();
}
});
}
}
希望能对看到这篇博客的小伙伴有所帮助,仅供大家参考