处理自定义视图的不同触摸状态

时间:2014-03-21 11:35:56

标签: android android-listview android-canvas android-custom-view

我有一个自定义视图,扩展RelativeLayout并将其用作ListView中的列表项。它基本上是一个左边有一个时髦角度的矩形。根据我在onTouchEvent中返回的内容,我最终可能处于两种可能的状态:

  1. 列表项将正确显示其按下状态并返回未按下状态,但不会将点击发送到ListView。
  2. 列表项将保持按下状态(并且无法退出)但是将点击完全发送到ListView。
  3. 我之前从未做过这种类型的自定义视图,但我真的很感激建议。我认为我错过了一些简单的事情,但是我现在花费了太多时间来继续这样做而没有提出问题!

    以下是代码:

    public class ListItemBackgroundView extends RelativeLayout {
        private final Path PATH = new Path();
        private final Paint BRUSH = new Paint(Paint.ANTI_ALIAS_FLAG);
        private static int mBackgroundColor = -1;
        private static int mPressedBackgroundColor = -1;
        private static int mAngleOffset = -1;
    
        private boolean isPressed;
    
        public ListItemBackgroundView(Context context) {
            super(context);
            setWillNotDraw(false);
        }
    
        public ListItemBackgroundView(Context context, AttributeSet attrs) {
            super(context, attrs);
            setWillNotDraw(false);
        }
    
        public ListItemBackgroundView(Context context, AttributeSet attrs, int defStyle) {
            super(context, attrs, defStyle);
            setWillNotDraw(false);
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
    
            final int w = canvas.getWidth();
            final int h = canvas.getHeight();
    
            if (mBackgroundColor == -1) {
                mBackgroundColor = getResources().getColor(R.color.color_1);
                mPressedBackgroundColor = getResources().getColor(R.color.color_2);
                mAngleOffset = getResources().getDimensionPixelSize(R.dimen.list_view_angle_offset);
            }
    
            PATH.moveTo(0, 0);
            PATH.lineTo(mAngleOffset, h * (0.35f));
            PATH.lineTo(0, h);
            PATH.lineTo(w, h);
            PATH.lineTo(w, 0);
            PATH.lineTo(0, 0);
            PATH.close();
    
            BRUSH.setStyle(Paint.Style.FILL);
            BRUSH.setColor(isPressed ? mPressedBackgroundColor : mBackgroundColor);
    
            canvas.drawPath(PATH, BRUSH);
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            boolean result = super.onTouchEvent(event);
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    isPressed = true;
                    result = false;
                    invalidate();
                    break;
                case MotionEvent.ACTION_UP:
                case MotionEvent.ACTION_CANCEL:
                    isPressed = false;
                    result = true;
                    invalidate();
                    break;
            }
            return result;
        }
    }
    

2 个答案:

答案 0 :(得分:2)

解决方案是使用StateListDrawable作为背景。

public class ListItemStateListDrawable extends StateListDrawable {
    private final int mBackgroundColor, mPressedBackgroundColor, mAngleOffset;
    private final Path PATH = new Path();
    private final Paint BRUSH_PRESSED = new Paint(Paint.ANTI_ALIAS_FLAG);
    private final Paint BRUSH = new Paint(Paint.ANTI_ALIAS_FLAG);
    private boolean isDrawn = false;
    private boolean isPressed = false;

    public ListItemStateListDrawable(int backgroundColor, int pressedBackgroundColor, int angleOffset) {
        this.mBackgroundColor = backgroundColor;
        this.mPressedBackgroundColor = pressedBackgroundColor;
        this.mAngleOffset = angleOffset;
    }

    @Override
    protected boolean onStateChange(int[] stateSet) {
        for(int st : stateSet){
            boolean pressed = st == android.R.attr.state_pressed;
            boolean not_pressed = st == android.R.attr.stateNotNeeded || st ==  -android.R.attr.state_pressed ||
                    st ==  android.R.attr.state_accelerated || st ==  android.R.attr.state_focused;
            if(pressed || not_pressed){
                isPressed = pressed;
                invalidateSelf();
                break;
            }
        }
        return super.onStateChange(stateSet);
    }

    @Override
    public void draw(Canvas canvas) {
        if(!isDrawn){
            final int w = canvas.getWidth();
            final int h = canvas.getHeight();

            PATH.moveTo(0, 0);
            PATH.lineTo(mAngleOffset, h * (0.35f));
            PATH.lineTo(0, h);
            PATH.lineTo(w, h);
            PATH.lineTo(w, 0);
            PATH.lineTo(0, 0);
            PATH.close();

            BRUSH.setStyle(Paint.Style.FILL);
            BRUSH.setColor(mBackgroundColor);
            BRUSH_PRESSED.setStyle(Paint.Style.FILL);
            BRUSH_PRESSED.setColor(mPressedBackgroundColor);

            canvas.drawPath(PATH, BRUSH);
            isDrawn = true;
        }else {
            canvas.drawPath(PATH, isPressed ? BRUSH_PRESSED : BRUSH);
        }
    }
}

答案 1 :(得分:2)

使用StateListDrawable并通过addState添加状态是一种很好的标准方法,但你可以通过以下方式缩短它:

class MyStateListDrawable extends StateListDrawable {
    @Override
    protected boolean onStateChange(int[] stateSet) {
        invalidateSelf();
        return super.onStateChange(stateSet);
    }
    @Override
    public void draw(Canvas canvas) {
        int[] state = getState();
        Log.d(TAG, "draw " + StateSet.dump(getState()));
        // remove states you dont want or add you do
        if (isA(android.R.attr.state_pressed)) {
            Log.d(TAG, "draw state_pressed");
            canvas.drawColor(0xffff0000);
        } else
        if (isA(android.R.attr.state_focused)) {
            Log.d(TAG, "draw state_focused");
            canvas.drawColor(0xffff0000);
        } else
        if (isA(android.R.attr.state_selected)) {
            Log.d(TAG, "draw state_selected");
            canvas.drawColor(0xffffff00);
        } else {
            Log.d(TAG, "draw default state");
            canvas.drawColor(0xff0000ff);
        }
    }
    private boolean isA(int i) {
        int[] state = getState();
        for (int st : state) {
            if (st == i) {
                return true;
            }
        }
        return false;
    }
}