关闭

Android完全自定义控件并且实现监听事件

标签: android控件
279人阅读 评论(0) 收藏 举报
分类:

本篇文章带来Android的完全自定义控件。载体是自定义一个开关的控件,并且能够响应事件,首先我们先创一个项目,名字就叫ToggleView,修改MainActivity

public class MainActivity extends Activity {

    private ToggleView toggleView;

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

        toggleView = (ToggleView) findViewById(R.id.toggleView);
//        toggleView.setSwitchBackgroundResource(R.drawable.switch_background);
//        toggleView.setSlideButtonResource(R.drawable.slide_button);
//        toggleView.setSwitchState(true);
//        
        // 设置开关更新监听
        toggleView.setOnSwitchStateUpdateListener(new ToggleView.OnSwitchStateUpdateListener(){

            @Override
            public void onStateUpdate(boolean state) {
                Toast.makeText(getApplicationContext(), "state: " + state, Toast.LENGTH_SHORT).show();
            }

        });
    }

//  @Override
//  protected void onResume() {
//      super.onResume();
//  }
//    
}

写一个继承View的自定义控件的类ToggleView

/**
 * 自定义开关
 * @author poplar
 * 
 * Android 的界面绘制流程
 * 测量            摆放     绘制
 * measure  ->  layout  ->  draw
 *    |           |          |
 * onMeasure -> onLayout -> onDraw 重写这些方法, 实现自定义控件
 * 
 * onResume()之后执行
 * 
 * View
 * onMeasure() (在这个方法里指定自己的宽高) -> onDraw() (绘制自己的内容)
 * 
 * ViewGroup
 * onMeasure() (指定自己的宽高, 所有子View的宽高)-> onLayout() (摆放所有子View) -> onDraw() (绘制内容)
 */
public class ToggleView extends View {

    private Bitmap switchBackgroupBitmap; // 背景图片
    private Bitmap slideButtonBitmap; // 滑块图片
    private Paint paint; // 画笔
    private boolean mSwitchState = false; // 开关状态, 默认false
    private float currentX;

    /**
     * 用于代码创建控件
     * @param context
     */
    public ToggleView(Context context) {
        super(context);
        init();
    }

    /**
     * 用于在xml里使用, 可指定自定义属性
     * @param context
     * @param attrs
     */
    public ToggleView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
        // 获取配置的自定义属性
        String namespace = "http://schemas.android.com/apk/res-auto";
        int switchBackgroundResource = attrs.getAttributeResourceValue(namespace , "switch_background", -1);
        int slideButtonResource = attrs.getAttributeResourceValue(namespace , "slide_button", -1);

        mSwitchState = attrs.getAttributeBooleanValue(namespace, "switch_state", false);
        setSwitchBackgroundResource(switchBackgroundResource);
        setSlideButtonResource(slideButtonResource);
    }

    /**
     * 用于在xml里使用, 可指定自定义属性, 如果指定了样式, 则走此构造函数
     * @param context
     * @param attrs
     * @param defStyle
     */
    public ToggleView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init();
    }

    private void init() {
        paint = new Paint();
    }


    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        setMeasuredDimension(switchBackgroupBitmap.getWidth(), switchBackgroupBitmap.getHeight());
    }

    // Canvas 画布, 画板. 在上边绘制的内容都会显示到界面上.
    @Override
    protected void onDraw(Canvas canvas) {
        // 1. 绘制背景
        canvas.drawBitmap(switchBackgroupBitmap, 0, 0, paint);

        // 2. 绘制滑块

        if(isTouchMode){
            // 根据当前用户触摸到的位置画滑块

            // 让滑块向左移动自身一半大小的位置
            float newLeft = currentX - slideButtonBitmap.getWidth() / 2.0f;

            int maxLeft = switchBackgroupBitmap.getWidth() - slideButtonBitmap.getWidth();

            // 限定滑块范围
            if(newLeft < 0){
                newLeft = 0; // 左边范围
            }else if (newLeft > maxLeft) {
                newLeft = maxLeft; // 右边范围
            }

            canvas.drawBitmap(slideButtonBitmap, newLeft, 0, paint);
        }else {
            // 根据开关状态boolean, 直接设置图片位置
            if(mSwitchState){// 开
                int newLeft = switchBackgroupBitmap.getWidth() - slideButtonBitmap.getWidth();
                canvas.drawBitmap(slideButtonBitmap, newLeft, 0, paint);
            }else {// 关
                canvas.drawBitmap(slideButtonBitmap, 0, 0, paint);
            }
        }

    }

    boolean isTouchMode = false;
    private OnSwitchStateUpdateListener onSwitchStateUpdateListener;
    // 重写触摸事件, 响应用户的触摸.
    @Override
    public boolean onTouchEvent(MotionEvent event) {

        switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            isTouchMode = true;
            System.out.println("event: ACTION_DOWN: " + event.getX());
            currentX = event.getX();
            break;
        case MotionEvent.ACTION_MOVE:
            System.out.println("event: ACTION_MOVE: " + event.getX());
            currentX = event.getX();
            break;
        case MotionEvent.ACTION_UP:
            isTouchMode = false;
            System.out.println("event: ACTION_UP: " + event.getX());
            currentX = event.getX();

            float center = switchBackgroupBitmap.getWidth() / 2.0f;

            // 根据当前按下的位置, 和控件中心的位置进行比较. 
            boolean state = currentX > center;

            // 如果开关状态变化了, 通知界面. 里边开关状态更新了.
            if(state != mSwitchState && onSwitchStateUpdateListener != null){
                // 把最新的boolean, 状态传出去了
                onSwitchStateUpdateListener.onStateUpdate(state);
            }

            mSwitchState = state;
            break;

        default:
            break;
        }

        // 重绘界面
        invalidate(); // 会引发onDraw()被调用, 里边的变量会重新生效.界面会更新

        return true; // 消费了用户的触摸事件, 才可以收到其他的事件.
    }

    /**
     * 设置背景图
     * @param switchBackground
     */
    public void setSwitchBackgroundResource(int switchBackground) {
        switchBackgroupBitmap = BitmapFactory.decodeResource(getResources(), switchBackground);
    }

    /**
     * 设置滑块图片资源
     * @param slideButton
     */
    public void setSlideButtonResource(int slideButton) {
        slideButtonBitmap = BitmapFactory.decodeResource(getResources(), slideButton);
    }

    /**
     * 设置开关状态
     * @param
     */
    public void setSwitchState(boolean mSwitchState) {
        this.mSwitchState = mSwitchState;
    }

    public interface OnSwitchStateUpdateListener{
        // 状态回调, 把当前状态传出去
        void onStateUpdate(boolean state);
    }

    public void setOnSwitchStateUpdateListener(
            OnSwitchStateUpdateListener onSwitchStateUpdateListener) {
                this.onSwitchStateUpdateListener = onSwitchStateUpdateListener;
    }

}

