Andrea
Andrea

Reputation: 214

placing custom edittext within drawview

I created a custom EditText loosely based on http://alinberce.wordpress.com/2012/02/20/android-edittext-with-custom-font-and-clear-button/ . My app's main xml is a RelativeLayout containing a bunch of control buttons and a custom drawview class which extends View. I was able to programatically add the EditText to the main xml when a button was clicked, and it worked there: I was able to resize it by dragging its corner, and drag it around the screen by pressing > 100ms.

Because it was possible to move the EditText over the control buttons, I felt the EditText's proper place was within the drawview. So I changed the drawview to a ViewGroup and added what I hoped was appropriate code to place the EditText there. I didn't make any changes to how the custom EditText operates (just enough to get it to appear in a Viewgroup rather than a View), but now it doesn't work as it did before. It shows up as a square rather than a rectangle, and it won't move or resize by touch.

I've been struggling with various LayoutParams and MarginLayoutParams, and editText.setMinimumHeight and .setMinimumWidth, but changing these does not seem to make any difference. Abstractly, I know I need to override onMeasure in the ViewGroup and measure the child. Abstractly, I know I need to override onLayout and do editText.layout, and it seems I should create a Rect to define its bounds, but since it should natively autoresize as text is entered, and resize when the corner is dragged, I don't know if the Rect is the right thing to do. What specific methods I should use? I don't have the experience to extrapolate from the api documentation & examples. Can someone point me in the right direction?

Edit:

The drawview is too long to post, and the overrides on onMeasure and onLayout are what I'm trying to get help with.

adding the editText to the drawview:

 private void addEditText() {
    final MovableEditText et = new MovableEditText(app);
 // I know this is terrifically messy
    et.setMinimumWidth(50);
    et.setMinimumHeight(30);
    et.setTextSize(app.getTextSize());
    et.setTextColor(app.getLineColor());
    et.setBackgroundColor(Color.parseColor("#77CCCCCC")); // some kind of light transparent gray
    et.setText(app.getText());
    et.setTag("TEXTENTRY");
    et.setTextCompletedListener(new TextCompletedListener() {
        @Override
        public void onTextCompleted() {
            removeEditText();
            drawView.invalidate();
        }
    });
    app.editText = et;
    // place the new editText sorta top center of the screen
 //        Rect clipBounds = drawView.getClipBounds();
    DrawView dv = (DrawView) findViewById(R.id.drawview);
    ViewGroup.MarginLayoutParams etParams = new ViewGroup.MarginLayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
            ViewGroup.LayoutParams.WRAP_CONTENT);
    dv.addView(et, etParams);
    et.requestFocus();
    dv.invalidate();
    InputMethodManager imm = (InputMethodManager) app.getSystemService(Context.INPUT_METHOD_SERVICE);
    if (imm != null) {
        imm.showSoftInput(et, 0);
    }
}

The edittext itself:

 public class MovableEditText extends EditText {

// 2 drawables, a close (cancel) and a resize handle
private Drawable closeImg = getResources().getDrawable(R.drawable.text_ok);
private Drawable resizeImg = getResources().getDrawable(R.drawable.text_resize);
private long timeFingerDown;
private InputMethodManager imm;
private App app;
private boolean resizing;
private TextCompletedListener textListener;

public MovableEditText(Context context) {
    super(context);
    app = (App) context.getApplicationContext();
    imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
    init();
}

public MovableEditText(Context context, AttributeSet attrs) {
    super(context, attrs);
    app = (App) context.getApplicationContext();
    imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
    init();
}

public MovableEditText(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
    app = (App) context.getApplicationContext();
    imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
    init();
}

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
}

public void setTextCompletedListener(TextCompletedListener l) {
    textListener = l;
}

private void init() {
    // Set bounds of the Clear button so it will look ok
    closeImg.setBounds(0, 0, closeImg.getIntrinsicWidth(), closeImg.getIntrinsicHeight());
    resizeImg.setBounds(0, 0, resizeImg.getIntrinsicWidth(), resizeImg.getIntrinsicHeight());
    // There may be initial text in the field, so we may need to display the  button
    showOkButton();
    this.setOnTouchListener(new OnTouchListener() {
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            MovableEditText et = MovableEditText.this;
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    timeFingerDown = event.getEventTime();
                    return true;

                case MotionEvent.ACTION_UP:
                    //if the Close image is displayed and the user remove his finger from the button, clear it. Otherwise do nothing
 //                  Log.i("napkinapp", "event x,y = "+event.getX()+", "+event.getY());
                    if (resizing) {
                        resizing = false;
                    }
                    // clicking on the checkbox
                    if (event.getY() < et.getPaddingTop() + closeImg.getIntrinsicHeight()) {
                        et.okButtonClick();
                        return false;
                    }
                    // otherwise, show the keyboard
                    if (imm != null) {
                        imm.showSoftInput(MovableEditText.this, 0);
                    }

                    return false;

                case MotionEvent.ACTION_MOVE:
                    if (event.getX() > et.getWidth() - et.getPaddingRight() - resizeImg.getIntrinsicWidth()) {
                        resizeBox(et, event);
                        // we touched the arrow, so we are resizing the box (i.e. increasing font size)
                        resizing = true;
                        return true;
                    }
                    if (event.getEventTime() - timeFingerDown > 200 && !resizing) {
                        // we are moving the box
                        moveBox(et, event);
                    }
                    return true;
            }
            return false;
        }
    });

    //if text changes, take care of the button
    this.addTextChangedListener(new TextWatcher() {
        @Override
        public void onTextChanged(CharSequence s, int start, int before, int count) {
            //
            MovableEditText.this.showOkButton();
        }

        @Override
        public void afterTextChanged(Editable arg0) {
        }

        @Override
        public void beforeTextChanged(CharSequence s, int start, int count, int after) {
        }
    });
}

