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

来源:互联网 发布:java培训有哪些骗局 编辑:程序博客网 时间:2024/05/18 02:45

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

自定义的组合控件
继承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();            }        });    }}

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

2 0