上一篇讲解了控件的测量与绘制,难度一下子上来了。本篇来讲解点击事件和开关状态获取,相比于上一篇,这部分的内容相对简单一点。
设置与获取开关状态
回顾一下上一篇的init方法。
private void init(){
mPaint = new Paint();
mPaint.setAlpha(255);
mPaint.setAntiAlias(true);
//1
switchOn = false;
setOnClickListener(null);
}
这里的开关状态switchOn 是直接初始化为false的。现在把代码1删了,我们通过xml的方式获取。
打开之前的参数声明文件 attr_switch.xml,添加一个自定义参数switchOn,用来设置开关的初始状态。
<resources>
<declare-styleable name="Switch">
<attr name="OnColor" format="color"/>
<attr name="OffColor" format="color"/>
<attr name="InsideColor" format="color"/>
<attr name="BackColor" format="color"/>
<attr name="SwitchPadding" format="dimension"/>
<attr name="InsidePadding" format="dimension"/>
<attr name="radius" format="dimension"/>
<!--初始状态-->
<attr name="switchOn" format="boolean"/>
</declare-styleable>
</resources>
然后在之前写的 getAttrFromXml 方法里获取它。
private void getAttrFromXml(Context context,AttributeSet attrs){
TypedArray typedArray = context.obtainStyledAttributes(attrs,R.styleable.Switch);
mInsideColor = typedArray.getColor(R.styleable.Switch_InsideColor, Color.GREEN);
mOnColor = typedArray.getColor(R.styleable.Switch_OnColor, Color.BLUE);
mOffColor = typedArray.getColor(R.styleable.Switch_OffColor, Color.GRAY);
mBackColor = typedArray.getColor(R.styleable.Switch_BackColor, Color.RED);
mSwitchPadding = typedArray.getDimensionPixelSize(R.styleable.Switch_SwitchPadding,0);
mInsidePadding = typedArray.getDimensionPixelSize(R.styleable.Switch_InsidePadding,0);
mRadius = typedArray.getDimensionPixelSize(R.styleable.Switch_radius,0);
//默认开关状态设置为false
switchOn = typedArray.getBoolean(R.styleable.Switch_switchOn,false);
typedArray.recycle();
}
为了能让外界控制开关,我们再写get 和 set 方法。
public boolean isSwitchOn(){
return switchOn;
}
public void setSwitchOn(boolean switchOn){
this.switchOn = switchOn;
invalidate();
}
这里注意一下invalidate方法。这个方法会强制让控件重新绘制。因为这个方法改变了开关的状态,开 和 关的图像是不一样的,所以这里调用invalidate方法重绘一下画面。
点击事件监听器 和 状态监听器
OnClickListener 相信大家已经非常熟悉了,为了能在点击时改变开关的状态,我们需要在setOnclickListener方法上做点小改动。
不过首先来创建一个状态监听的静态内部类。类似于OnClickListener,只不过它是在状态改变时调用。
public static interface OnStateChangeListener{
void onStateChanged(View view,boolean switchOn);
}
重写一下setOnClickListener方法。
private OnClickListener mOnClickListener;
private OnStateChangeListener mOnStateChangeListener;
@Override
public void setOnClickListener(@Nullable OnClickListener l) {
mOnClickListener = l;
OnClickListener onSwitchClickListener = new OnClickListener() {
@Override
public void onClick(View v) {
if(mOnClickListener != null){
mOnClickListener.onClick(v);
}
setSwitchOn(!switchOn);
}
};
super.setOnClickListener(onSwitchClickListener);
}
这么写有一个好处,在设置OnClickListener的时候不会把状态监听的回调方法弄丢。
接着回到刚刚setSwitchOn方法,添加状态监听回调的代码。
public void setSwitchOn(boolean switchOn){
this.switchOn = switchOn;
if(mOnStateChangeListener != null){
mOnStateChangeListener.onStateChanged(this,switchOn);
}
invalidate();
}
如此一来,无论是外界调用setSwitchOn方法,还是点击这个控件,开关状态都会随之改变了。
为了能让控件一开始就能监听开关状态,这里再改改init方法。
private void init(){
mPaint = new Paint();
mPaint.setAlpha(255);
mPaint.setAntiAlias(true);
setOnClickListener(null);
}
简单测试
这里贴上布局文件和Activity的完整代码,测试一下效果。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<com.pyjtlk.widgetlib.Switch
android:id="@+id/MySwitch"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:BackColor="#AA0000"
app:InsideColor="#FF00AA00"
app:OffColor="#FFAAAAAA"
app:OnColor="#0000AA"
app:InsidePadding="10dp"
app:SwitchPadding="5dp"
app:radius="20dp"/>
</LinearLayout>
public class MainActivity extends AppCompatActivity {
private Switch aSwitch;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
aSwitch = findViewById(R.id.MySwitch);
aSwitch.setOnStateChangeListener(new Switch.OnStateChangeListener() {
@Override
public void onStateChanged(View view, boolean switchOn) {
String result;
if(switchOn){
result = "打开";
}else{
result = "关闭";
}
Toast.makeText(MainActivity.this,result, Toast.LENGTH_SHORT).show();
}
});
}
}
效果如下。
至此,整个Switch控件的基本功能就实现了。
完整项目
虽然说基本功能实现了,但其实控件还有很多可以改进和拓展的地方,如支持过渡动画、防止内存泄漏、支持自定义图案、修复Padding的Bug等等。感兴趣的朋友可以看看Github项目,喜欢这个控件的话可以star鼓励一下。