简单自定义控件,实现按钮开关,继承View
效果图:
触摸事件
优先执行
点击事件
声明自定义属性
步骤1
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="com.example.mhy.zidingyiview.MyToggle">
<attr name="slide_state" format="boolean" />
<!--reference代表该属性为引用类型 -->
<attr name="slide_drawable" format="reference" />
</declare-styleable>
</resources>
步骤2 使用属性
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:myattr="http://schemas.android.com/apk/res/com.example.mhy.zidingyiview"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.mhy.zidingyiview.MainActivity">
<com.example.mhy.zidingyiview.MyToggle
myattr:slide_state="true"
myattr:slide_drawable="@mipmap/slide_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:id="@+id/mcb" />
</RelativeLayout>
步骤3
//在布局文件中调用(没有自定义属性)
public MyToggle(Context context, AttributeSet attrs) {
super(context, attrs);
initView();
//加载自定义属性
flag = attrs.getAttributeBooleanValue(
"http://schemas.android.com/apk/res/com.example.mhy.zidingyiview",
"slide_state", false);
/*第一个参数是自定义属性的命名空间
第二个参数为自定义属性的名字
第三个参数为如果未获取到自定义属性的值,给该变量返回的默认值
*/
int id = attrs.getAttributeResourceValue(
"http://schemas.android.com/apk/res/com.example.mhy.zidingyiview",
"slide_drawable", 0);
if(id!=0){
slide = BitmapFactory.decodeResource(getResources(), id);
}
flushState();
}
完整代码:
values下的attrs.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="com.example.mhy.zidingyiview.MyToggle">
<attr name="slide_state" format="boolean" />
<!--reference代表该属性为引用类型 -->
<attr name="slide_drawable" format="reference" />
</declare-styleable>
</resources>
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:myattr="http://schemas.android.com/apk/res/com.example.mhy.zidingyiview"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.mhy.zidingyiview.MainActivity">
<com.example.mhy.zidingyiview.MyToggle
myattr:slide_state="true"
myattr:slide_drawable="@mipmap/slide_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:id="@+id/mcb" />
</RelativeLayout>
MainActivity.java
package com.example.mhy.zidingyiview;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity {
private MyToggle mcb;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mcb = (MyToggle) findViewById(R.id.mcb);
mcb.setListener(new MyToggle.OnStateChangedListener() {
@Override
public void change(boolean isOpen) {
if (isOpen) {
Toast.makeText(getApplicationContext(), "当前的状态为开", 0)
.show();
} else {
Toast.makeText(getApplicationContext(), "当前的状态为关", 0)
.show();
}
}
});
}
}
MyToggle.java
package com.example.mhy.zidingyiview;
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 mhy on 2016/6/12.
*/
public class MyToggle extends View {
private Bitmap bg;
private Bitmap slide;
/**
* 记录控件开关状态
*/
private boolean flag = false;
private int max_distance;
//滑块距离父控件左边的距离
private int slide_left;
//在自定义文件中使用(使用了自定义属性)
public <span style="color:#ff6666;"><strong>MyToggle</strong></span>(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView();
}
//一般代码中调用
public <span style="color:#ff0000;"><strong>MyToggle</strong></span>(Context context) {
super(context);
initView();
}
//在布局文件中调用(没有自定义属性)
public <span style="color:#ff6666;"><strong>MyToggle</strong></span>(Context context, AttributeSet attrs) {
super(context, attrs);
initView();
//加载自定义属性
flag = attrs.getAttributeBooleanValue(
"http://schemas.android.com/apk/res/com.example.mhy.zidingyiview",
"slide_state", false);
/*第一个参数是自定义属性的命名空间
第二个参数为自定义属性的名字
第三个参数为如果未获取到自定义属性的值,给该变量返回的默认值
*/
int id = attrs.getAttributeResourceValue(
"http://schemas.android.com/apk/res/com.example.mhy.zidingyiview",
"slide_drawable", 0);
if(id!=0){
slide = BitmapFactory.decodeResource(getResources(), id);
}
flushState();
}
private void initView() {
bg = BitmapFactory.decodeResource(getResources(),
R.mipmap.switch_background);
slide = BitmapFactory.decodeResource(getResources(),
R.mipmap.slide_button);
max_distance = bg.getWidth() - slide.getWidth();
//设置条目的点击事件
setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
//修改滑块的状态
if (isClick) {
if (flag) {
flag = false;
} else {
flag = true;
}
//刷新界面
flushState();
}
}
});
}
// 回调方法
private OnStateChangedListener listener;
// 可以设置接口
public void setListener(OnStateChangedListener listener) {
this.listener = listener;
}
// 定义了接口
public interface O<span style="color:#ff0000;"><strong>nStateChangedListener</strong></span> {
void change(boolean isOpen);
}
private int startX;
private boolean isClick = true;// 是否允许响应点击事件
//触摸事件
int distance;//记录总共滑动的距离
@Override
public boolean <span style="color:#ff0000;"><strong>onTouchEvent</strong></span>(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN://按下的操作
//步骤1 记录开始的位置
startX = (int) event.getX();
System.out.println("ACTION_DOWN");
break;
case MotionEvent.ACTION_MOVE:// 手指移动调用
System.out.println("ACTION_MOVE");
// 步骤2 记录新的位置
int newX = (int) event.getX();
// 步骤3 记录坐标的改变
int dx = newX - startX;
distance += Math.abs(dx);
// 步骤4 修改位置
slide_left += dx;
// invalidate();
flushView();
// 步骤5
startX = newX;
break;
case MotionEvent.ACTION_UP:
if (Math.abs(distance) > 20) { // 如果发现滑动的总距离 大于20像素 认为处理了触摸事件
// 不要处理点击事件
isClick = false;//屏蔽了点击
distance = 0; // 重置滑动总距离
}
// 如果点击事件能执行 不会执行触摸事件 如果触摸事件执行 就不会执行点击事件
if(!isClick) {
System.out.println("ACTION_UP");
if (slide_left > max_distance / 2) {
flag = true;
} else {
flag = false;
}
flushState();
}
break;
default:
break;
}
//由系统处理是滑动还是点击
return super.onTouchEvent(event);
}
public void flushView() {
// 坐标修正
if (slide_left < 0) {
slide_left = 0;
} else if (slide_left > max_distance) {
slide_left = max_distance;
}
invalidate();
}
private void flushState() {
if (flag) {
slide_left = max_distance;
} else {
slide_left = 0;
}
if (listener != null) {
listener.change(flag);// 把当前的选中状态 赋值给接口
}
invalidate(); //刷新界面 重新调用onDraw方法
}
//测量控件的大小 onMeasure 在onDraw方法前调用 用来测量控件的宽和高
@Override
protected void <span style="color:#ff6666;"><strong>onMeasure</strong></span>(int widthMeasureSpec, int heightMeasure) {
setMeasuredDimension(bg.getWidth(), bg.getHeight());//测量当前控件的宽和高
}
@Override
protected void <span style="color:#ff6666;"><strong>onDraw</strong></span>(Canvas canvas) {
//Paint paint = new Paint();
//paint.setColor(Color.RED);
canvas.drawBitmap(bg, 0, 0, null);
canvas.drawBitmap(slide, slide_left, 0, null);
}
}