大凡在公司做客户端产品开发的都会发现,android和ios的差异化,ios得益于“老乔”的精心设计,界面用户体验做到了极致,而android秉承开源思想,界面用户体验百家各有其长,相互不得统一。不说废话,先上图,看看ios的“开关按钮”:
package com.example.slidebutton.view; import com.example.slidebutton.R; import com.example.slidebutton.view.interf.OnToggleStateChangeListener; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; public class SlideButton extends View { /** 滑动开关的背景 */ private Bitmap slideButtonBG; /** 滑动块的背景 */ private Bitmap switchBG; /** 设置开关的状态,打开/关闭。 默认:关闭 */ private boolean currentState = false; /** 当前滑动块的移动距离 */ private int currentX; /** 记录当前滑动块滑动的状态。默认,false */ private boolean isSliding = false; /** 开关状态改变监听 */ private OnToggleStateChangeListener mListener; public SlideButton(Context context, AttributeSet attrs) { super(context, attrs); initBitmap(); } private void initBitmap() { slideButtonBG = BitmapFactory.decodeResource(getResources(), R.drawable.slide_button_background); switchBG = BitmapFactory.decodeResource(getResources(), R.drawable.switch_background); } /** * 移动效果的处理 */ @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: // 手指按下 currentX = (int) event.getX(); isSliding = true; break; case MotionEvent.ACTION_MOVE: // 手指移动 currentX = (int) event.getX(); break; case MotionEvent.ACTION_UP: // 手指抬起 isSliding = false; // 判断当前滑动块,偏向于哪一边,如果滑动块的中心点<背景的中心点,设置为关闭状态 int bgCenter = switchBG.getWidth() / 2; boolean state = currentX >= bgCenter; // 改变后的状态 // 手指抬起时,回调监听,返回当前的开关状态 if (state != currentState && mListener != null) { mListener.onToggleStateChange(state); } currentState = state; break; default: break; } invalidate(); // 刷新控件,该方法会调用onDraw(Canvas canvas)方法 return true; // 自己处理事件,不让父类负责消耗事件 } /** * 测量当前控件宽高时回调 */ @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { // TODO Auto-generated method stub super.onMeasure(widthMeasureSpec, heightMeasureSpec); // 设置开关的宽和高 setMeasuredDimension(switchBG.getWidth(), switchBG.getHeight()); } /** * 绘制控件 */ @Override protected void onDraw(Canvas canvas) { // TODO Auto-generated method stub super.onDraw(canvas); // 1,滑动开关背景绘制到控件 canvas.drawBitmap(switchBG, 0, 0, null); // 2,绘制滑动块显示的位置,开启或关闭 if (isSliding) { int left = currentX - slideButtonBG.getWidth() / 2; // 处理手指触点,将触点从slidingButton的左边移动到中间 if (left < 0) { left = 0; } else if (left > switchBG.getWidth() - slideButtonBG.getWidth()) { left = switchBG.getWidth() - slideButtonBG.getWidth(); } canvas.drawBitmap(slideButtonBG, left, 0, null); } else { if (currentState) { // 绘制打开状态 canvas.drawBitmap(slideButtonBG, switchBG.getWidth() - slideButtonBG.getWidth(), 0, null); } else { // 绘制关闭状态 canvas.drawBitmap(slideButtonBG, 0, 0, null); } } } public void setToggleState(boolean b) { currentState = b; } /** * 对外设置监听方法 * * @param listener */ public void setOnToggleStateChangeListener(OnToggleStateChangeListener listener) { this.mListener = listener; } }
public SlideButton(Context context) { super(context); // TODO Auto-generated constructor stub }该构造用于该组件仅仅在代码中实例化,不能在布局文件中声明
public SlideButton(Context context, AttributeSet attrs) { super(context, attrs); // TODO Auto-generated constructor stub }声明该构造后,可以在布局文件中声明出这个控件,配置一些相关属性的。一般自定义组件都复写这个构造方法。
public SlideButton(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); // TODO Auto-generated constructor stub }这是View的第三个构造,可以在布局文件中声明之外,还需要制定组件的显示样式,用的较少。
package com.example.slidebutton.view.interf; /** * 开关状态改变监听事件 * * @author Administrator * */ public interface OnToggleStateChangeListener { /** * 当开关状态改变回调此方法 * * @param b * 当前开关的最新状态 */ void onToggleStateChange(boolean b); }关于监听的内部实现,请参考SlidingButton自定义组件代码。这里主要为外部引用该控件的界面提供了一个设置监听的方法setOnToggleStateChangeListener(OnToggleStateChangeListener listener),在判断每次手指抬起的时候,说明开关状态改变的过程结束,需要在那里为监听器设置回调参数,即当前的开关的状态boolean变量。这样,外部引用该控件的界面就可以拿到这个boolean值,做出相应的处理。那么,以下就是外部引用该自定义组件的主要代码:
<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" > <com.example.slidebutton.view.SlideButton android:id="@+id/slidebutton" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" /> </RelativeLayout> 主界面的代码MainActivity.java: [java] view plaincopyprint?在CODE上查看代码片派生到我的代码片 package com.example.slidebutton; import com.example.slidebutton.view.SlideButton; import com.example.slidebutton.view.interf.OnToggleStateChangeListener; import android.os.Bundle; import android.widget.Toast; import android.app.Activity; public class MainActivity extends Activity implements OnToggleStateChangeListener { public SlideButton slideButton; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); slideButton = (SlideButton) findViewById(R.id.slidebutton); slideButton.setToggleState(true); slideButton.setOnToggleStateChangeListener(this); } @Override public void onToggleStateChange(boolean b) { // TODO Auto-generated method stub if (b) { Toast.makeText(MainActivity.this, "开关打开", Toast.LENGTH_SHORT).show(); } else { Toast.makeText(MainActivity.this, "开关关闭", Toast.LENGTH_SHORT).show(); } } }
以上内容,有诸多不专业不正确或者疏忽的地方,欢迎大家批评指正,共同学习~
转载地址:http://blog.csdn.net/allen315410/article/details/39319057
源码下载地址:http://download.csdn.net/detail/itjavawfc/8433877