开发者

Magnify glass for EditText like in iphone: is it possible to draw outside of a view?

开发者 https://www.devze.com 2023-03-22 15:36 出处:网络
I am trying to make a FastSelectEditText, so that: Text can be selected by long click and slide the finger.

I am trying to make a FastSelectEditText, so that:

  1. Text can be selected by long click and slide the finger.
  2. When sliding and selecting, show a magnify glass(like iphone), so that user can see the text under her finger.

Unfortunately there is a problem with my design: The MagGlass shows only inside my FastSelectEditText. When user is selecting text in top lines, she can't see the mag glass.

So I have to use this work around: show the mag glass lower than the finger, when it r开发者_如何学Pythoneaches the top of the FastSelectEditText.

I understand if I use another view for Mag Glass, that won't be a problem. But to keep code simple, I think it's better to keep the Mag Glass inside the FastSelectEditText.

Is there a way to draw something outside the bound of a view?

Or should I write another view(instead of some code inside the customized EditText) to implement a Mag Glass?(And probably put these views inside a frame layout?)

Magnify glass for EditText like in iphone: is it possible to draw outside of a view?

Magnify glass for EditText like in iphone: is it possible to draw outside of a view?

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

}


To draw outside the bound of a view, you need to set the view's parent's clipChildren to false.

By default a ViewGroup has the clipChildrenset to true, which caused the children to draw on a canvas clipped to their bounds.

0

精彩评论

暂无评论...
验证码 换一张
取 消