自定义View之MenuItemView

着手开发一款应用的时候,设置或者菜单页面是可能需要的,但是,那重复的布局会很令人苦恼。新手可能会一项项的重复绘制,有经验的你或许会用到include,或者用到组合控件。除了以上的方法之外,闲来无事,写了一个通用的View(MenuItemView)。此view暂时可以展示两种功能,一是通用的项,另一种是带开关的项,截图如下:

1.自定义属性:attrs.xml

	<declare-styleable name="MenuItemView" >
		<!--基本的参数设置-->
		<attr name="headerDrawable" format="reference"/>
		<attr name="drawPadding" format="dimension"/>
		<attr name="textHeader" format="string"/>
		<attr name="textHeaderColor" format="color"/>
		<attr name="textSize" format="dimension"/>
		<attr name="arrowDrawable" format="reference"/>
		<attr name="arrowSize" format="dimension"/>
		<attr name="arrowWropToSelf" format="boolean"/>
		<attr name="arrowColor" format="color"/>

		<attr name="Style" format="enum">
			<enum name="customStyle" value="0"/>
			<enum name="switchStyle" value="1"/>
		</attr>

		<!--当类型为switch时,绘制switch用到的参数-->
		<attr name="switchButtonWidth" format="dimension"/>
		<attr name="switchButtonHeight" format="dimension"/>
		<attr name="bordeWidth" format="dimension"/>
		<attr name="areaColor" format="color"/>
		<attr name="offColor" format="color"/>
		<attr name="onColor" format="color"/>
		<attr name="handlerColor" format="color"/>
		<attr name="animate" format="boolean"/>
		<attr name="defaultOn" format="boolean"/>

		<!--下分割线的绘制属性-->
		<attr name="dividerr" format="reference|color"/>
		<attr name="dividerWidth" format="dimension"/>
		<attr name="dividerVisibilty" format="boolean"/>

	</declare-styleable>

2.MenuItemView.java源码:

package com.dandy.widget;

import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.ShapeDrawable;
import android.graphics.drawable.StateListDrawable;
import android.graphics.drawable.shapes.RectShape;
import android.os.Build;
import android.os.Handler;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.TypedValue;
import android.view.MotionEvent;
import android.view.View;

import com.lingyun.switchbutton.R;

/**
 * 设置,菜单中的布局项
 * 默认控件是纵向居中显示,所有paddingTop和paddingBottom在这没有作用
 * Created by dandy on 2016/4/14.
 */
public class MenuItemView extends View{

    private static final String TAG = "MenuItemView";

    /**默认是按下状态的最小值**/
    private static final long PRESSED_TIMEOUT = 10;

    /**默认控件距离边界的大小**/
    private static final int PADDING_DEFAULT = 18;

    /**默认控件的高**/
    private static final int HEIGHT_DEFAULT = 50;

    /**文字绘制时默认大小**/
    private static final int TEXTSZIE_DEFAULT = 14;

    /**尾部箭头图标大小**/
    private static final int ARROW_SIZE = 13;

    /***SwitchButton默认宽*/
    private static final int SWITCHBUTTON_WIDTH = 50;

    /***SwitchButton默认高*/
    private static final int SWITCHBUTTON_HEIGHT = 28;

    /**头标**/
    private Drawable headerDrawable;

    /**头标宽**/
    private int headerDrawableWidth;
    /**头标高**/
    private int headerDrawableHeight;

    /**距离左边缘的距离**/
    private int paddingLeft;

    /**距离右边缘的距离**/
    private int paddingRight;

    /**绘制头标时,画布在Y轴的绘制偏移量**/
    private float headerDrawableStartDrawY;

    /**文字与图片间的距离**/
    private int drawablePadding = -1;

    /**头部文字提示**/
    private String textHeader;

    /**文字颜色**/
    private int textHeaderColor = Color.parseColor("#5a5a5a");

    /**文字大小**/
    private int textSize = -1;

