Android自己定义控件系列二:自己定义开关button(一)「建议收藏」

Android自己定义控件系列二:自己定义开关button(一)

大家好,又见面了,我是全栈君。

这一次我们将会实现一个完整纯粹的自己定义控件,而不是像之前的组合控件一样。拿系统的控件来实现。计划分为三部分:自己定义控件的基本部分,自己定义控件的触摸事件的处理和自己定义控件的自己定义属性;

以下就開始第一部分的编写。本次以一个定义的开关button为例。以下就開始吧:


先看看效果,一个点击开关button。实现点击切换开关状态:

Android自己定义控件系列二:自己定义开关button(一)「建议收藏」


为了可以解说清晰,还是来一些主要的介绍。

首先须要明白的就是自己定义控件还是继承自View这个类,Google在View这个类里面提供了相当多的方法供我们使用,使用这些方法我们能够实现相当多的效果和功能。在这里须要用到几个基本的方法。


自己定义控件的步骤、用到的主要方法:


1、首先须要定义一个类。继承自View;对于继承View的类,会须要实现至少一个构造方法;实际上这里一共同拥有三个构造方法:


public View (Context context)是在java代码创建视图的时候被调用(使用new的方式),假设是从xml填充的视图,就不会调用这个


public View (Context context, AttributeSet attrs)这个是在xml创建可是没有指定style的时候被调用


public View (Context context, AttributeSet attrs, int defStyle)这个是在第二个基础上加入style的时候被调用的


所以对于这里来说,假设不使用style。 我们重点关注第二个构造方法就可以



2、对于不论什么一个控件来说。它须要显示在我们的界面上,那么肯定须要定义它的大小。

在这里Google提供了一个方法:protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec);我们去看这种方法的内部,实际上是调用了protected final void setMeasuredDimension(int measuredWidth, int measuredHeight);这种方法,当中第一个參数是view的宽。第二个參数是view的高,这样我们就能够设置view的宽高了,可是要注意,这样设置的单位都是像素



3、对于一个须要显示的控件来说。我们往往还须要确定它的位置:

这就要求重写onLayout方法。可是实际上这种方法在自己定义view的时候使用的不多,原因是由于对于位置来说,控件仅仅有建议权而没有决定权。决定权一般在父控件那里。


4、对于一个控件,须要显示,我们当然须要将它绘制出来,这里就须要重写onDraw方法,来将这个控件绘制出来



5、当控件状态改变的时候,我们非常可能须要刷新view的显示状态,这时候就须要调用invalidate()方法。这种方法实际上会又一次调用onDraw方法来重绘控件



6、在定义控件的过程中。假设须要对view设置点击事件。能够直接使用setOnClickListener方法。而不须要写view.setOnClickListener;



7、在布局文件里将这个自己定义控件定义出来,注意名字要使用全类名。并且,因为是继承自view控件,所以在xml文件里假设是view本身的属性都能够直接使用,比方:android:layout_width等等



这里比較关键的地方就在于这个onDraw方法,我们一起来看一下:

/**
	 * 画view的方法,绘制当前view的内容
	 */
	@Override
	protected void onDraw(Canvas canvas) {
		// super.onDraw(canvas);


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


		// 画背景
		canvas.drawBitmap(backgroundBitmap, 0, 0, paint);
		// 画滑块
		canvas.drawBitmap(slideButton, slideBtn_left, 0, paint);
	}

onDraw方法传入的參数是一个Canvas画布对象,这个实际上跟Java中的差不太多。我们要在画布上画画也须要一个画笔,我们这里也将其初始化出来Paint paint = new Paint(),同一时候设置了一个抗锯齿效果paint.setAntiAlias(true),然后调用drawBitmap的方法。先后绘制了开关的背景和开关的滑块。分别入下图:


Android自己定义控件系列二:自己定义开关button(一)「建议收藏」                          Android自己定义控件系列二:自己定义开关button(一)「建议收藏」


这里要注意的一点就是。drawBitmap(Bitmap bitmap, float left, float top, Paint paint)方法中间的两个float类型的參数。分别代表绘制图形的左上角的x和y的坐标(原点设置在左上角),所以这里假设我们个绘制坐标都传入0,0。那么开关会处在一个关的状态,这里,我们对于滑块使用了一个变量slideBtn_left来设置其位置,那么对于关闭状态。slideBtn_left的值就应该为0,对于开启状态。slideBtn_left的值就应该是backgroundBitmap(背景)的宽度减去slideButton(滑块)的宽度。


那么这样一来。机制就比較清楚了,我们仅仅须要在控件上设置一个点击事件,同一时候设置一个boolean变量代表开关的状态。当点击的时候。切换这个boolean类型的变量为true或者false。同一时候变化slideButton的值为0或者backgroundBitmap.getWidth()-slideButton.getWidth(),然后再调用invalidate()方法刷新控件,就能够实现主要的开关功能了


以下来看具体的代码,注解比較具体:


自己定义控件的类MyToggleButton.java。继承自View:

package com.example.togglebutton.ui;

import com.example.togglebutton.R;

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.View;

/*
 * 自己定义view的几个步骤:
 * 1、首先须要写一个类来继承自View
 * 2、须要得到view的对象。那么须要重写构造方法。当中一參的构造方法用于new。二參的构造方法用于xml布局文件使用,三參的构造方法能够传入一个样式
 * 3、须要设置view的大小,那么须要重写onMeasure方法
 * 4、须要设置view的位置。那么须要重写onLayout方法,可是这种方法在自己定义view的时候用的不多。原因主要在于view的位置主要是由父控件来决定
 * 5、须要绘制出所须要显示的view,那么须要重写onDraw方法
 * 6、当控件状态改变的时候。须要重绘view,那么调用invalidate();方法,这种方法实际上会又一次调用onDraw方法
 * 7、在这当中。假设须要对view设置点击事件。能够直接调用setOnClickListener方法
 */

