手把手写Android自定义控件(四):添加状态监听器

上一篇讲解了控件的测量与绘制,难度一下子上来了。本篇来讲解点击事件和开关状态获取,相比于上一篇,这部分的内容相对简单一点。

设置与获取开关状态

回顾一下上一篇的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鼓励一下。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值