    /**文字绘制时,画布在Y轴的绘制偏移量**/
    private float textStartDrawY;

    /**绘制文字的画笔**/
    private Paint textPaint;

    /** 尾部 > 图片**/
    private Drawable arrowDrawable;
    /**尾部 > 大小**/
    private int arrowSize = -1;

    /** > 绘制的X轴偏移量**/
    private float arrowStartDrawX;

    /** > 绘制的Y轴偏移量**/
    private float arrowStartDrawY;

    /**footerDrawable != null 时,绘制的时候是否按照原图片大小绘制**/
    private boolean arrowWropToSelf = true;

    /**尾部宽**/
    private int arrowDrawableWidth;
    /**尾部高**/
    private int arrowDrawableHeight;

    /**绘制arrow画笔**/
    private Paint arrowPaint;

    /**arrowPaint 颜色**/
    private int arrowColor = Color.parseColor("#5a5a5a");

    private DisplayMetrics dm;

    /*以下是绘制SwitchButton所用到的参数*/

    private Style style = Style.CUSTOM_ITEM;

    /**默认宽**/
    private  int switchButtonWidth = -1;

    /**默认高**/
    private int switchButtonHeight = -1;

    private static  final long DELAYDURATION = 10;

    /**开启颜色**/
    private int onColor = Color.parseColor("#4ebb7f");
    /**关闭颜色**/
    private int offColor = Color.parseColor("#dadbda");
    /**灰色带颜色**/
    private int areaColor = Color.parseColor("#dadbda");
    /**手柄颜色**/
    private int handlerColor = Color.parseColor("#ffffff");
    /**边框颜色**/
    private int borderColor = offColor;
    /**开关状态**/
    private boolean toggleOn = false;
    /**边框宽**/
    private int borderWidth = 2;
    /**纵轴中心**/
    private float centerY;
    /**按钮水平方向开始、结束的位置**/
    private float startX,endX;
    /**手柄x轴方向最小、最大值**/
    private float handlerMinX,handlerMaxX;
    /**手柄大小**/
    private int handlerSize;
    /**手柄在x轴的坐标位置**/
    private float handlerX;
    /**关闭时内部灰色带宽度**/
    private float areaWidth;
    /**是否使用动画效果**/
    private boolean animate = true;
    /**是否默认处于打开状态**/
    private boolean defaultOn = true;
    /**按钮半径**/
    private float radius;

    /**整个switchButton的区域**/
    private RectF switchRectF = new RectF();

    /**绘制switchButton的画笔**/
    private Paint switchPaint;

    private OnToggleChangedListener mListener;

    private Handler mHandler = new Handler();

    private double currentDelay;

    private float downX = 0;

    /**switchButton在X轴绘制的偏移量**/
    private float switchButtonDrawStartX;

    /**switchButton在Y轴绘制的偏移量**/
    private float switchButtonDrawStartY;

    /**分割线,默认在底部绘制**/
    private Drawable dividerr;

    /**分割线绘制的宽**/
    private int dividerWidth = 2;

    /**是否需要绘制分割线**/
    private boolean dividerVisibilty = true;

    /**触摸事件是否完成**/
    private boolean touchDownEnd = false;

