操作步骤

1、属性设置,通过res/values/attrs.xml自定义属性值;
2、自定义java类绘制自定义的view,通过onMeasure()测量尺寸、onDraw()绘制图形和添加渲染;
3、在layout引用自定义的view.

具体实现(自定义switch)

添加自定义控件的简单熟悉

在res/values/下创建文件attrs.xml文件

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <attr name="turnOnText" format="string" />
    <attr name="turnOffText" format="string" />
    <attr name="turnOnColor" format="color" />
    <attr name="turnOffColor" format="color" />
    <attr name="textColor" format="color" />
    <attr name="turnOn" format="boolean" />
    <attr name="textSize" format="dimension" />
    <attr name="text" format="string" />
    <declare-styleable name="CustomerSwitch">
        <attr name="textColor" />
        <attr name="textSize" />
        <attr name="turnOnText" />
        <attr name="turnOffText" />
        <attr name="turnOnColor" />
        <attr name="turnOffColor" />
        <attr name="turnOn" />
    </declare-styleable>
</resources>

java代码实现部分

  • 通过java代码实现简单功能
    首先通过TypedArray获取到当前已被设置的属性值,实现如下:
TypedArray typedArray = context.obtainStyledAttributes(attrs,
                R.styleable.CustomerSwitch);
        int couter = typedArray.getIndexCount();
        for (int i = 0; i < couter; i++) {
            int attr = typedArray.getIndex(i);
            switch (attr) {
            case R.styleable.CustomerSwitch_turnOnText:
                turn_on_text = typedArray
                        .getString(R.styleable.CustomerSwitch_turnOnText);
                break;
            case R.styleable.CustomerSwitch_turnOffText:
                turn_off_text = typedArray
                        .getString(R.styleable.CustomerSwitch_turnOffColor);
                break;
            case R.styleable.CustomerSwitch_textColor:
                textColor = typedArray.getColor(
                        R.styleable.CustomerSwitch_textColor,
                        DEFAULT_TEXT_COLOR);
                break;
            case R.styleable.CustomerSwitch_textSize:
                textSize = typedArray.getDimension(
                        R.styleable.CustomerSwitch_textSize, DEFAULT_TEXT_SIZE);
                break;
            case R.styleable.CustomerSwitch_turnOffColor:
                turn_off_color = typedArray.getColor(
                        R.styleable.CustomerSwitch_turnOffColor,
                        DEFAULT_TURN_OFF_COLOR);
                break;
            case R.styleable.CustomerSwitch_turnOn:

                turn_on = typedArray.getBoolean(
                        R.styleable.CustomerSwitch_turnOn, DEFAULT_TURN_ON);
                break;
            case R.styleable.CustomerSwitch_turnOnColor:
                turn_on_color = typedArray.getColor(
                        R.styleable.CustomerSwitch_turnOnColor,
                        DEFAULT_TURN_ON_COLOR);
                break;
            }

        }
        typedArray.recycle();
  • 通过onMeasure()方法对当前view的尺寸进行测量并进行设置自定义的尺寸大小
    这里我将其拆分为两个方法,分别对其width和height进行设置,代码如下:
@Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        setMeasuredDimension(getMeasureWidth(widthMeasureSpec),
                getMeasureHeight(heightMeasureSpec));
    }

    // get the view width
    private int getMeasureWidth(int widthMeasureSpec) {
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int width;
        if (widthMode == MeasureSpec.EXACTLY) {
            width = widthSize;
            this.width = width;
        } else {
            width = this.width;
        }
        return width;
    }

    // get the view height
    private int getMeasureHeight(int heightMeasureSpec) {
        // 获取当前的view的height的填充模式
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        // 获取当前的view的height的填充模式下的尺寸大小
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
        int height;
        if (heightMode == MeasureSpec.EXACTLY) {
            height = heightSize;
            this.height = height;
        } else {
            height = this.height;
        }
        return height;
    }
  • 通过ondraw()方法绘制自定义view
    这里由于涉及到全局变量,放在后面统一展示,通过一个线程控制其切换动画,代码如下:

完整代码如下所示:

