【问题标题】:Handle custom view's different touch states处理自定义视图的不同触摸状态
【发布时间】:2014-03-21 11:35:56
【问题描述】:

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

  1. 列表项将正确显示其按下状态并返回未按下状态,但不会将点击发送到 ListView。
  2. 列表项将保持按下状态(并且无法退出),但会将点击发送到 ListView 非常好。

我以前从未做过这种类型的自定义视图,但非常感谢您的建议。我想我遗漏了一些简单的东西,但是我在这一点上花了太多时间来继续不问问题!

代码如下:

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;
    }
}

【问题讨论】:

  • 尝试使用 StateListDrawable 作为背景 Drawable?
  • @pskink 如果我的背景是图像而不是自定义形状,这会很好用(除非我遗漏了什么)。
  • 是的,你错过了 SLD 显示的不是位图而是 Drawables 的事实
  • @pskink 啊,我没有意识到您可以通过编程方式定义它们。我会试一试,虽然我不确定如何在没有 onDraw 的情况下创建每个可绘制对象。
  • 好问题!答案是 Drawable.draw

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


【解决方案1】:

解决方案是使用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);
        }
    }
}

【讨论】:

    【解决方案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;
        }
    }
    

    【讨论】:

    • 使用canvas.drawColor(color) 删除形状并用颜色填充矩形。我试过做canvas.drawPath(PATH, BRUSH),但它只适用于按下状态。
    • 我使用 drawColor 只是为了绘制任何可见的东西,你可以看到蓝色是默认状态(最后一个),红色是按下状态(第一个 if),如果出现问题,请参阅 logcat,因为 i Log。 d 视图状态
    • @kayton 你是对的:有一个错误,我在几分钟内就写好了,现在它应该可以工作了 :)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-09-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多