Reputation: 214
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
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