package com.yxtupgrade.customerview;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import com.yxtupgrade.R;
;
/**
* 
* @author hfcai
* @since 2015/12/8
* @version 1.0
* @serial customer view
* 
*/
public class CustomerSwitch extends View {
// declare variable
private static final String TAG = "CustomerSwitch";
private Context context;
private AttributeSet attrs;
private int def;
private TypedArray typedArray;
// private Paint mPaint;
private final int DEFAULT_TURN_ON_COLOR = 0x234561ff;
private final int DEFAULT_TURN_OFF_COLOR = 0x34343400;
private final boolean DEFAULT_TURN_ON = false;
private final String DEFAULT_TURN_ON_TEXT = "on";
private final String DEFAULT_TURN_OFF_TEXT = "off";
private final int DEFAULT_WIDTH = 90;
private final int DEFAULT_HEIGHT = 40;
private final float DEFAULT_PADDING = 6;
private final float DEFAULT_TEXT_SIZE = 20;
private final int DEFAULT_TEXT_COLOR = 0xff000000;
private final int SWITCH_TURN_OFF_STATUS = 0;
private final int SWITCH_TURN_ON_STATUS = 1;
private final int SWITCH_ANIMATION_STATUS = 2;
// declare customer Attribute
private String turn_on_text;
private String turn_off_text;
private int turn_on_color;
private int turn_off_color;
private boolean turn_on;
private float textSize;
private int textColor;
private int width;
private int height;
private float padding_left;
private float padding_right;
private float padding_top;
private float padding_bottom;
private int switch_status;
private float animation_cx;
private float animation_cy;
private float left_cx = 0;
private float left_cy = 0;
private float right_cx = 0;
private float right_cy = 0;
// private int background_color;
public CustomerSwitch(Context context) {
this(context, null);
}
public CustomerSwitch(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public CustomerSwitch(Context context, AttributeSet attrs, int def) {
super(context, attrs, def);
//    Log.d(TAG, "CustomerSwitch.");
// Assignment for any variable.
this.context = context;
this.attrs = attrs;
this.def = def;
initView();
}
// init variables
private void initVariable() {
width = DEFAULT_WIDTH;
height = DEFAULT_HEIGHT;
padding_bottom = DEFAULT_PADDING;
padding_left = DEFAULT_PADDING;
padding_right = DEFAULT_PADDING;
padding_top = DEFAULT_PADDING;
turn_on_text = DEFAULT_TURN_ON_TEXT;
turn_off_text = DEFAULT_TURN_OFF_TEXT;
textSize = DEFAULT_TEXT_SIZE;
textColor = DEFAULT_TEXT_COLOR;
turn_off_color = DEFAULT_TURN_OFF_COLOR;
turn_on_color = DEFAULT_TURN_ON_COLOR;
turn_on = false;
}
// postInvalidate()更新界面,非UI线程中使用,invalidate()更新界面,UI线程中使用
private void initView() {
//    Log.d(TAG, "initView");
// init variables
initVariable();
// get customer setting
typedArray = context.obtainStyledAttributes(attrs,
R.styleable.CustomerSwitch);
int couter = typedArray.getIndexCount();
for (int i = 0; i < couter; i++) {
int attr = typedArray.getIndex(i);
switch (attr) {
case R.styleable.CustomerSwitch_turnOnText:
turn_on_text = typedArray
.getString(R.styleable.CustomerSwitch_turnOnText);
break;
case R.styleable.CustomerSwitch_turnOffText:
turn_off_text = typedArray
.getString(R.styleable.CustomerSwitch_turnOffColor);
break;
case R.styleable.CustomerSwitch_textColor:
textColor = typedArray.getColor(
R.styleable.CustomerSwitch_textColor,
DEFAULT_TEXT_COLOR);
break;
case R.styleable.CustomerSwitch_textSize:
textSize = typedArray.getDimension(
R.styleable.CustomerSwitch_textSize, DEFAULT_TEXT_SIZE);
break;
case R.styleable.CustomerSwitch_turnOffColor:
turn_off_color = typedArray.getColor(
R.styleable.CustomerSwitch_turnOffColor,
DEFAULT_TURN_OFF_COLOR);
break;
case R.styleable.CustomerSwitch_turnOn:
turn_on = typedArray.getBoolean(
R.styleable.CustomerSwitch_turnOn, DEFAULT_TURN_ON);
break;
case R.styleable.CustomerSwitch_turnOnColor:
turn_on_color = typedArray.getColor(
R.styleable.CustomerSwitch_turnOnColor,
DEFAULT_TURN_ON_COLOR);
break;
}
}
typedArray.recycle();
// switch_status=(turn_on ? 1:0);
}
@Override
public void setPadding(int left, int top, int right, int bottom) {
super.setPadding(left, top, right, bottom);
padding_bottom = bottom;
padding_left = left;
padding_right = right;
padding_top = top;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(getMeasureWidth(widthMeasureSpec),
getMeasureHeight(heightMeasureSpec));
}
// get the view width
private int getMeasureWidth(int widthMeasureSpec) {
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int width;
if (widthMode == MeasureSpec.EXACTLY) {
width = widthSize;
this.width = width;
} else {
width = this.width;
}
return width;
}
// get the view height
private int getMeasureHeight(int heightMeasureSpec) {
// 获取当前的view的height的填充模式
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
// 获取当前的view的height的填充模式下的尺寸大小
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int height;
if (heightMode == MeasureSpec.EXACTLY) {
height = heightSize;
this.height = height;
} else {
height = this.height;
}
return height;
}
// public void setTurnOn(boolean on) {
// turn_on = on;
// // switch_status=(turn_on ? 1:0);
// invalidate();
//
// }
@Override
protected void onDraw(Canvas canvas) {
switch_status = (switch_status == 2 ? SWITCH_ANIMATION_STATUS
: (turn_on ? SWITCH_TURN_ON_STATUS : SWITCH_TURN_OFF_STATUS));
// Log.d(TAG, "turn_on is :" + turn_on);
// Log.d(TAG, "switch_status is :" + switch_status);
float radius = (height - padding_top - padding_bottom) / 2;
Paint mPaint = new Paint();
left_cx = (left_cx == 0 ? padding_left + radius : left_cx);
left_cy = (left_cy == 0 ? (float) height - padding_bottom - radius
: left_cy);
right_cx = (right_cx == 0 ? (float) width - padding_right - radius
: right_cx);
right_cy = (right_cy == 0 ? (float) height - padding_bottom - radius
: right_cy);
// 设置画笔的填充方式,fill:内部,stroke:空心,fill_and_stroke:外部实线加内部
if (switch_status == SWITCH_TURN_OFF_STATUS) {
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
// 设置锯齿
mPaint.setAntiAlias(true);
// 设置画笔颜色
mPaint.setColor(turn_off_color);
// new RectF(left,top,right,bottom);
RectF rectF = new RectF(padding_top, padding_left, width
- padding_right, height - padding_bottom);
// canvas.drawRoundRect(),绘制圆角矩形
canvas.drawRoundRect(rectF, radius, radius, mPaint);
mPaint.setColor(textColor);
// 设置字体
mPaint.setTextSize(textSize);
Rect bounds = new Rect();
mPaint.setTextAlign(Paint.Align.CENTER);
// 获取字体的整体尺寸信息
mPaint.getTextBounds(turn_off_text, 0, turn_off_text.length(),
bounds);
// 写字
canvas.drawText(turn_off_text, ((float) width - padding_left
- padding_right - 3 * radius)
/ 2
+ 2
* radius
+ padding_left
, ((float) height - padding_bottom - padding_top) / 2
+ padding_top + ((float) bounds.height()) / 2, mPaint);
mPaint.setColor(Color.WHITE);
canvas.drawCircle(left_cx, left_cy, radius, mPaint);
animation_cx = 0;
animation_cy = 0;
} else if (switch_status == SWITCH_TURN_ON_STATUS) {
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
// 设置锯齿
mPaint.setAntiAlias(true);
// 设置画笔颜色
mPaint.setColor(turn_on_color);
// new RectF(left,top,right,bottom);
RectF rectF = new RectF(padding_top, padding_left, (float) width
- padding_right, (float) height - padding_bottom);
// canvas.drawRoundRect(),绘制圆角矩形
canvas.drawRoundRect(rectF, radius, radius, mPaint);
mPaint.setColor(textColor);
// 设置字体
mPaint.setTextSize(textSize);
Rect bounds = new Rect();
mPaint.setTextAlign(Paint.Align.CENTER);
// 获取字体的整体尺寸信息
mPaint.getTextBounds(turn_on_text, 0, turn_on_text.length(), bounds);
// 写字
canvas.drawText(turn_on_text, ((float) width - padding_left
- padding_right - 3 * radius)
/ 2 + radius + padding_left - ((float) bounds.width()) / 2,
((float) height - padding_bottom - padding_top) / 2
+ padding_top + ((float) bounds.height()) / 2,
mPaint);
mPaint.setColor(Color.WHITE);
// 绘制圆形
canvas.drawCircle(right_cx, right_cy, radius, mPaint);
animation_cx = 0;
animation_cy = 0;
} else if (switch_status == SWITCH_ANIMATION_STATUS) {
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
// 设置锯齿
mPaint.setAntiAlias(true);
// 设置画笔颜色
mPaint.setColor(turn_on_color);
// new RectF(left,top,right,bottom);
RectF rectF = new RectF(padding_top, padding_left, (float) width
- padding_right, (float) height - padding_bottom);
// canvas.drawRoundRect(),绘制圆角矩形
canvas.drawRoundRect(rectF, radius, radius, mPaint);
mPaint.setColor(Color.WHITE);
// 绘制圆形
if (turn_on) {
canvas.drawCircle(right_cx - animation_cx, right_cy - radius,
radius, mPaint);
} else if (!turn_on) {
canvas.drawCircle(left_cx + animation_cx, left_cy, radius,
mPaint);
}
}
}
// touch 时间触发之前调用
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
break;
case MotionEvent.ACTION_UP:
if (switch_status == SWITCH_TURN_OFF_STATUS) {
AnimationThread animationThread = new AnimationThread(left_cx,
left_cy, right_cx, right_cy, 30, 0,
true);
animationThread.start();
} else if (switch_status == SWITCH_TURN_ON_STATUS) {
AnimationThread animationThread = new AnimationThread(right_cx,
right_cy, left_cx, left_cy, 30, 0,
false);
animationThread.start();
}
break;
}
return super.dispatchTouchEvent(event);
}
class AnimationThread extends Thread {
private final int DIRECTION_VERTICAL = 1;
private final int DIRECTION_HORIZONTAL = 0;
private int duration;
private float cx;
private float cy;
// 0:horizontal;1:vertical
private int direction;
private float tx;
private float ty;
private boolean on;
public AnimationThread(float cx, float cy, float tx, float ty,
int duration, int direction, boolean on) {
this.duration = duration;
this.cx = cx;
this.cy = cy;
this.tx = tx;
this.ty = ty;
this.direction = direction;
this.on = on;
}
private float getAbsolutly(float num){
if (num<0) {
num=-num;
}
return num;
}
@Override
public void run() {
if (direction == DIRECTION_HORIZONTAL) {// 水平方向
float spac = getAbsolutly(tx - cx);
float item = spac / 10;
for (int i = 0; i < 9; i++) {
try {
Thread.sleep(duration);
} catch (InterruptedException e) {
e.printStackTrace();
}
animation_cx = i * item;
postInvalidate();
}
turn_on=on;
postInvalidate();
} else if (direction == DIRECTION_VERTICAL) {// 垂直方向
float spac = ty - cy;
float item = spac / 10;
for (int i = 0; i < 9; i++) {
try {
Thread.sleep(duration);
} catch (InterruptedException e) {
e.printStackTrace();
}
animation_cy = i * item;
postInvalidate();
}
turn_on=on;
postInvalidate();
}
}
}
// getter and setter methods
public String getTurnOnText() {
return turn_on_text;
}
public void setTurnOnText(String turn_on_text) {
this.turn_on_text = turn_on_text;
invalidate();
}
public String getTurnOffText() {
return turn_off_text;
}
public void setTurnOffText(String turn_off_text) {
this.turn_off_text = turn_off_text;
invalidate();
}
public int getTurnOnColor() {
return turn_on_color;
}
public void setTurnOnColor(int turn_on_color) {
this.turn_on_color = turn_on_color;
invalidate();
}
public int getTurnOffColor() {
return turn_off_color;
}
public void setTurnOffColor(int turn_off_color) {
this.turn_off_color = turn_off_color;
invalidate();
}
public boolean isTurnOn() {
return turn_on;
}
public void setTurnOn(boolean turn_on) {
this.turn_on = turn_on;
invalidate();
}
public float getTextSize() {
return textSize;
}
public void setTextSize(float textSize) {
this.textSize = textSize;
invalidate();
}
public int getTextColor() {
return textColor;
}
public void setTextColor(int textColor) {
this.textColor = textColor;
invalidate();
}
public int getSwitchStatus() {
return switch_status;
}
public void setSwitchStatus(int switch_status) {
this.switch_status = switch_status;
invalidate();
}
}
  • 在Layout中的使用如下:
    在代码LinearLayout元素中添加
xmlns:app="http://schemas.android.com/apk/res/com.yxtupgrade"

即可引入,同时我们自定义的属性也是以app:开头,其中“app”不固定,但当在该控件中使用自定义属性时开头引入需与其保持一致.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res/com.yxtupgrade"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<com.yxtupgrade.customerview.CustomerSwitch 
android:id="@+id/test"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:turnOn="true"
app:turnOffColor="#ff0000"
app:turnOnColor="#dddddd"
android:onClick="click"
/></LinearLayout>
  • 显示如下
anyShare分享到:
打赏一下,鼓励Ta创作更多好内容
微信
支付宝
微信二维码图片

微信扫描二维码打赏

支付宝二维码图片

支付宝扫描二维码打赏

avatar
  订阅  
提醒
普人特福的博客cnzz&51la for wordpress,cnzz for wordpress,51la for wordpress