【Android】自定義控制元件實現可滑動的開關(switch)
阿新 • • 發佈:2018-12-23
介紹
昨天晚上寫了一個Android的滑動開關, 即SlideSwitch。效果如下:
實現
實現的思路其實很簡單,監聽控制元件上的touch事件,並不斷重新整理,讓滑塊在手指的位置上繪出,達到滑塊跟著手指滑動的顯示效果。
先看一下程式碼:
SlideSwitch.java (7月3日有修改:在touch事件裡呼叫onStateChangedListener前增加判空)
package com.incell.view;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
public class SlideSwitch extends View{
private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); //抗鋸齒
boolean isOn = false;
float curX = 0;
float centerY; //y固定
float viewWidth;
float radius;
float lineStart; //直線段開始的位置(橫座標,即
float lineEnd; //直線段結束的位置(縱座標
float lineWidth;
final int SCALE = 4; // 控制元件長度為滑動的圓的半徑的倍數
OnStateChangedListener onStateChangedListener;
public SlideSwitch(Context context, AttributeSet attrs, int defStyle) {
super (context, attrs, defStyle);
}
public SlideSwitch(Context context, AttributeSet attrs) {
super(context, attrs);
}
public SlideSwitch(Context context) {
super(context);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
// TODO Auto-generated method stub
curX = event.getX();
if(event.getAction() == MotionEvent.ACTION_UP)
{
if(curX > viewWidth / 2)
{
curX = lineEnd;
if(false == isOn)
{
//只有狀態發生改變才呼叫回撥函式, 下同
if(null != onStateChangedListener)
{
onStateChangedListener.onStateChanged(true);
}
isOn = true;
}
}
else
{
curX = lineStart;
if(true == isOn)
{
if(null != onStateChangedListener)
{
onStateChangedListener.onStateChanged(false);
}
isOn = false;
}
}
}
/*通過重新整理呼叫onDraw*/
this.postInvalidate();
return true;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// TODO Auto-generated method stub
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
/*保持寬是高的SCALE / 2倍, 即圓的直徑*/
this.setMeasuredDimension(this.getMeasuredWidth(), this.getMeasuredWidth() * 2 / SCALE);
viewWidth = this.getMeasuredWidth();
radius = viewWidth / SCALE;
lineWidth = radius * 2f; //直線寬度等於滑塊直徑
curX = radius;
centerY = this.getMeasuredWidth() / SCALE; //centerY為高度的一半
lineStart = radius;
lineEnd = (SCALE - 1) * radius;
}
@Override
protected void onDraw(Canvas canvas) {
// TODO Auto-generated method stub
super.onDraw(canvas);
/*限制滑動範圍*/
curX = curX > lineEnd?lineEnd:curX;
curX = curX < lineStart?lineStart:curX;
/*劃線*/
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(lineWidth);
/*左邊部分的線,綠色*/
mPaint.setColor(Color.BLUE);
canvas.drawLine(lineStart, centerY, curX, centerY, mPaint);
/*右邊部分的線,灰色*/
mPaint.setColor(Color.GRAY);
canvas.drawLine(curX, centerY, lineEnd, centerY, mPaint);
/*畫圓*/
/*畫最左和最右的圓,直徑為直線段寬度, 即在直線段兩邊分別再加上一個半圓*/
mPaint.setStyle(Paint.Style.FILL);
mPaint.setColor(Color.GRAY);
canvas.drawCircle(lineEnd, centerY, lineWidth / 2, mPaint);
mPaint.setColor(Color.BLUE);
canvas.drawCircle(lineStart, centerY, lineWidth / 2, mPaint);
/*圓形滑塊*/
mPaint.setColor(Color.LTGRAY);
canvas.drawCircle(curX, centerY, radius , mPaint);
}
/*設定開關狀態改變監聽器*/
public void setOnStateChangedListener(OnStateChangedListener o)
{
this.onStateChangedListener = o;
}
/*內部介面,開關狀態改變監聽器*/
public interface OnStateChangedListener
{
public void onStateChanged(boolean state);
}
}
註釋應該很詳細了。主要有以下幾點。
1、重寫了onMeasure方法,使控制元件高度依賴於控制元件的寬度。這樣不論在佈局檔案中如何設定,總能保證控制元件的寬高比。
2、控制好滑塊的活動範圍
3、定義內部介面OnStateChangedListener,並在自定義控制元件裡定義了其物件以及從外部賦值的方法setOnStateChangedListener,以便對開關狀態更改事件進行監聽並呼叫回撥。
使用及Demo
在佈局檔案中新增該控制元件即可使用。Demo效果為動圖展示效果(demo裡顏色為綠色,動圖為藍色是因為綠色會導致擷取gif時出問題,臨時更改的)。
Demo中佈局檔案如下:
activity_main.xml:
<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.slideswitchexample.SlideSwitch
android:id="@+id/slide_switch"
android:layout_width="200dp"
android:layout_height="wrap_content"
android:layout_centerInParent="true"/>
</RelativeLayout>
Demo中Activity程式碼如下:
MainActivity.java
package com.example.slideswitchexample;
import com.example.slideswitchexample.SlideSwitch.OnStateChangedListener;
import android.app.Activity;
import android.os.Bundle;
import android.widget.Toast;
public class MainActivity extends Activity {
SlideSwitch sSwitch;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
sSwitch = (SlideSwitch) this.findViewById(R.id.slide_switch);
sSwitch.setOnStateChangedListener(new OnStateChangedListener(){
@Override
public void onStateChanged(boolean state) {
// TODO Auto-generated method stub
if(true == state)
{
Toast.makeText(MainActivity.this, "開關已開啟", 1000).show();
}
else
{
Toast.makeText(MainActivity.this, "開關已關閉", 1000).show();
}
}
});
}
}