Andriod下完全自定义控件和在自定义控件中使用自定义属性

首先,自定义控件分为三类:

自定义的组合控件
继承View的自定义控件
继承ViewGroup的自定义控件

在这里,我要写的是第二种,也就是继承自View的自定义控件,第一种自定义的组合控件,我已经写过了,可以在我的博客中可以找到
现在来看一下继承View的自定义控件

首先,需要写一个类继承自View,那么,它也有三个构造方法,有一个参数的构造方法实在代码中new这个自定义控件时被调用;有两个参数的构造方法是在布局中使用这个自定义控件的时候调用,有三个参数的构造方法,实在使用到这个自定义控件的样式时被调用;同样,用到那个就重写那个
其次,需要重写onMeasure()方法和onDraw()方法
onMeasure()方法是为了测量控件自己的宽高,onDraw()方法是为了绘制的内容,如果你继承的是ViewGroup,那么你还需要重写onLayout()方法
最后,实现业务逻辑
在这里,我实现的是一个开关的效果
如下图:
这里写图片描述
这是可以滑动和点击的
自定义属性的步骤,具体请参考我的自定义的组合控件,哪里已经做了详细说明
1、先自定义一个类继承View

    // 在代码中创建控件
    public MyButton(Context context) {
        super(context);
    }

    // 控件使用在xml布局文件中
    public MyButton(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    // 使用样式时
    public MyButton(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

接下来,你需要在布局中使用这个控件,用全类名

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:abc="http://schemas.android.com/apk/res/com.abc.togglestate"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
     >

    <com.wdj.togglestate.ui.MyButton
        android:id="@+id/mybutton"
        android:layout_width="match_parent"
        android:layout_height="match_parent" 
        abc:state="false"
        abc:backgroundRes="@drawable/switch_background"
        abc:slideButtonRes="@drawable/slide_button_background"   
        />

</RelativeLayout>

2、重写onMeasure()方法

@Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        //把背景图片的宽高作为控件的宽高
        setMeasuredDimension(mSwitchBackground.getWidth(), mSwitchBackground.getHeight());
    }

3、重写onDraw()方法

@Override
    protected void onDraw(Canvas canvas) {

        super.onDraw(canvas);
        canvas.drawBitmap(mSwitchBackground, 0, 0, null);
        if(!isTouching){
            //根据当前状态滑动图片
            int left = 0;
            if(currentToggleState){ //如果是打开状态,左边距等于背景的宽度-滑块的宽度
                left = mSwitchBackground.getWidth() - mSlideButton.getWidth();
            }else {
                //关闭状态
                left = 0;
            }
            canvas.drawBitmap(mSlideButton, left, 0, null);
        }else{
            //根据手指触摸的位置,绘制滑动的图片
            int left = currentX - mSlideButton.getWidth() / 2;      //为了让用户感觉手在图片中间滑动
            if(left < 0){  //图片超出左边界,直接绘制到0位置
                left = 0;
            }else if(left >= mSwitchBackground.getWidth() - mSlideButton.getWidth()){  //图片超出右边界,直接绘制到右边
                left = mSwitchBackground.getWidth() - mSlideButton.getWidth();
            }
            canvas.drawBitmap(mSlideButton, left, 0, null);
        }

    }

接下来就是处理自己的业务逻辑
在这里,我把代码全部放在这里了,这是MyButton .java

 public class MyButton extends View {

    private Bitmap mSwitchBackground;
    private Bitmap mSlideButton;
    private boolean currentToggleState;  //记录当前开关的状态
    private int currentX;
    private boolean isTouching  = false; //记录当前控件是否处于触摸中
    private MyButtonOnStateChangedListener listener;

    private String namespace = "http://schemas.android.com/apk/res/com.abc.togglestate";

    /**
     * 在代码中创建控件调用
     * @param context,
     */
    public MyButton(Context context) {
        super(context);

    }

    /**
     * 控件使用在xml布局中使用
     * @param context
     * @param attrs
     */
    public MyButton(Context context, AttributeSet attrs) {
        super(context, attrs);
        //获取布局文件中的属性
        int backgroundRes = attrs.getAttributeResourceValue(namespace, "backgroundRes",0);
        setBackgroundRes(backgroundRes);

        int slideButtonRes = attrs.getAttributeResourceValue(namespace, "slideButtonRes", 0);
        setSlidButtonBackgroundRes(slideButtonRes);

        currentToggleState = attrs.getAttributeBooleanValue(namespace, "state", false);

    }

    /**
     * 使用样式时调用
     * @param context
     * @param attrs
     * @param defStyle
     */
    public MyButton(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);

    }

    /**
     * 测量自己的宽高
     */
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        //把背景图片的宽高作为控件的宽高
        setMeasuredDimension(mSwitchBackground.getWidth(), mSwitchBackground.getHeight());
    }

    /**
     * 绘制内容
     */
    @Override
    protected void onDraw(Canvas canvas) {

        super.onDraw(canvas);
        canvas.drawBitmap(mSwitchBackground, 0, 0, null);
        if(!isTouching){
            //根据当前状态滑动图片
            int left = 0;
            if(currentToggleState){ //如果是打开状态,左边距等于背景的宽度-滑块的宽度
                left = mSwitchBackground.getWidth() - mSlideButton.getWidth();
            }else {
                //关闭状态
                left = 0;
            }
            canvas.drawBitmap(mSlideButton, left, 0, null);
        }else{
            //根据手指触摸的位置,绘制滑动的图片
            int left = currentX - mSlideButton.getWidth() / 2;      //为了让用户感觉手在图片中间滑动
            if(left < 0){  //图片超出左边界,直接绘制到0位置
                left = 0;
            }else if(left >= mSwitchBackground.getWidth() - mSlideButton.getWidth()){  //图片超出右边界,直接绘制到右边
                left = mSwitchBackground.getWidth() - mSlideButton.getWidth();
            }
            canvas.drawBitmap(mSlideButton, left, 0, null);
        }

    }

    /**
     * 给控件设置背景图片
     * @param switchBackground
     */
    public void setBackgroundRes(int switchBackground) {
    mSwitchBackground = BitmapFactory.decodeResource(getResources(),switchBackground);
    }

    /**
     * 给控件设置滑块
     * @param slideButtonBackground
     */
    public void setSlidButtonBackgroundRes(int slideButtonBackground) {
        mSlideButton = BitmapFactory.decodeResource(getResources(), slideButtonBackground);
    }

    /**
     * 开关的状态(这里没有用到)
     * 用于外部调用
     * @param toggleState
     */
    public void isState(boolean toggleState) {
        currentToggleState = toggleState;
    }

    /**
     * 处理触摸事件
     * 触摸后,获取当前的触摸位置,根据位置,更新控件
     */
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            isTouching = true;
            currentX = (int) event.getX();
            break;

        case MotionEvent.ACTION_MOVE:
            currentX = (int) event.getX();
            break;
        case MotionEvent.ACTION_UP:
            isTouching = false;
            currentX = (int) event.getX();

            //手抬起后,更改当前控件的状态,根据当前手触摸的 位置和背景图片的中间值进行比较
            boolean tempState = currentX >= mSwitchBackground.getWidth() / 2;
            //6.3、判断当前的状态是否发生变化
            if(tempState != currentToggleState){
                currentToggleState = tempState;
                if(listener != null){
                    listener.OnStateChanged(currentToggleState);
                }
            }
            break;
        }
        invalidate();  //重新绘制控件,自动触发onDraw(在主线程中绘制控件)
        //postInvalidate();  //重新绘制控件,自动触发onDraw(在子线程中绘制控件)
        return true;  //自己消费事件
    }

    //6.1、对外提供开关监听
    public interface MyButtonOnStateChangedListener{
        public void OnStateChanged(boolean state);
    }

    //6.2、让外界把监听器传进来
    public void setMyButtonOnStateChangedListener(MyButtonOnStateChangedListener listener){
        this.listener = listener;
    }
}

MainActivity.java代码如下

public class MainActivity extends Activity {

    private MyButton mybutton;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mybutton = (MyButton) findViewById(R.id.mybutton);
        mybutton.setMyButtonOnStateChangedListener(new MyButtonOnStateChangedListener() {

            @Override
            public void OnStateChanged(boolean state) {
                //出来开关状态业务发生变化
                Toast.makeText(getApplicationContext(), "" + state, 0).show();
            }
        });
    }
}

希望能对看到这篇博客的小伙伴有所帮助,仅供大家参考

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值