在视图上拖动时选择叠加

Selection overlay when dragging over views

我正在尝试构建一个类似于日历议程视图的视图(用户可以通过拖动 select 的垂直小时列表),但我卡在了用户开始的部分按下屏幕并将手指拖过小时行至 select。当用户拖动 selection?

时,如何使代表 selection 的视图覆盖在代表小时数的视图上并 expand/shrink 它

我当前的界面由一个垂直方向的 LinearLayout 组成,其中包含空的 TextViews 作为占位符,代表一天中的几个小时。

我的屏幕是这样的:

每个绿色列是 LinearLayoutTextViews 代表插槽。我想让用户开始按下其中一个插槽并向下拖动以制作 selection,并且在拖动过程中有一个覆盖 shrinks/expands 以反映 selection.

我已经设法通过扩展 LinearLayout 并像这样连接到绘图机制来解决它:

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.support.v4.view.MotionEventCompat;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;

public class AvailabilityCalendarColumnLayout extends LinearLayout{

    private Drawable overlay;

    public AvailabilityCalendarColumnLayout(Context context) {
        super(context);
        setWillNotDraw(false);
    }

    public AvailabilityCalendarColumnLayout(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
        setWillNotDraw(false);
    }

    public AvailabilityCalendarColumnLayout(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        setWillNotDraw(false);
    }

    @Override
    protected void dispatchDraw(Canvas canvas) {
        super.dispatchDraw(canvas);
        if(overlay != null) {
            overlay.draw(canvas);
        }
    }

    public boolean startSelectionOverlay(View startingView) {
        if(startingView == null) {
            return false;
        }
        overlay = getResources().getDrawable(R.drawable.overlay);
        float[] l = new float[2];
        l[0] = startingView.getX();
        l[1] = startingView.getY();
        int w = startingView.getWidth();
        int h = startingView.getHeight();
        overlay.setBounds((int) l[0], (int) l[1], (int) l[0] + w, (int) l[1] + h);
        invalidate();
        return true;
    }

    private void extendSelectionOverlay(View endView) {
        if(endView == null) {
            return;
        }
        float[] l = new float[2];
        l[0] = endView.getX();
        l[1] = endView.getY();
        int w = endView.getWidth();
        int h = endView.getHeight();

        Rect r = overlay.getBounds();
        r.bottom = (int)l[1] + h;
        overlay.setBounds(r);
        invalidate();
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        return true;
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        final int action = MotionEventCompat.getActionMasked(ev);
        float[] pos = new float[2];
        pos[0] = ev.getX();
        pos[1] = ev.getY();

        if(action == MotionEvent.ACTION_DOWN && overlay == null) {
            View view = getChildViewUnderPosition(pos);
            if(view != null) {
                startSelectionOverlay(view);
            }
        }

        if(action == MotionEvent.ACTION_MOVE && overlay != null) {
            View view = getChildViewUnderPosition(pos);
            extendSelectionOverlay(view);
        }

        if(action == MotionEvent.ACTION_UP && overlay != null) {
            endSelectionOverlay();
            invalidate();
        }
        return true;
    }

    private View getChildViewUnderPosition(float[] pos) {
        int num = getChildCount();
        View v;
        for(int x = 0; x < num; x++) {
            v = getChildAt(x);
            if(v.getX() <= pos[0] && (v.getX() + v.getWidth()) >= pos[0] && v.getY() <= pos[1] && (v.getY() + v.getHeight()) >= pos[1] && !v.isSelected()) {
                return v;
            }
        }

        return null;
    }

    private void endSelectionOverlay() {
        overlay = null;
        invalidate();
    }
}