    public MenuItemView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        setup(attrs);
    }
    public MenuItemView(Context context, AttributeSet attrs) {
        super(context, attrs);
        setup(attrs);
    }

    /**
     * 初始化控件,获取相关的控件属性
     * @param attrs
     */
    private void setup(AttributeSet attrs){

        dm = Resources.getSystem().getDisplayMetrics();

        TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.MenuItemView);
        if(typedArray != null){
            int count = typedArray.getIndexCount();
            for(int i = 0;i < count;i++){
                int attr = typedArray.getIndex(i);
                switch (attr){
                    case R.styleable.MenuItemView_headerDrawable:
                        headerDrawable = typedArray.getDrawable(attr);
                        break;
                    case R.styleable.MenuItemView_drawPadding:
                        drawablePadding = typedArray.getDimensionPixelSize(attr,drawablePadding);
                        break;
                    case R.styleable.MenuItemView_textHeader:
                        textHeader = typedArray.getString(attr);
                        break;
                    case R.styleable.MenuItemView_textHeaderColor:
                        textHeaderColor = typedArray.getColor(attr, textHeaderColor);
                        break;
                    case R.styleable.MenuItemView_textSize:
                        textSize = typedArray.getDimensionPixelSize(attr, textSize);
                        break;
                    case R.styleable.MenuItemView_arrowDrawable:
                        arrowDrawable = typedArray.getDrawable(attr);
                        break;
                    case R.styleable.MenuItemView_arrowSize:
                        arrowSize = typedArray.getDimensionPixelSize(attr, arrowSize);
                        break;
                    case R.styleable.MenuItemView_arrowWropToSelf:
                        arrowWropToSelf = typedArray.getBoolean(attr, true);
                        break;
                    case R.styleable.MenuItemView_arrowColor:
                        arrowColor = typedArray.getColor(attr, arrowColor);
                        break;
                    case R.styleable.MenuItemView_onColor:
                        onColor = typedArray.getColor(attr, onColor);
                        break;
                    case R.styleable.MenuItemView_offColor:
                        borderColor = offColor = typedArray.getColor(attr,offColor);
                        break;
                    case R.styleable.MenuItemView_areaColor:
                        areaColor = typedArray.getColor(attr, areaColor);
                        break;
                    case R.styleable.MenuItemView_handlerColor:
                        handlerColor = typedArray.getColor(attr, handlerColor);
                        break;
                    case R.styleable.MenuItemView_bordeWidth:
                        borderWidth = typedArray.getColor(attr, borderWidth);
                        break;
                    case R.styleable.MenuItemView_animate:
                        animate = typedArray.getBoolean(attr, animate);
                        break;
                    case R.styleable.MenuItemView_defaultOn:
                        defaultOn = typedArray.getBoolean(attr, defaultOn);
                        break;
                    case R.styleable.MenuItemView_Style:
                        style = Style.getValue(typedArray.getInt(attr, Style.CUSTOM_ITEM.ordinal()));
                        break;
                    case R.styleable.MenuItemView_switchButtonWidth:
                        switchButtonWidth = typedArray.getDimensionPixelOffset(attr, switchButtonWidth);
                        break;
                    case R.styleable.MenuItemView_switchButtonHeight:
                        switchButtonHeight = typedArray.getDimensionPixelOffset(attr, switchButtonHeight);
                        break;
                    case R.styleable.MenuItemView_dividerr:
                        dividerr = typedArray.getDrawable(attr);
                        break;
                    case R.styleable.MenuItemView_dividerWidth:
                        dividerWidth = typedArray.getDimensionPixelOffset(attr,dividerWidth);
                        break;
                    case R.styleable.MenuItemView_dividerVisibilty:
                        dividerVisibilty = typedArray.getBoolean(attr,dividerVisibilty);
                        break;
                }
            }
            typedArray.recycle();
        }

        if(drawablePadding  == -1){
            drawablePadding = applyDimension(PADDING_DEFAULT);
        }

        if(textSize == -1){
            textSize = applyDimension(TEXTSZIE_DEFAULT);
        }

        textPaint = new Paint();
        textPaint.setColor(textHeaderColor);
        textPaint.setTextSize(textSize);
        textPaint.setAntiAlias(true);

        if(style == Style.SWITCH_ITEM){

            if(switchButtonWidth == -1){
                switchButtonWidth  = applyDimension(SWITCHBUTTON_WIDTH);
            }

            if(switchButtonHeight == -1){
                switchButtonHeight = applyDimension(SWITCHBUTTON_HEIGHT);
            }

            switchPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
            switchPaint.setStyle(Paint.Style.FILL);
            switchPaint.setStrokeCap(Paint.Cap.ROUND);
            switchPaint.setAntiAlias(true);

            if(defaultOn){
                currentDelay = 1;
                toggleOn();
            }else{
                currentDelay = 0;
                toggleOff();
            }
        }else{
            if(arrowSize == -1){
                arrowSize = applyDimension(ARROW_SIZE);
            }
            arrowPaint = new Paint();
            arrowPaint.setColor(arrowColor);
            arrowPaint.setAntiAlias(true);
            arrowPaint.setStrokeWidth(3.0f);
        }

        if(getBackground() == null){
            if(style == Style.SWITCH_ITEM){
                setBackgroundColor(Color.parseColor("#ffffff"));
            }else{
                if(Build.VERSION.SDK_INT >=16){
                    setBackground(new StatePressedListDrawable());
                }else{
                    setBackgroundDrawable(new StatePressedListDrawable());
                }
            }
        }
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {

        if(headerDrawable == null){
            throw new NullPointerException("headerDrawable should be setted!");
        }

        headerDrawableWidth = headerDrawable.getIntrinsicWidth();
        headerDrawableHeight = headerDrawable.getIntrinsicHeight();
        Log.i(TAG,"[onLayout] headerDrawableWidth = "+headerDrawableWidth+",headerDrawableHeight = "+headerDrawableHeight);

        paddingLeft = getPaddingLeft();
        paddingLeft = paddingLeft > 0 ?paddingLeft:applyDimension(PADDING_DEFAULT);
        paddingRight = getPaddingRight();
        paddingRight = paddingRight > 0?paddingRight:applyDimension(PADDING_DEFAULT);
        Log.i(TAG, "[onLayout] paddingLeft = " + paddingLeft + ",paddingRight = " + paddingRight);

        headerDrawableStartDrawY = (getHeight() - headerDrawableHeight)/2.0f;
        Log.i(TAG, "[onLayout] headerDrawableStartDrawY = " + headerDrawableStartDrawY);

        textStartDrawY = (getHeight() - getTextHeight()) / 2.0f;
        Log.i(TAG, "[onLayout] textStartDrawY = " + textStartDrawY);

        if(style == Style.CUSTOM_ITEM){
            if(arrowDrawable != null && arrowWropToSelf){
                arrowDrawableWidth = arrowDrawable.getIntrinsicWidth();
                arrowDrawableHeight = arrowDrawable.getIntrinsicHeight();
            }else{
                arrowDrawableWidth = arrowDrawableHeight =  (int)arrowSize;
            }
            Log.i(TAG, "[onLayout] arrowDrawableWidth = " + arrowDrawableWidth + ",arrowDrawableHeight = " + arrowDrawableHeight);

            arrowStartDrawX = getWidth() - paddingRight - arrowDrawableWidth;
            arrowStartDrawY = (getHeight() - arrowDrawableHeight) / 2.0f;
            Log.i(TAG, "[onLayout] arrowStartDrawX = " + arrowStartDrawX + ",arrowStartDrawY = " + arrowStartDrawY);
        }else {
            radius = Math.min(switchButtonWidth,switchButtonHeight) * 0.5f;
            centerY = radius;
            startX = centerY;
            endX = switchButtonWidth - radius;
            handlerMinX = startX + borderWidth;
            handlerMaxX = endX - borderWidth;
            handlerSize = (int)switchButtonHeight - 4*borderWidth;
            handlerX = toggleOn?handlerMaxX:handlerMinX;
            areaWidth = 0;

            switchButtonDrawStartX = getWidth() - paddingRight - switchButtonWidth;
            switchButtonDrawStartY = (getHeight() - switchButtonHeight) / 2.0f;
            Log.i(TAG, "[onLayout] switchButtonDrawStartX = " + switchButtonDrawStartX + ",switchButtonDrawStartY = " + switchButtonDrawStartY);
        }

        super.onLayout(changed, left, top, right, bottom);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

        /**计算宽**/
        final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int width = MeasureSpec.getSize(widthMeasureSpec);
        if(widthMode == MeasureSpec.UNSPECIFIED || widthMode == MeasureSpec.AT_MOST){
            width = applyDimension(-1);
            widthMeasureSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY);
        }

        /**计算高**/
        final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int height = MeasureSpec.getSize(heightMeasureSpec);
        if(heightMode == MeasureSpec.UNSPECIFIED || heightMode == MeasureSpec.AT_MOST){
            height = applyDimension(HEIGHT_DEFAULT);
            heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
        }

        Log.i(TAG, "[onMeasure] width = " + width + ",height = " + height);

        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    @Override
    protected void onDraw(Canvas canvas) {

        Log.i(TAG, "[onDraw] start draw!");

        drawHeaderDrawable(canvas);

        drawHeaderText(canvas);

        if (style == Style.CUSTOM_ITEM){
            drawFooterDrawable(canvas);
        }else{
            drawSwitchButton(canvas);
        }

        drawDivider(canvas);

        super.onDraw(canvas);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()){
            case MotionEvent.ACTION_DOWN:
                touchDownEnd = false;
                if (style == Style.CUSTOM_ITEM){
                    mHandler.postDelayed(touchDownRunnable, PRESSED_TIMEOUT);
                }else{
                    downX = event.getX();
                }
                break;
            case MotionEvent.ACTION_MOVE:
                touchDownEnd = true;
                this.setPressed(false);
                break;
            case MotionEvent.ACTION_UP:
                touchDownEnd = true;
                if(style == Style.CUSTOM_ITEM){
                    this.setPressed(false);
                    performClick();
                }else if(downX >= switchButtonDrawStartX){//为了扩大switchButton的响应区域
                    toggle();
                }
                break;
            default:
                touchDownEnd = true;
                this.setPressed(false);
                break;
        }

        return true;
    }

    /**
     * 绘制头标
     * @param canvas
     */
    private void drawHeaderDrawable(Canvas canvas){
        if(headerDrawable == null){
            throw new NullPointerException("headerDrawable was setted null !");
        }
        canvas.save();
        canvas.translate(paddingLeft, headerDrawableStartDrawY);
        headerDrawable.setBounds(0, 0, headerDrawableWidth, headerDrawableHeight);
        headerDrawable.draw(canvas);
        canvas.restore();
    }

    /**
     * 绘制头部文字提示
     * @param canvas
     */
    private void drawHeaderText(Canvas canvas){
        canvas.save();
        canvas.translate(paddingLeft + headerDrawableWidth + drawablePadding, textStartDrawY);
        canvas.drawText(textHeader, 0, textStartDrawY, textPaint);
        canvas.restore();
    }

    /**
     * 绘制尾部指示图标
     *
     * @param canvas
     */
    private void drawFooterDrawable(Canvas canvas){
        canvas.save();
        canvas.translate(arrowStartDrawX, arrowStartDrawY);
        if(arrowDrawable != null){
            arrowDrawable.setBounds(0, 0, arrowDrawableWidth, arrowDrawableHeight);
            arrowDrawable.draw(canvas);
        }else{
            /**
             * 自行绘制arrow图标,绘制规则是:X轴坐标在arrow中间位置开始绘制,Y轴绘制不变
             */
            final float centerY = arrowDrawableHeight / 2.0f;
            final float lineStartX = arrowDrawableWidth / 2.0f;
            final float lineEndX = arrowDrawableWidth;
            canvas.drawLine(lineStartX,0,lineEndX,centerY,arrowPaint);
            canvas.drawLine(lineStartX,arrowDrawableHeight,lineEndX,centerY,arrowPaint);
        }
        canvas.restore();
    }

    /**
     * 绘制switchButton
     * @param canvas
     */
    private void drawSwitchButton(Canvas canvas){
        canvas.save();
        canvas.translate(switchButtonDrawStartX,switchButtonDrawStartY);
        /**绘制整个按钮**/
        switchRectF.set(0, 0, switchButtonWidth, switchButtonHeight);
        switchPaint.setColor(borderColor);
        canvas.drawRoundRect(switchRectF, radius, radius,switchPaint);

        /**绘制关闭灰色区域**/
        if(areaWidth > 0 ){
            final float cy = areaWidth * 0.5f;
            switchRectF.set(handlerX - cy,centerY - cy,endX + cy,centerY + cy);
            switchPaint.setColor(offColor);
            canvas.drawRoundRect(switchRectF,cy,cy,switchPaint);
        }

        /**绘制手柄**/
        final float handlerRadius = handlerSize * 0.5f;
        switchRectF.set(handlerX - handlerRadius, centerY - handlerRadius, handlerX + handlerRadius, centerY + handlerRadius);
        switchPaint.setColor(handlerColor);
        canvas.drawRoundRect(switchRectF, handlerRadius, handlerRadius, switchPaint);
        canvas.restore();
    }

    /**
     * 绘制分割线
     * @param canvas
     */
    private void drawDivider(Canvas canvas){
        if(dividerVisibilty){
            canvas.save();
            canvas.translate(paddingLeft, getHeight() - dividerWidth);
            if(dividerr == null){
                ShapeDrawable divider = new ShapeDrawable(new RectShape());
                divider.getPaint().setStrokeWidth(dividerWidth);
                divider.getPaint().setAntiAlias(true);
                divider.getPaint().setColor(Color.parseColor("#cccccc"));
                divider.setBounds(0, 0, getWidth(), dividerWidth);
                divider.draw(canvas);

            }else{
                dividerr.setBounds(0, 0, getWidth(),dividerWidth);
                dividerr.draw(canvas);
            }
            canvas.restore();
        }
    }

    /**
     * 开关状态切换
     */
    public void toggle(){
        toggle(animate);
    }
    /**
     * 开关状态切换
     * @param animate
     */
    public void toggle(boolean animate){
        toggleOn = !toggleOn;
        takeEffect(animate);
    }

    /**
     * 开启状态
     */
    public void toggleOn(){
        toggleOn(animate);
    }
    /**
     * 开启状态
     * @param animate
     */
    public void toggleOn(boolean animate){
        toggleOn = true;
        takeEffect(animate);
    }

    /**
     * 关闭状态
     */
    public void toggleOff(){
        toggleOff(animate);
    }
    /**
     * 关闭状态
     * @param animate
     */
    public void toggleOff(boolean animate){
        toggleOn = false;
        takeEffect(animate);
    }

    /**
     * 开始处理状态切换
     * @param animate
     */
    private void takeEffect(boolean animate){
        if(mListener != null){
            mListener.onToggle(toggleOn);
        }
        if(animate){
            mHandler.postDelayed(toggleRunnable, DELAYDURATION);
        }else {
            caculateEffect(toggleOn ? 1 : 0);
        }
    }

    /**
     * 时时计算
     * @param value
     */
    private void caculateEffect(double value){

        handlerX = (float)mapValueFromRangeToRange(value,0,1.0,handlerMinX,handlerMaxX);

        areaWidth = (float)mapValueFromRangeToRange(1.0-value,0,1.0,10,handlerSize);

        final int fb = Color.blue(onColor);
        final int fr = Color.red(onColor);
        final int fg = Color.green(onColor);

        final int tb = Color.blue(offColor);
        final int tr = Color.red(offColor);
        final int tg = Color.green(offColor);

        int sb = (int) mapValueFromRangeToRange(1.0 - value, 0, 1.0, fb, tb);
        int sr = (int) mapValueFromRangeToRange(1.0 - value, 0, 1.0, fr, tr);
        int sg = (int) mapValueFromRangeToRange(1.0 - value, 0, 1.0, fg, tg);

        sb = clamp(sb, 0, 255);
        sr = clamp(sr, 0, 255);
        sg = clamp(sg, 0, 255);

        borderColor = Color.rgb(sr, sg, sb);

        postInvalidate();
    }

    private int clamp(int value, int low, int high) {
        return Math.min(Math.max(value, low), high);
    }
    /**
     * Map a value within a given range to another range.
     * @param value the value to map
     * @param fromLow the low end of the range the value is within
     * @param fromHigh the high end of the range the value is within
     * @param toLow the low end of the range to map to
     * @param toHigh the high end of the range to map to
     * @return the mapped value
     */
    private  double mapValueFromRangeToRange(
            double value, double fromLow, double fromHigh,
            double toLow, double toHigh) {
        double fromRangeSize = fromHigh - fromLow;
        double toRangeSize = toHigh - toLow;
        double valueScale = (value - fromLow) / fromRangeSize;
        return toLow + (valueScale * toRangeSize);
    }

    private final Runnable toggleRunnable = new Runnable() {
        @Override
        public void run() {
            mHandler.removeCallbacks(toggleRunnable);
            if(toggleOn){
                if(currentDelay <= 1){
                    caculateEffect(currentDelay);
                    mHandler.postDelayed(toggleRunnable, DELAYDURATION);
                    currentDelay = currentDelay + 0.1;
                }else{
                    currentDelay = 1;
                }
            }else{
                if(currentDelay >= 0){
                    caculateEffect(currentDelay);
                    mHandler.postDelayed(toggleRunnable, DELAYDURATION);
                    currentDelay = currentDelay - 0.1;
                }else{
                    currentDelay = 0;
                }
            }
        }
    };

    private final Runnable touchDownRunnable = new Runnable() {
        @Override
        public void run() {
            MenuItemView.this.setPressed(true);
            MenuItemView.this.setPressed(touchDownEnd?false:true);
        }
    };

    /**
     * px2dp
     * @param value
     */
    private int applyDimension(float value){
       return (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,value,dm);
    }

    /**
     * 获取文字高度
     */
    private int getTextHeight(){
        Paint.FontMetrics fm = textPaint.getFontMetrics();
        return (int) Math.ceil(fm.descent - fm.top) + 2;
    }

    /**
     * 默认的点击效果
     */
    static class StatePressedListDrawable extends StateListDrawable{

        public StatePressedListDrawable(){
            addState(new int[]{android.R.attr.state_pressed},drawPressed());
            addState(new int[]{},drawNormal());
        }

        /**绘制正常情况下的背景**/
        private ShapeDrawable drawNormal(){
            ShapeDrawable normal = new ShapeDrawable(new RectShape());
            normal.getPaint().setColor(Color.parseColor("#ffffff"));
            return normal;
        }

        /**绘制正常情况下的背景**/
        private ShapeDrawable drawPressed(){
            ShapeDrawable pressed = new ShapeDrawable(new RectShape());
            pressed.getPaint().setColor(Color.parseColor("#eeeeee"));
            return pressed;
        }
    }

    /**
     * 类型
     */
    static enum Style{

        CUSTOM_ITEM, SWITCH_ITEM;

        public static Style getValue(int index){
            for(Style style:values()){
                if(style.ordinal() == index){
                    return style;
                }
            }
            return CUSTOM_ITEM;
        }
    }

    /**
     * 设置开关监听
     */
    public void setOnToggleChangedlistener(OnToggleChangedListener listener){
        this.mListener = listener;
    }
    /**
     * 开关状态监听
     */
    public interface OnToggleChangedListener{
        /**
         * 是否开启
         * @param on
         */
        void onToggle(boolean on);
    }
}

