android - 处理自定义 View 的不同触摸状态

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

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

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

我以前从未做过这种类型的自定义 View ,但非常感谢您的建议。我认为我错过了一些简单的东西,但我已经在这一点上花费了太多时间,无法在不问问题的情况下继续!

代码如下:

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作为背景。

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

关于android - 处理自定义 View 的不同触摸状态,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22557691/

相关文章:

android - 带有视频源的 HTML5 Canvas drawImage 不适用于 Android

java - 如何检查圆的边是否要绘制在手机之外

android - 用 Sax 解析这个页面

当列表开始滚动时,使用 CursorAdapter 的 Android ListView 会出现困惑

android - 如何通过代码触发滚动动画?

android - 按下后仅删除 fragment

android - Livedata无法正确更新 fragment 中的数据

android - 用户滚动时是否可以在分段 ListView 或线性布局中显示 "pile up"标题

android - 在两个 View 之间沿着手指画线(ImageViews)

java - Android - 连续绘制形状到随机位置