«

自定义view_开关按钮

时间:2024-3-2 18:03     作者:韩俊     分类: Android



注:源自传智播客视频教程

现整理此案例,以供日后自己或大家学习、参考:


android自定义view实现的简单demo


实现效果:





1.点击按钮可以改变开关的状态

2.拖动按钮可以改变开关的状态


此为完整代码下载链接:

http://download.csdn.net/detail/wang725/8734937


1.新建一个实现View的MyToggleButton类

<pre name="code" class="java">package com.example.togglebtn;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;

public class MyToggleButton extends View implements OnClickListener {

    /**
     * 作为背景的图片
     */
    private Bitmap backgroudBitmap;

    /**
     * 可以滑动的图片
     */
    private Bitmap slideBtm;

    private Paint paint;

    /**
     * 滑动按钮的左边值
     */
    private float slideBtn_left;

    /**
     * @param context
     * @param attrs
     * @param defStyleAttr
     */
    public MyToggleButton(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        // TODO Auto-generated constructor stub
    }

    /**
     * 在布局文件中声明的view,创建时系统自动调用
     * @param context
     * @param attrs
     */
    public MyToggleButton(Context context, AttributeSet attrs) {
        super(context, attrs);
        // TODO Auto-generated constructor stub
    }

    /**
     * 在代码里面创建对象的时候使用此构造方法
     * @param context
     */
    public MyToggleButton(Context context) {
        super(context);
        // TODO Auto-generated constructor stub
    }

    /**
     * view对象显示在屏幕上 有几个重要步骤
     * 1、构造方法创建对象
     * 2、测量view的大小 onMeasure
     * 3、确定view的位置,view自身有一些建议权,决定权在父view手中 onLayout();
     * 4、绘制view的内容 onDraw(Canvas);
     */

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        initView();
        /**
         * 设置当前view的大小
         * width : 当前view的宽度(单位:像素)
         * height : 当前view的高度(单位:像素)
         */
        setMeasuredDimension(backgroudBitmap.getWidth(), backgroudBitmap.getHeight());
    }

    /**
     * 自定义view的时候 作用不大
     * 确定位置的时候调用此方法
     */
//  @Override
//  protected void onLayout(boolean changed, int left, int top, int right,
//          int bottom) {
//      super.onLayout(changed, left, top, right, bottom);
//  }

    /** 
     * 当前开关状态
     */
    private boolean currentState = false;

    /**
     * 绘制当前view的内容
     */
    @Override
    protected void onDraw(Canvas canvas) {
//      super.onDraw(canvas);
        // 绘制背景
        /**
         * backgroundBitmap 要绘制的图片
         * left 图片的左边界
         * top 图片的上边届
         * paint 绘制图片使用的画笔
         */
        canvas.drawBitmap(backgroudBitmap, 0, 0, paint);
        // 绘制可滑动的按钮
        canvas.drawBitmap(slideBtm, slideBtn_left, 0, paint);
    }

    /**
     * 初始化
     */
    private void initView() {
        backgroudBitmap  = BitmapFactory.decodeResource(getResources(), R.drawable.switch_background);
        slideBtm = BitmapFactory.decodeResource(getResources(), R.drawable.slide_button);

        // 画笔
        paint = new Paint();
        paint.setAntiAlias(true);// 打开抗锯齿

        // 添加点击事件监听
        setOnClickListener(this);
    }

    /**
     * 判断是否发生拖动 
     * 若拖动了,就不再响应onlick事件
     */
    private boolean isDrag = false;

    /**
     * 点击事件
     * onclick事件在view.onTouchEvent事件中被解析
     * 系统对onclick事件的解析 过于简陋 只要有down时间 和 up事件 系统即认为发生了click事件
     */
    @Override
    public void onClick(View v) {
        // 若果没有拖动才执行改变状态的动作
        if(!isDrag) {
            currentState = !currentState;
            flushState();
        }
    }

    /**
     * 刷新当前状态
     */
    private void flushState() {
        if(currentState) {
            slideBtn_left = backgroudBitmap.getWidth() -  slideBtm.getWidth();
        } else {
            slideBtn_left = 0;
        }
        /**
         * 刷新当前view 会导致onDraw方法的执行
         * Invalidate the whole view. If the view is visible,
         *  onDraw(android.graphics.Canvas) will be called at some point in the future. This must be called from a UI thread. 
         *  To call from a non-UI thread, call postInvalidate().
         */
        invalidate();
    }

    /**
     * down 事件时的x值
     */
    private int firstX;

    /**
     * touch 事件的上一个x的值
     */
    private int lastX;

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        super.onTouchEvent(event);
        switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            firstX = lastX = (int) event.getX();
            isDrag =false;
            break;

        case MotionEvent.ACTION_MOVE:
            // 判断是否发生拖动
            if(Math.abs(event.getX() - firstX) > 5) {
                isDrag = true;
            }

            // 计算手指在屏幕上移动的距离
            int dis = (int) (event.getX() - lastX);
            // 将本次的位置 设置给lastX
            lastX = (int) event.getX();
//          if(lastX >= backgroudBitmap.getWidth() - )

            // 根据手指移动的距离,改变图片slideBtn_left的位置
            slideBtn_left = slideBtn_left + dis;
            break;
        case MotionEvent.ACTION_UP:
            // 在发生拖动的情况下,根据最后的位置,判断当前开关的状态
            if(isDrag) {
                if(slideBtn_left > (backgroudBitmap.getWidth() - slideBtm.getWidth())/2) {
                    slideBtn_left = backgroudBitmap.getWidth() - slideBtm.getWidth();
                    currentState = true;
                } else {
                    slideBtn_left = 0;
                    currentState = false;
                }
            }
            break;
        default:
            break;
        }
        flushView();
        return true;
    }

    /**
     * 刷新当前view
     */
    private void flushView() {
        /**
         * 对slideBtn_left 的值进行判断,确保其在河里的位置  0 <= slideBtn_left <= maxLeft
         */
        int maxLeft = backgroudBitmap.getWidth() - slideBtm.getWidth();
        // 1 确保slideBtn_left 大于0
        slideBtn_left = (slideBtn_left>0)?slideBtn_left:0;
        // 2 确保slideBtn_left 小于 maxLeft
        slideBtn_left = (slideBtn_left<maxLeft)?slideBtn_left:maxLeft;
        // 刷新当前视图 导致 onDraw方法执行
        invalidate();
    }
}



2.activity_main.xml,在不居中引用自定义的view

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.example.togglebtn.MainActivity" >

    <com.example.togglebtn.MyToggleButton
        android:id="@+id/mytogglebtn"
        android:layout_centerInParent="true"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>

</RelativeLayout>

标签: android

热门推荐