Magnify glass for EditText like in iphone: is it possible to draw outside of a view?
I am trying to make a FastSelectEditText, so that:
- Text can be selected by long click and slide the finger.
- 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?)
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 clipChildren
set to true
, which caused the children to draw on a canvas clipped to their bounds.
精彩评论