3.布局就不说了,和正常的View使用就是了。

时间: 2024-11-13 09:40:22

自定义View之MenuItemView的相关文章

自定义View 篇三 《手动打造ViewPage》

有了之前自定义View的理论基础,有了ViewPage.事件分发机制.滑动冲突.Scroller使用等相关知识的铺垫,今天纯手动打造一款ViewPage. 1.完成基本的显示: 在MainActivity中: public class MainActivity extends AppCompatActivity { private MyViewPage mViewPage; int[] imageIds = new int[]{ R.drawable.pic_0, R.drawable.pic_

九点(九宫格)式手势解锁自定义view

周末闲着没事,写了个手势解锁的view,实现起来也蛮快的,半天多一点时间就完事.把源码和资源贴出来,给大家分享,希望对大家有用. 效果,就跟手机上的九点手势解锁一样,上个图吧: 过程嘛感觉确实没啥好讲的了,涉及的知识以前的博客都说过了,无非就是canva,paint,touch事件这些,画画圆圈画画线条,剩下的就是细节处理逻辑了.都在代码里,所以这里就主要是贴资源吧. 这个自定义view就一个类,源码如下: package com.cc.library.view; import android.

iOS开发——笔记篇&amp;关于字典plist读取/字典转模型/自定义View/MVC/Xib的使用/MJExtension使用总结

关于字典plist读取/字典转模型/自定义View/MVC/Xib的使用/MJExtension使用总结 一:Plist读取 1 /******************************************************************************/ 2 一:简单plist读取 3 4 1:定义一个数组用来保存读取出来的plist数据 5 @property (nonatomic, strong) NSArray *shops; 6 7 2:使用懒加载的方

自定义View之实现日出日落太阳动效

以前也很羡慕网上大神随手写写就是一个很漂亮的自定义控件,所以我下决心也要学着去写,刚好最近复习了Android View的绘制流程知识,看来看去就是那些个知识点,没点产出总感觉很迷.现在个人呢用的是华为荣耀8手机,碰巧在看自带的天气APP时,滑到最下面看到那个动效图:日出时间和日落时间上边是一个半圆,白天任意的时刻(在日出和日落时间之间)都有对应一个太阳从日出时刻沿着半圆弧做动画特效,个人第一感觉就是:就拿这个来练练手啦!于是拿着笔和纸,画了模型图,甚至求什么sin.cos函数,有点过分了哈,还

Android 自定义View

最近在看鸿洋大神的博客,在看到自定义部分View部分时,想到之前案子中经常会要用到"图片 + 文字"这类控件,如下图所示: 之前的做法是在布局文件中,将一个Image & TextView组件放在LinearLayout/RelativeLayout中.今天就尝试了通过自定义View的方式来实现"图片 + 文字"组件. 首先在/res/value/目录下新建attrs.xml文件,在该文件中为CustomTextView定义以下几个attr.分别为 1.文字

Android自定义View探索(一)—生命周期

Activity代码: public class FiveActivity extends AppCompatActivity { private MyView myView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Log.e("log", "Activity生命周期:onCreate"); setConte

Android自定义view之仿支付宝芝麻信用仪表盘

自定义view练习 仿支付宝芝麻信用的仪表盘 对比图: 首先是自定义一些属性,可自己再添加,挺基础的,上代码 <?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable name="RoundIndicatorView"> <!--最大数值--> <attr name="maxNum" form

自定义view—折线图

学习导航 第一节:http://blog.csdn.net/bobo8945510/article/details/53197727 -自定义View-自定义属性及引用 第二节:http://blog.csdn.net/bobo8945510/article/details/53203233 自定义view02-图形绘制 第三节:http://blog.csdn.net/bobo8945510/article/details/53213938 自定义View-绘图基础之Path 第四节:http

Android笔记自定义View之制作表盘界面

前言 最近我跟自定义View杠上了,甚至说有点上瘾到走火入魔了.身为菜鸟的我自然要查阅大量的资料,学习大神们的代码,这不,前两天正好在郭神在微信公众号里推送一片自定义控件的文章--一步步实现精美的钟表界面.正适合我这种菜鸟来学习,闲着没事,我就差不多依葫芦画瓢也写了一个自定义表盘View,现在纯粹最为笔记记录下来.先展示下效果图: 下面进入正题 自定义表盘属性 老规矩,先在attrs文件里添加表盘自定义属性 <declare-styleable name="WatchView"&