注:源自传智播客视频教程
现整理此案例,以供日后自己或大家学习、参考:
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>