Android中的自定义控件(二),android自定义控件
案例四: 自定义开关
功能介绍:本案例实现的功能是创建一个自定义的开关,可以自行决定开关的背景。当滑动开关时,开关的滑块可跟随手指移动。当手指松开后,滑块根据开关的状态,滑到最右边或者滑到最左边,同时保存开关的状态,将开关的状态回调给调用者。当然,上述功能系统给定的switch控件也可以实现。
实现步骤:
1. 写一个类继承view,重写两个参数的构造方法。在构造方法中指定工作空间,通过attrs.getAttributeResourceValue方法将java代码中的属性值和xml中的属性值联系起来。这样可以在xml文件中指定相关的属性值。重写onmeasure和ondraw方法,绘制图片。这里测量图片大小直接用setMeasuredDimension方法,获取图片本身的大小。
2. 设置接口回调。对于图片来说,我们希望能够在调用者获取开关的状态,因此需要设置一个接口回调,用于监控开关的状态,当开关的状态发生变化时间调用。接口回调的优势在于调用者并不知道何时调用,所以在另一个文件中设置一个接口,在该文件触发事件。由于重写了接口的方法,因此,执行重写后的方法。这样就可以实现数据的回调。自定义控件中接口回调的应用较为广泛,几乎所有的控件都需要设置监听,且写法较为固定。
3. 重写ontouchevent()方法。分析得知,开关由两部分组成,一部分是底座儿,一部分是划片而。当手指滑动时,划片儿应该跟随手指移动。当划片的左边小于底座左边坐标时,让划片左边的坐标和底座对齐,当划片的右边大于底座右边坐标时,让划片右边的坐标和底座对齐,这样保证划片不越界。当手指松开后,判断划片的中线坐标是在底座儿中线坐标的左边还是右边,以此来决定划片最终是停在左边还是右边。同时改变开关的状态,将开关的状态回调给控件的调用中,读取开关的状态。
代码实现。 主程序(调用者)中的代码:
package com.example.aswitch; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.widget.Toast; public class MainActivity extends AppCompatActivity { private MyToggleButton toggleButton; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); toggleButton = (MyToggleButton) findViewById(R.id.toggle_button); toggleButton.setOnStateChangedListener(new MyToggleButton.OnStateChangedListener() { @Override public void onStateChanged(boolean state) { Toast.makeText(MainActivity.this, state ? "开" : "关", Toast.LENGTH_SHORT).show(); } }); } }
自定义开关的具体实现;
package com.example.aswitch; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; /** * Created by huang on 2016/12/1. */ public class MyToggleButton extends View { private Bitmap background; private Bitmap slideIcon; private boolean state; private OnStateChangedListener mOnStateChangedListener; private int backgroundWidth; private int backgroundHeight; private int slideIconWidth; private int slideIconHeight; private int slideIconLeft; private int maxSlideIconLeft; public MyToggleButton(Context context, AttributeSet attrs) { super(context, attrs); String namespace = "http://schemas.android.com/apk/res-auto"; int slideBackgroundResId = attrs.getAttributeResourceValue(namespace, "slideBackground", -1); int slideIconResId = attrs.getAttributeResourceValue(namespace, "slideIcon", -1); if (slideBackgroundResId != -1 && slideIconResId != -1) { setSwitchImage(slideBackgroundResId, slideIconResId); } boolean state = attrs.getAttributeBooleanValue(namespace, "state", false); setState(state); } /** * 设置开关的图片 * @param slideBackgroundResId 开关的背景图片资源id * @param slideIconResId 开关上面的滑块icon */ public void setSwitchImage(int slideBackgroundResId, int slideIconResId) { background = BitmapFactory.decodeResource(getResources(), slideBackgroundResId); slideIcon = BitmapFactory.decodeResource(getResources(), slideIconResId); backgroundWidth = background.getWidth(); backgroundHeight = background.getHeight(); slideIconWidth = slideIcon.getWidth(); slideIconHeight = slideIcon.getHeight(); maxSlideIconLeft = backgroundWidth - slideIconWidth; } /** 设置开关按钮的状态 */ public void setState(boolean state) { checkState(state); if (state) { slideIconLeft = maxSlideIconLeft; } else { slideIconLeft = 0; } } public void setOnStateChangedListener(OnStateChangedListener mOnStateChangedListener) { this.mOnStateChangedListener = mOnStateChangedListener; } /** 开关按钮状态改变的监听器 */ public interface OnStateChangedListener { void onStateChanged(boolean state); } /** * 对View进行测量的方法 */ @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { setMeasuredDimension(backgroundWidth, backgroundHeight); } /** 把View画出来的方法 * @param canvas 画布 * */ @Override protected void onDraw(Canvas canvas) { int left = 0; int top = 0; canvas.drawBitmap(background, left, top, null); canvas.drawBitmap(slideIcon, slideIconLeft, 0, null); } @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction())