自定义控件步骤:
1. 写类继承View
2. 重写onDraw, 进行绘制
3. 重新onMeasure,修改尺寸 要使用 setMeasuredDimension设置当前自定义View的大小
4. 在xml布局文件中配置
可能需要重写的方法:
测量:onMeasure 设置自己显示在屏幕上的宽高 在重写这个方法时,必须调用setMeasuredDimension(int, int)来存储测量得到的宽度和高度值
布局:onLayout 设置自己显示在屏幕上的位置(只有在自定义ViewGroup中才用到) 需要调用各自的layout(int, int, int, int)方法View没有子view,所以不需要onLayout方法,但是必须实现onDraw
自定义控件实际操作:
1、创建类继承View
2、重写两个构造器
3、重写
onMeasure并调用setMeasuredDimension方法设置控件的宽和高
onDraw 绘制自己显示在屏幕上的样子
4、定义操作控件的方法
a、设置开关块的图片
b、设置开关背景的图片
c、设置更改开关状态的方法
5、定义onTouchEvent事件
6、定义接口对外开关状态
onMeasure并调用setMeasuredDimension方法设置控件的宽和高
onDraw 绘制自己显示在屏幕上的样子
a、设置开关块的图片
b、设置开关背景的图片
c、设置更改开关状态的方法
public class MySwitch extends View { private SwitchState sState; // 开关状态 private Bitmap slideBitmap; private Bitmap switchBitmap; /** * 如果自定义的View只是在布局文件中使用,只需要重写这个构造方法 * * @param context * @param attrs */ public MySwitch(Context context, AttributeSet attrs) { super(context, attrs); } /** * 如果自定义的View是在Java代码中动态的new出来,需要重写这个构造方法 * * @param context */ public MySwitch(Context context) { super(context); } /** * 设置活动块的图片 */ public void setSlideBackgroudResource(int slideBackground) { // 将资源文件转换为Bitmap slideBitmap = BitmapFactory.decodeResource(getResources(), slideBackground); } /** * 设置switch的图片 * * @param switchBackground */ public void setSwitchBackgroundResource(int switchBackground) { // 将资源文件转换为Bitmap switchBitmap = BitmapFactory.decodeResource(getResources(), switchBackground); } /** * Switch开关状态的枚举 * * @author Denny * @date 2016-4-25 */ public enum SwitchState { OPEN, CLOSE } /** * 设置Switch开关状态 * * @param open */ public void setSwitchState(SwitchState open) { sState = open; } /** * 设置当前控件在屏幕上的宽和高 */ @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); // 设置当前View的大小 这个方法决定了当前View的大小 setMeasuredDimension(switchBitmap.getWidth(), switchBitmap.getHeight()); } /** * 绘制自己显示在屏幕上的样子 */ @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); // 1.在某一个位置 绘制背景图片 // left:图片的左边的x坐标 // top:图片的顶部的y坐标 canvas.drawBitmap(switchBitmap, 0, 0, null); // 2.绘制滑动块图片 //当抬起的时候 绘制滑动块的位置 // 根据SwitchState 确定滑动块的位置 // 如果滑动块在左边的话 left:0 top:0 // 如果滑动块在右边:left : 背景的宽 - 滑动块的宽 top:0 if (sState == SwitchState.OPEN) { canvas.drawBitmap(slideBitmap, switchBitmap.getWidth() - slideBitmap.getWidth(), 0, null); } else if (sState == SwitchState.CLOSE) { canvas.drawBitmap(slideBitmap, 0, 0, null); } } }
定义一个变量用于判断是根据滑动还是 根据状态进行 操作
private boolean isSliding = false;
@Override public boolean onTouchEvent(MotionEvent event) { // 获取当前的x点 currentX = (int) event.getX(); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: isSliding = true; break; case MotionEvent.ACTION_MOVE: break; case MotionEvent.ACTION_UP: isSliding = false; //获取中间线 int centernX = switchBitmap.getWidth() / 2; //判断当前x值和中间线的大小 更改SwitchState if( currentX >= centernX){ //open if(sState != SwitchState.OPEN){ sState = SwitchState.OPEN; } }else{ //close if(sState != SwitchState.CLOSE){ sState = SwitchState.CLOSE; } } break; } // 调用invalidate()方法,请求重新draw() invalidate(); return true; }
protected void onDraw(Canvas canvas) { super.onDraw(canvas); // 1.在某一个位置 绘制背景图片 // left:图片的左边的x坐标 // top:图片的顶部的y坐标 canvas.drawBitmap(switchBitmap, 0, 0, null); // 2.绘制滑动块图片 //如果是滑动的话 则根据currentX来进行操作 if(isSliding ){ //当鼠标移动到slideBitmap一半的时候才会有响应 int left = currentX - slideBitmap.getWidth() / 2; //如果left if (left < 0) { left = 0; } int maxX = switchBitmap.getWidth() - slideBitmap.getWidth(); if (left > (maxX)) { left = maxX; } canvas.drawBitmap(slideBitmap, left, 0, null); }else{ //当抬起的时候 绘制滑动块的位置 // 根据SwitchState 确定滑动块的位置 // 如果滑动块在左边的话 left:0 top:0 // 如果滑动块在右边:left : 背景的宽 - 滑动块的宽 top:0 if (sState == SwitchState.OPEN) { canvas.drawBitmap(slideBitmap, switchBitmap.getWidth() - slideBitmap.getWidth(), 0, null); } else if (sState == SwitchState.CLOSE) { canvas.drawBitmap(slideBitmap, 0, 0, null); } } }
b、定义根据接口来更改状态的方法
public interface OnSwitchStateChangedListern{ void onSwitchStateChenged(SwitchState state); } private OnSwitchStateChangedListern listern; public void setOnSwitchStateChangedListern(OnSwitchStateChangedListern listern) { this.listern = listern; }
public boolean onTouchEvent(MotionEvent event) { // 获取当前的x点 currentX = (int) event.getX(); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: isSliding = true; break; case MotionEvent.ACTION_MOVE: break; case MotionEvent.ACTION_UP: isSliding = false; //获取中间线 int centernX = switchBitmap.getWidth() / 2; //判断当前x值和中间线的大小 更改SwitchState if( currentX >= centernX){ //open if(sState != SwitchState.OPEN){ sState = SwitchState.OPEN; if(listern != null){ listern.onSwitchStateChenged(sState); } } }else{ //close if(sState != SwitchState.CLOSE){ sState = SwitchState.CLOSE; if(listern != null){ listern.onSwitchStateChenged(sState); } } } break; } // 调用invalidate()方法,请求重新draw() invalidate(); return true; }
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity" > <com.example.togbutton.view.MySwitch android:id="@+id/ms_btn" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" /> </RelativeLayout>
public class MainActivity extends Activity { private MySwitch mySwitch; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mySwitch = (MySwitch) findViewById(R.id.ms_btn); // 设置滑动块的背景图片 mySwitch.setSlideBackgroudResource(R.drawable.slide_button); // 设置Switch背景图片 mySwitch.setSwitchBackgroundResource(R.drawable.switch_background); // 设置更改Switch的开关状态 mySwitch.setSwitchState(SwitchState.OPEN); // 设置Switch的开关状态监听器 mySwitch.setOnSwitchStateChangedListern(new OnSwitchStateChangedListern() { @Override public void onSwitchStateChenged(SwitchState state) { Toast.makeText(MainActivity.this, state == SwitchState.OPEN ? "开启" : "关闭", 0).show(); } }); } }