private void moveBox(MovableEditText et, MotionEvent event) {
    ViewGroup.MarginLayoutParams etParams = (ViewGroup.MarginLayoutParams) this.getLayoutParams();
    etParams.leftMargin += (int) (event.getX() - (et.getWidth()/2));
    etParams.topMargin += (int) (event.getY() - this.getHeight()) ;
    et.setLayoutParams(etParams);
}

private void resizeBox(MovableEditText et, MotionEvent event) {
    et.setWidth((int) event.getX());
    et.setHeight((int) event.getY());
    resizeText(et);
}

private void resizeText(MovableEditText et) {
    int height = (int) ((et.getHeight() - closeImg.getIntrinsicHeight())*.25);
    et.setTextSize(height);
}

void showOkButton() {
    if (this.getText().toString().equals("")) {
        // remove the clear button
        this.setCompoundDrawables(this.getCompoundDrawables()[0],null, resizeImg, this.getCompoundDrawables()[3] );
    } else {
        //add clear button
        this.setCompoundDrawables(this.getCompoundDrawables()[0], closeImg, resizeImg, this.getCompoundDrawables()[3]);
    }
}

void okButtonClick() {
    // get the text, get the size, get the position, create a Stroke from it
    if (this.getText().toString().length()>0) {
        app.setText(this.getText().toString());
        app.setTextSize(this.getPaint().getTextSize()*.5f);
        ViewGroup.MarginLayoutParams etParams = (ViewGroup.MarginLayoutParams) this.getLayoutParams();
        int left = this.getLeft() + etParams.leftMargin;
        int bottom = this.getBottom() + etParams.topMargin + this.getHeight() - this.getPaddingBottom() ;
        app.strokeInProgress = new TextStrokeBuilder(app, left, bottom);
    }
    // make the keyboard go away
    imm.hideSoftInputFromWindow(this.getWindowToken(), 0);
    // tell app to delete the EditText
    textListener.onTextCompleted();
}
}

Upvotes: 0

Views: 558

Answers (1)

Andrea
Andrea

Reputation: 214

So I've found (more or less) a solution. We got rid of the MoveableEditText's MarginLayoutParameters which were so vexing, and instead gave it fields for leftMargin and topMargin, which we initialized with 100 and 100.

Code to add the edittext changed to:

private void addEditText() {
    MovableEditText et = new MovableEditText(app);
    et.setMinimumWidth(60);
    et.setTextSize(app.getTextSize());
    et.setTextColor(app.getLineColor());
    et.setBackgroundColor(Color.parseColor("#77CCCCCC")); // some kind of light transparent gray
    et.setText(app.getText());
    et.setTag("TEXTENTRY");
    et.setTextCompletedListener(new TextCompletedListener() {
        @Override
        public void onTextCompleted() {
            removeEditText();
            drawView.invalidate();
        }
    });
    app.editText = et;

    DrawView dv = (DrawView) findViewById(R.id.drawview);
    dv.addView(et);
    et.requestFocus();
    InputMethodManager imm = (InputMethodManager) app.getSystemService(Context.INPUT_METHOD_SERVICE);
    if (imm != null) {
        imm.showSoftInput(et, 0);
    }
}

Code to move the box in MoveableEditText changed to:

private void moveBox(MovableEditText et, MotionEvent event) {
    leftMargin += (int) (event.getX() - (et.getWidth()/2));
    topMargin += (int) (event.getY() - this.getHeight());
    requestLayout();
}

The onLayout and onMeasure ended up being very simple after that.

@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
    for (int i = 0; i < getChildCount(); i++) {
        final MovableEditText child = (MovableEditText) getChildAt(i);
        int left = l + child.leftMargin;
        int top = t + child.topMargin;
        child.layout(left, top, left + child.getMeasuredWidth(), top + child.getMeasuredHeight());
    }
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    int width = getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec);
    int height = getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec);
    setMeasuredDimension(width, height);

    for (int i = 0; i < getChildCount(); i++) {
        getChildAt(i).measure(width, height);
    }
}

Upvotes: 1

Related Questions