Android自定义控件之滑动开关解析。包括开关的绘制、开关滑动实现。android自定义控件、android滑动控件、android自定义开关。
效果如图:
首先在布局activity_main.xml中放置该开关,宽高选择包裹内容,实际的大小和图片我们将在java代码中指定
以下为引用内容:<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="cn.hugo.android.slidetoggle.MainActivity" > <!--必须需要把自定义的控件的包名和类名写完整--> <cn.hugo.android.slidetoggle.widget.SlideToggle android:id="@+id/toggle" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" > </cn.hugo.android.slidetoggle.widget.SlideToggle> </RelativeLayout>
然后,在activity中设置该布局文件MainActivity.java
以下为引用内容:package cn.hugo.android.slidetoggle; import android.app.Activity; import android.os.Bundle; import cn.hugo.android.slidetoggle.widget.SlideToggle; public class MainActivity extends Activity { private SlideToggle toggle; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); toggle = (SlideToggle) findViewById(R.id.toggle); toggle.setToggleState(true); //设置开关状态为打开 } }
最后定义一个继承view的滑动开关控件,SlideToggle.java
以下为引用内容:package cn.hugo.android.slidetoggle.widget; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.util.AttributeSet; import android.view.View; import cn.hugo.android.slidetoggle.R; public class SlideToggle extends View { private Bitmap slideButton; private Bitmap switchButton; private boolean mState; // 开关状态 public SlideToggle(Context context, AttributeSet attrs) { this(context, attrs, 0); } public SlideToggle(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); slideButton = BitmapFactory.decodeResource(getResources(), R.drawable.slide_button_background); switchButton = BitmapFactory.decodeResource(getResources(), R.drawable.switch_background); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); // 把开关的背景画到画布上 canvas.drawBitmap(switchButton, 0, 0, null); // 根据开关状态把可移动的背景描绘在画布上 if (mState) { canvas.drawBitmap(slideButton, switchButton.getWidth() - slideButton.getWidth(), 0, null); } else { canvas.drawBitmap(slideButton, 0, 0, null); } } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); // 设置控件的宽和高,为背景图片的宽高 setMeasuredDimension(switchButton.getWidth(), switchButton.getHeight()); } /** * 设置开关状态 * @param b * true:开;false:关 */ public void setToggleState(boolean b) { this.mState = b; } }
Canvas.drawBitmap(Bitmap bitmap,float left, float top,Paint paint) 方法,是在该控件中绘制图画,其中bitmap是需要绘制的图,left和top是把该图画在距离该控件左边、上边的距离的位置。现在,安装该应用后,显 示如前面的效果图。
我们此时需要覆盖View的public boolean onTouchEvent(MotionEvent event);方法,对手指的按下、移动、移开操作进行处理。有些细节需要进行特殊处理,比如滑块滑出控件,开关状态的处理等。
以下为引用内容:package cn.hugo.android.slidetoggle.widget; 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; import cn.hugo.android.slidetoggle.R; public class SlideToggle extends View { private Bitmap slideButton; private Bitmap switchButton; private boolean mState; // 开关状态 private float mCurrentX; // 记录手指按下的位置 private boolean isSliding; // 是否正在滑动 public SlideToggle(Context context, AttributeSet attrs) { this(context, attrs, 0); } public SlideToggle(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); slideButton = BitmapFactory.decodeResource(getResources(), R.drawable.slide_button_background); switchButton = BitmapFactory.decodeResource(getResources(), R.drawable.switch_background); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); // 把开关的背景画到画布上 canvas.drawBitmap(switchButton, 0, 0, null); if (isSliding) { // 正在对开关进行滑动 float left = mCurrentX - slideButton.getWidth() / 2; // 使滑块处于手指的中间位置 // 避免滑块滑出控件范围 if (left < 0) { left = 0; } else if (left > switchButton.getWidth() - slideButton.getWidth()) { left = switchButton.getWidth() - slideButton.getWidth(); } canvas.drawBitmap(slideButton, left, 0, null); } else { // 根据开关状态把可移动的背景描绘在画布上 if (mState) { canvas.drawBitmap(slideButton, switchButton.getWidth() - slideButton.getWidth(), 0, null); } else { canvas.drawBitmap(slideButton, 0, 0, null); } } } @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: { mCurrentX = event.getX(); // 记录按下时相对控件的x轴位置,event.getRawX是相对整个屏幕的x位置 isSliding = true; break; } case MotionEvent.ACTION_UP: { isSliding = false; // 判断当前状态属于何种状态,把开关设置为相应状态 mState = mCurrentX > switchButton.getWidth() / 2; // 移开滑块的x位置大于开关背景一半,则视为打开状态 break; } case MotionEvent.ACTION_MOVE: { mCurrentX = event.getX(); break; } default: break; } invalidate(); // 使控件调用一次onDraw()方法 return true; // 消耗触摸事件,事件不再传递 } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); // 设置控件的宽和高,为背景图片的宽高 setMeasuredDimension(switchButton.getWidth(), switchButton.getHeight()); } /** * 设置开关状态 * @param b * true:开;false:关 */ public void setToggleState(boolean b) { this.mState = b; } }