修改activity_main.xml

<RelativeLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:my="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.mengxin.toggleview.ui.ToggleView
        android:id="@+id/toggleView"
        android:layout_centerInParent="true"
        my:switch_background="@drawable/switch_background"
        my:slide_button="@drawable/slide_button"
        my:switch_state="false"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

</RelativeLayout>

在value文件夹下面建一个attrs.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>

    <declare-styleable name="ToggleView">
        <attr name="switch_background" format="reference"/>
        <attr name="slide_button" format="reference" />
        <attr name="switch_state" format="boolean" />
    </declare-styleable>

</resources>

具体实现参考源码

0
0
查看评论
发表评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场

自定义android用户控件,使用回调函数实现自定义事件

在android软件设计中会用到好多的控件,但系统自带的控件有好多不能够达到需要实现的功能或是控件不够美观。那怎么办呢? android应为我们提供了好多的控件,我们可以继承某一控件,然...
  • gebitan505
  • gebitan505
  • 2014-01-14 15:21
  • 8436

android自定义日历并添加事件

前几天闲来无事,变想做一些小工具玩玩。花了一天多的时间,弄出一个简单日历的View。分为月份模式和星期模式。滚动查看,先上图看看: 上面的是显示的是月份的模式。下面是星期...
  • u011730649
  • u011730649
  • 2015-03-13 15:42
  • 3401

Android自定义View时添加自己的监听器

监听器在Java中非常常用,在自定义控件时可能根据自己的需要去监听一些数据的改变,这时就需要我们自己去写监听器,Java中的监听器实现上就是C++中的回调函数,在初始化时设置了这个函数,由某个事件触发...
  • deng0zhaotai
  • deng0zhaotai
  • 2014-03-18 15:07
  • 4435

Android 自定义View 环绕六边形控件及其TouchEvent事件的监听

六边形菜单自定义,点击六边形能执行不同的操作,
  • LJYBQ
  • LJYBQ
  • 2016-08-23 12:27
  • 1372

自定义AlterDialog,并给控件添加监听事件

  • 2015-07-30 21:16
  • 3.00MB
  • 下载

Android 仿照IOS的分段控件SegmentContro(自定义控件 + 事件监听 + 背景选择器)

还是在做项目中,遇到的一个界面问题
  • Danielntz
  • Danielntz
  • 2016-01-20 11:56
  • 1497

【Android游戏开发二十三】自定义ListView【通用】适配器并实现监听控件!

李华明Himi 原创,转载务必在明显处注明:转载自【黑米GameDev街区】 原文链接: http://www.himigame.com/android-game/374.html  ListView...
  • xiaominghimi
  • xiaominghimi
  • 2011-04-11 10:01
  • 22673

【Android游戏开发二十三】自定义ListView【通用】适配器并实现监听控件!

ListView :在Android应用开发过程中属于最常用的系统组件之一,当然可能童鞋们问为什么会突然游戏开发中讲这个,呵呵,其实在游戏开发中,也会常常使用到系统组件,比如游戏排行榜,简单的游戏关卡...
  • MYBOYER
  • MYBOYER
  • 2013-08-04 12:36
  • 448

【Android开发】自定义ListView,使用通用适配器,并实现ListView上的每一项和每一项上的按钮等控件同时监听

ListView在Android开发中是比较常用的系统组件,但是有时候我们除了需要做ListView上每一行的点击监听事件之外,如果每一行上还有其他需要监听的控件例如Button、CheckBox等,...
  • judyge
  • judyge
  • 2015-05-27 21:25
  • 747

自定义表格控件(通过TabLayout+TabRow)获取表格所有数据,并对表格进行相关事件监听

相关知识点:TabLayout布局与TabRow数据行的关系。   效果代码: 重点是:动态生成表格和动态表格的数据获取-------自己先做下笔记,以防以后的项目使用到自定义表格,朋友们可...
  • zhouzhiwengang
  • zhouzhiwengang
  • 2013-11-07 09:17
  • 1673