public class MyToggleButton extends View {

	/**
	 * 开关按钮的背景
	 */
	private Bitmap backgroundBitmap;
	/**
	 * 开关按钮的滑动部分
	 */
	private Bitmap slideButton;
	/**
	 * 滑动按钮的左边界
	 */
	private float slideBtn_left;
	/**
	 * 当前开关的状态
	 */
	private boolean currentState = false;

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

	/**
	 * 在布局文件里声明的view,创建时由系统自己主动调用
	 * 
	 * @param context
	 * @param attrs
	 */
	public MyToggleButton(Context context, AttributeSet attrs) {
		super(context, attrs);
		initView();
	}

	/**
	 * 測量尺寸时的回调方法
	 */
	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		// super.onMeasure(widthMeasureSpec, heightMeasureSpec);
		// 设置当前view的大小 width:view的宽,单位都是像素值 heigth:view的高。单位都是像素值
		setMeasuredDimension(backgroundBitmap.getWidth(),
				backgroundBitmap.getHeight());
	}

	// 这种方法对于自己定义view的时候帮助不大,由于view的位置一般由父组件来决定的
	@Override
	protected void onLayout(boolean changed, int left, int top, int right,
			int bottom) {
		super.onLayout(changed, left, top, right, bottom);
	}

	/**
	 * 画view的方法,绘制当前view的内容
	 */
	@Override
	protected void onDraw(Canvas canvas) {
		// super.onDraw(canvas);

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

		// 画背景
		canvas.drawBitmap(backgroundBitmap, 0, 0, paint);
		// 画滑块
		canvas.drawBitmap(slideButton, slideBtn_left, 0, paint);
	}

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

		/*
		 * 点击事件
		 */
		setOnClickListener(new OnClickListener() {

			@Override
			public void onClick(View v) {

				currentState = !currentState;
				flushState();
				flushView();
			}
		});
	}

	/**
	 * 刷新视图
	 */
	protected void flushView() {
		// 刷新当前view会导致ondraw方法的运行
		invalidate();
	}

	/**
	 * 刷新当前的状态
	 */
	protected void flushState() {
		if (currentState) {
			slideBtn_left = backgroundBitmap.getWidth()
					- slideButton.getWidth();
		} else {
			slideBtn_left = 0;
		}
	}

}

在布局文件里将其定义出来:

<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"
    tools:context="${relativePackage}.${activityClass}" >


    <com.example.togglebutton.ui.MyToggleButton
        android:id="@+id/my_toggle_btn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true" />


</RelativeLayout>



在这里因为没有写不论什么点击触发业务的逻辑,仅仅是一个单纯的控件。所以在MainActivity里面没有增加多的代码:

package com.example.togglebutton;

import android.app.Activity;
import android.os.Bundle;

public class MainActivity extends Activity {

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

	}
}

至此一个自己定义的开关button就完毕了,后面两篇将会介绍怎样在上面实现 点击拖动开关的效果 和怎样实现自己定义属性。谢谢支持!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/115268.html原文链接:https://javaforall.net

赞 (0)
全栈程序员-站长的头像全栈程序员-站长

相关推荐

  • 阿里消息队列rocketmq_阿里云mqtt

    阿里消息队列rocketmq_阿里云mqtt最近需求需要实现收款语音的播报,如果不考虑费用问题,使用推送与百度AL开发平台的语音合成功能就可也实现收款语音的播报功能,但是考虑到费用问题,同时还考虑到ios与Android可以同步使用的问题(ps:ios如果使用推送需要把应用上架到苹果商店,需要有证书,而且还需要通过审核才能进行推送。但是目前ios的签名一般都不上架,而是进行企业签,使用第三方的分发进行软件的更新,所以一般都不考虑上架)。由此…

    2022年11月2日
  • 数据挖掘项目总结文档模板_数据挖掘实训心得

    数据挖掘项目总结文档模板_数据挖掘实训心得数据挖掘项目总结文档1、文档概述1.1编写目的记录本次实验思路及流程,备忘用。1.2适用对象个人学习总结,描述有偏差之处陆续更进。2、业务理解与分析定义2.1需求概述针对传统网络入侵检测系统存在的误判以及漏检情况,采用数据挖掘的指导思想,通过大量的攻击样本数据进行知识发现,本次实验采用的kdd99数据集,其中包含了大量的模拟攻击行为。1998年美国国防

    2022年9月5日
  • vim搜索中文_vim编辑器搜索字符串

    vim搜索中文_vim编辑器搜索字符串2019独角兽企业重金招聘Python工程师标准>>>…

    2022年9月23日
  • 卸载python会删除pip安装的包吗_pip更新软件包

    卸载python会删除pip安装的包吗_pip更新软件包

    2022年10月17日
  • VMWare安装Win10虚拟机详细教程

    VMWare安装Win10虚拟机详细教程1. 安装Win10之前,先检查VMware虚拟网卡是否正常打开控制面板,找到网络和Internet点击更改设备器配置,查看VMware虚拟网卡是否存在和已启用2. 还原默认设置(网卡正常跳过此步骤)点击VMware中的—编辑—虚拟网络编辑器点击左下角的—还原默认设置3. 虚拟网卡正常后,开始创建虚拟机4. 选择—自定义(高级)©5. 根据自己安装的VMware选择合适的—硬件兼容性6. 选择自己所需要的Windows系统镜像进行安装7.

    2022年6月26日
  • Android 官方文档:(一)动画和图像 —— 1.5 画布和画图

    Android 官方文档:(一)动画和图像 —— 1.5 画布和画图

    2021年11月13日

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号