android – 在iPhone中为EditText放大玻璃:是否可以在视图之外绘制?

我正在尝试制作FastSelectEditText,以便:

>可以通过长按选择文本并滑动手指.
>滑动和选择时,显示放大玻璃(如iphone),以便用户可以在手指下看到文字.

不幸的是我的设计存在问题:MagGlass只显示在我的FastSelectEditText中.当用户在顶行选择文本时,她看不到mag玻璃.
所以我必须使用这个工作:当它到达FastSelectEditText的顶部时,显示低于手指的mag玻璃.

我明白如果我使用Mag Glass的另一个视图,这不会有问题.但是为了保持代码简单,我认为最好将Mag Glass保留在FastSelectEditText中.

有没有办法在视图范围之外绘制一些东西?
或者我应该编写另一个视图(而不是自定义EditText中的一些代码)来实现Mag Glass?(并且可能将这些视图放在框架布局中?)

  

public class FastSelectEditText extends EditText implements OnLongClickListener {

/**
 * @param context
 */
public FastSelectEditText(Context context) {
    super(context);
    init();
}

/**
 * @param context
 * @param attrs
 */
public FastSelectEditText(Context context, AttributeSet attrs) {
    super(context, attrs);
    init();
}

/**
 * @param context
 * @param attrs
 * @param defStyle
 */
public FastSelectEditText(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
    init();
}

private MagGlass mMagGlass;
private float mScale;
private void init(){
    DisplayMetrics metrics = getResources().getDisplayMetrics();
    mScale = metrics.density;
    setGravity(Gravity.TOP);
    setOnLongClickListener(this);
    mMagGlass = new MagGlass();
}

private int getOffset(int x, int y){
    Layout layout = getLayout();
    int row = layout.getLineForVertical(getScrollY()+y-getPaddingTop());
    return layout.getOffsetForHorizontal(row, x-getPaddingLeft());
}

/**
 * the position/index when touch down.
 */
private int mDownOffset = 0;
private int mOldSelStart, mOldSelEnd;
/**
 * Did the user moved his finger after down event?
 */
private boolean mMoved = false;
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
    int x = (int) event.getX();
    int y = (int) event.getY();
    mMagGlass.setObjectCenter(x, y);
    boolean result;
    switch (event.getAction()){
    case MotionEvent.ACTION_DOWN:
        mOldSelStart = getSelectionStart();
        mOldSelEnd = getSelectionEnd();
        if (mOldSelStart != mOldSelEnd){
            startSlideAndSelect();
        }
        mDownOffset = getOffset(x, y);
        return super.dispatchTouchEvent(event);
    case MotionEvent.ACTION_MOVE:
        result = super.dispatchTouchEvent(event);
        int offset = getOffset(x, y);
        if (!mMoved && mDownOffset != offset){
            mMoved = true;
        }
        if (mSlideAndSelect){            
            if (mMoved){
                setSelection(mDownOffset, offset);
            }
            return true;
        }
        return result;
    case MotionEvent.ACTION_UP:
        boolean moved = mMoved;
        // reset mMoved
        mMoved = false;

        boolean longClicked = mLongClicked;
        mLongClicked = false;

        if (mSlideAndSelect && moved){
            event.setAction(MotionEvent.ACTION_CANCEL);
        }
        result = super.dispatchTouchEvent(event);
        if (mSlideAndSelect){
            mSlideAndSelect = false;
            int upOffset = getOffset(x, y);
            if (!moved && mDownOffset == upOffset && longClicked){
                setSelection(mOldSelStart, mOldSelEnd);
                showContextMenu();
            }else{
                setSelection(mDownOffset, upOffset);
            }
            return true;
        }
        return result;
    case MotionEvent.ACTION_CANCEL:
        mSlideAndSelect = false;
        // reset mMoved
        mMoved = false;
        mLongClicked = false;
        return super.dispatchTouchEvent(event);
    default:
        return super.dispatchTouchEvent(event);
    }
}

protected void startSlideAndSelect() {
    mSlideAndSelect = true;
    ViewParent parent = getParent();
    if (parent != null){
        parent.requestDisallowInterceptTouchEvent(true);
    }
}

private boolean mSlideAndSelect = false;
private boolean mLongClicked = false;

private Vibrator mVibrator = (Vibrator) getContext().getSystemService(Context.VIBRATOR_SERVICE);
@Override
public boolean onLongClick(View v) {
    if (!mMoved){
        startSlideAndSelect();
        mLongClicked = true;
        mVibrator.vibrate(30);
    }
    return true;
}

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    if (mSlideAndSelect){
        mMagGlass.draw(canvas);
    }
}

/**
 * Need a drawable.mag_glass to work.
 * 
 * @author lifurong
 *
 */
class MagGlass{
    private int mWidth, mHeight;
    private Bitmap mMagGlassBitmap;
    private int mX, mY;
    private final static int INSET = 10;

    public MagGlass(){
        mMagGlassBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.mag_glass);
        mWidth = mMagGlassBitmap.getWidth();
        mHeight = mMagGlassBitmap.getHeight();
    }
    public void setObjectCenter(int x, int y){
        mX = x;
        mY = y;
    }

    public void draw(Canvas canvas) {
        final float left = mX-mWidth/2.0f;
        final float top = mY-mHeight/2.0f;
        final float right = mX+mWidth/2.0f;
        final float bottom = mY+mHeight/2.0f;
        float vTrans = 80*mScale;
        int vTransSign;
        int[] location = new int[2];
        getLocationInWindow(location);
        int topEdge = location[1]-getPaddingTop()>0? 0:-location[1]+getPaddingTop();
        if (top-vTrans > topEdge){
            vTransSign = -1;
        }else{
            vTransSign = 1;
        }
        canvas.translate(0, vTrans*vTransSign);
        canvas.clipRect(left, top, right, bottom);
        canvas.drawBitmap(mMagGlassBitmap, left, top, null);
        canvas.clipRect(left+INSET, top+INSET, right-INSET, bottom-INSET);
        FastSelectEditText.super.onDraw(canvas);
    }
}

}

最佳答案 要在视图范围之外绘制,您需要将视图的父级clipChildren设置为false.

默认情况下,ViewGroup将clipChildrenset设置为true,这会导致子项在剪裁到其边界的画布上绘制.

点赞