1

I'd like to have a TextView display text, and when you click/longclick on it, a textbox should "show up" and allow editing of said text. When you're done editing (onkey enter i suppose) it should revert back to a textview with the updated text...

I'm wondering if it's feasable to implement such a widget or should I hack a workaround? Tips and suggestions are very welcome.

If you need further idea of what I mean, just go to your e.g. (windows) skype profile and see for yourself.

EDIT: Clarification: I'm specifically asking for a widget or such which is a textview until clicked on, then transforms to an edittext containing the same text; once done editing it transforms back to a textview representing the new changed text. Thats what i mean by "edittext on demand widget".

But I'm hoping to get something better than

public class Widget {
     TextView text;
     EditText edit;
     String   textToRepresent;
     //...
}
Shark
  • 5,787
  • 3
  • 21
  • 46

4 Answers4

4

You have a few different options here.

First you will have to register an onClick or onLongClick to the TextView that you want to make interactive. Just make sure that the user knows it's clickable

Then have your onClick function start a DialogFragment. I like to create show functions. Note that you can use the support libraries here to make your app backwards compatible.

private void showDialog() {
    MyDialogFragment dialog = new MyDialogFragment();
    dialog.show(getSupportFragmentManager(), "dialog");
}

The DialogFragment is pretty straight forward. In your onCreateView you'll inflate the View that you'll want to display to the user. You can alternatively wrap it with a simple AlertDialogBuilder if you don't want to go custom.

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle     savedInstanceState) {
    View view = inflater.inflate(R.layout.your_dialog_layout);

    mTitleEditText = (TextView) view.findViewById(R.id.title);
    mTitleEditText.setOnClickListener(this);

    return view;
}

After your findViewByIds set your onClickListeners. The last thing you have to take care of is getting data back into your original TextView. You can do this by creating a public method in your Activity that you can call from inside of your DialogFragment. Something like this

 @Override
    public void onClick(View v) {
        int clickedId = v.getId();

        if (clickedId == mDoneButton.getId()) {
            MyActivity activity = (MyActivity)getActivity();

            mTitle = mTitleEditText.getText().toString();
            activity.setText(mTitle);
            dismiss();
        }
    }

I would recommend using a DialogFragment because it will handle your life cycle nicely. However, another option would be to create a new Activity themed to be a dialog

<activity android:theme="@android:style/Theme.Dialog" />

Then you can startActivityForResult to display your dialog and then capture your results in onActivityResult

Frank Sposaro
  • 8,203
  • 4
  • 39
  • 63
  • This textview does not change into an editview, just calls a dialog when clicked on. There is alot of fields to fill in, many of which are optional - having that many dialogs is not an option. – Shark Aug 10 '12 at 09:06
  • Next time please define your question a little better than. "Show up" and "widget" is pretty open to interpretation. – Frank Sposaro Aug 13 '12 at 13:09
  • I don't agree, widget can be either a visual component or that stupid visual component running as a service in the background; textbox widget should make it pretty obvious - "showing up" simply refers to 'it' (the textbox) not being there at all times (ergo the "on demand" part). – Shark Aug 13 '12 at 17:17
  • the more and more i read my OP the more I realize that Dialogs are nowhere to be mentioned and that "showing up" is clearly to the textbox. – Shark Aug 13 '12 at 18:08
  • Android's LongPress is intended to popup a dialog. http://developer.android.com/design/patterns/selection.html "In previous versions of Android, the long press gesture was universally used to display contextual actions for a given data item in a contextual menu." Context Menu is a Dialog. – Frank Sposaro Aug 13 '12 at 18:23
  • Yea, when they stop breaking their SDKs and keeping docs/examples up to date (or at least stop referring to outdated/404 documents) I might start reading up on their suggestions. In any case, thanks. – Shark Aug 14 '12 at 11:19
2

Here is my solution. I just give you the basic one. Create a TextView in front of EditText and two Button OK,Cancel (You can change to ImageButton like Skype). Change the visiblity of two view. The code is so simple without comment. You can add some null checking according your logic.

public class CompoundTextView extends RelativeLayout implements OnClickListener {
private EditText edt;
private TextView txt;
RelativeLayout layout;

public SkypeTextView(Context context, AttributeSet attrs) {
    super(context, attrs);
    // TODO Auto-generated constructor stub
}

@Override
protected void onFinishInflate() {
    super.onFinishInflate();
    edt = (EditText) findViewById(R.id.edt);
    txt = (TextView) findViewById(R.id.txt_name);
    layout = (RelativeLayout) findViewById(R.id.layout);
    Button ok = (Button) findViewById(R.id.ok_btn);
    Button cancel = (Button) findViewById(R.id.cancel_btn);
    ok.setOnClickListener(this);
    cancel.setOnClickListener(this);
    txt.setOnClickListener(this);

}

public void onClick(View v) {
    // TODO Auto-generated method stub
    switch (v.getId()) {
    case R.id.ok_btn:
        String editString = edt.getText().toString();

        txt.setText(editString);
        layout.setVisibility(View.INVISIBLE);
        txt.setVisibility(View.VISIBLE);

        break;

    case R.id.cancel_btn:
        layout.setVisibility(View.INVISIBLE);
        txt.setVisibility(View.VISIBLE);
        break;
    case R.id.txt_name:
        txt.setVisibility(View.INVISIBLE);
        layout.setVisibility(View.VISIBLE);

        break;

    }

}

}

Create a XML skypetextview. You can customize font and background to make it's prettier.

<?xml version="1.0" encoding="utf-8"?>

<RelativeLayout
    android:layout_width="fill_parent"
    android:layout_height="fill_parent" >

    <TextView
        android:id="@+id/txt_name"
        android:layout_width="fill_parent"
        android:layout_height="100dp"
        android:textColor="#FFFFFF"
        android:textSize="14sp"
        android:background="#ff0000" />

    <RelativeLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:visibility="invisible"
        android:id="@+id/layout" >

        <EditText
            android:id="@+id/edt"
            android:layout_width="270dp"
            android:layout_height="100dp" />

        <Button
            android:id="@+id/ok_btn"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_toRightOf="@id/edt"
            android:text="OK" />

        <Button
            android:id="@+id/cancel_btn"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_below="@id/ok_btn"
            android:layout_toRightOf="@id/edt"
            android:text="Cancel" />
    </RelativeLayout>
</RelativeLayout>

add (or include) this view to the layout you want. Example :

public class TestActivity extends Activity  {   

SkypeTextView test;

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    LayoutInflater inflate = getLayoutInflater();
    test = (SkypeTextView ) inflate.inflate(R.layout.compound_text_view,
            null);
    setContentView(test);
}

PS: i forgot. You should add some underline format for your textview in order to make user notice it clickable

Trung Nguyen
  • 7,192
  • 2
  • 40
  • 82
  • This does not make a dialog. It's simply displaying a new view that you created. – Frank Sposaro Aug 09 '12 at 17:07
  • 1
    Did he mention dialog ? He just want a textview like some fields in Skype's profile. – Trung Nguyen Aug 09 '12 at 17:10
  • I didn't specify for dialog, but I do not want a button switching my textview and a editview. I want the textview to change into the edittext for edditing, vice-versa when done and both reflecting the same data. – Shark Aug 10 '12 at 09:04
  • But if nothing better gets offered, i'll accept this and reward you bounty. – Shark Aug 10 '12 at 09:10
  • I think it's transparent from user so changing visibily of views is not the problem. If you don't like this, You can create one widget containt edittext can change background(1 normal edittext background, 1 background for alike textview) `onClick` with 2 `button` (OK and Cancel). 1 `flag` represent `edit mode` or `view mode`. If mode is `view mode` set Edittext `android:inputType="none"`. – Trung Nguyen Aug 10 '12 at 09:40
  • I'll probably have to roll with something similar to this where one is invisible and the other one isn't... but it needs to be able to 'latch' onto things (or vice-versa) so i'm thinking about the rest of the methods for the class... – Shark Aug 10 '12 at 09:52
  • If you have more elegant solution, please post here so everyone can learn more things :) – Trung Nguyen Aug 10 '12 at 09:56
  • I will. I just have to finish some other things first as this is more of a cosmetic feature... – Shark Aug 10 '12 at 10:01
  • i updated the answer with the polished, debugged code. feel free to take a look. – Shark Aug 15 '12 at 09:32
0

Let a EditText change its background based on its state(Editable or Frozen). Set a background selector that does this.

Use this selector xml

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
    <item android:state_focused="true" android:drawable="@android:drawable/edit_text"/>
    <item android:drawable="@android:drawable/screen_background_light_transparent"/>
</selector>
Ronnie
  • 23,807
  • 8
  • 52
  • 93
0

Like I said on thursday... Yul was pretty close but not quite close. He did have a general same idea but (theoretically) rushed into code too early ;)

The TextBoxOnDemand code supplied below is production-ready. The idea is similar to what I wanted to avoid in the OP and what Yul suggested, but with optimal implementation (using a ViewSwitcher instead of a RelativeLayout for instance)

I gathered the resources needed for this in the following articles:

Creating custom view from xml

Declaring a custom android UI element using XML

Defining custom attrs

How to pass custom component parameters in java and xml

http://kevindion.com/2011/01/custom-xml-attributes-for-android-widgets/

and decided to post them here because the official Google "training" docs are useless and are either obsolete (deprecated) or do not cover what I needed. I hope you don't mind me claiming my own bounty, but this is the solution I wanted (and expected, ergo the bounty). I guess the code will have to do ;)

TextBoxOnDemand.java:

    package com.skype.widget;

import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.TypedArray;
import android.text.SpannableString;
import android.text.style.UnderlineSpan;
import android.text.util.Linkify;
import android.util.AttributeSet;
import android.util.Log;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnFocusChangeListener;
import android.view.View.OnHoverListener;
import android.view.View.OnLongClickListener;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.TextView.OnEditorActionListener;
import android.widget.ViewSwitcher;

import com.skype.ref.R;
import com.skype.ref.RemoteKeys;

public class TextBoxOnDemand extends ViewSwitcher implements OnClickListener, OnLongClickListener, OnFocusChangeListener, OnHoverListener,
        OnEditorActionListener
{
    public static final String  LOGTAG          = "TextBoxOnDemand";

    private View                btmGuard;
    private ImageButton         cancel, accept;
    private EditText            editor;
    private RelativeLayout      editorLayout;
    private TextView            face;
    private String              hint            = new String();
    private boolean             inEditMode      = false;                    //normally this is in textview mode
    private boolean             inputReady      = false;
    private String              ourData         = new String();
    private String              prefillData     = new String();
    private String              tag             = new String();         //usually tag is empty.
    private View                topGuard;
    private int                 autoLinkMask;// = Linkify.EMAIL_ADDRESSES;  //Linkify.ALL;
    private ColorStateList      textColor, hintColor = null;

    public TextBoxOnDemand(Context context)
    {
        super(context);
        build(context);
        setEditable(false); //init
    }

    public TextBoxOnDemand(Context context, AttributeSet attrs)
    {
        super(context, attrs);
        build(context);
        init(context, attrs);
        setEditable(false); //init
    }

    public String getPrefillData()
    {
        return prefillData;
    }

    public String getTag()
    {
        return tag;
    }

    public String getText()
    {
        Log.d(LOGTAG, "getText() returning '" + ourData + "'");
        return ourData;
    }

    public boolean hasPrefillData()
    {
        return prefillData.isEmpty();
    }

    public boolean isEditable()
    {
        Log.d(LOGTAG, "isEditable() returning " + inEditMode);
        return inEditMode;
    }

    @Override
    public void onClick(View v)
    {
        Log.d(LOGTAG, "onClick(" + v + ")");
        if (inEditMode)
        {
            if (v.equals(accept))
            {
                if (editor.getEditableText().length() == 0 || editor.getEditableText().length() > 5)
                    ourData = editor.getEditableText().toString();

                setEditable(false);
            } else if (v.equals(cancel))
            {
                setEditable(false);
            }
        }
    }

    @Override
    public boolean onEditorAction(TextView v, int actionId, KeyEvent event)
    {
        //      Log.d(LOGTAG, "onEditorAction(" + v + ", " + actionId + ", " + event + ") fired!");
        Log.d(LOGTAG, "onEditorAction() fired, inputReady = " + inputReady);
        if (editor.getEditableText().length() > 0 && editor.getEditableText().length() < (prefillData.length() + 2)) return true;   //the user needs to enter something


        if (inputReady && (event.getKeyCode() == RemoteKeys.ENTER.keycode() || event.getKeyCode() == KeyEvent.KEYCODE_ENTER))   //always is
        {
            if (editor.getEditableText().length() > prefillData.length() || editor.getEditableText().length() == 0)
                ourData = editor.getEditableText().toString();

            setEditable(false);
            return false;
        }

        if ((editor.getEditableText().toString().compareToIgnoreCase(ourData) == 0 || editor.getEditableText().toString()
                .compareToIgnoreCase(prefillData) == 0)
                && !inputReady) //means we didn't just keep on holding enter
            return true;
        else
            inputReady = true;

        return true;
    }

    @Override
    public void onFocusChange(View v, boolean hasFocus)
    {
        Log.d(LOGTAG, "onFocusChange(" + v + ", " + hasFocus + ")\tinEditMode = " + inEditMode);
        if (inEditMode)
        {
            if (hasFocus && (v.equals(topGuard) || v.equals(btmGuard)))
            {
                setEditable(false);
                requestFocus();
            }

            if (hasFocus && (v.equals(editor) || v.equals(accept) || v.equals(cancel)))
            {
                //do nothing, you should be able to browse freely here
                if (ourData.isEmpty() && editor.getEditableText().length() < prefillData.length())
                {
                    Log.d(LOGTAG, "adding prefill, before = " + editor.getEditableText());
                    editor.setText("");
                    editor.append(prefillData);
                    Log.d(LOGTAG, "now is = " + editor.getEditableText());
                }
            }
        } else
        {
            String text = (ourData.isEmpty()) ? hint : ourData;
            ColorStateList color;
            if (hintColor != null && ourData.isEmpty())
                color = hintColor;
            else
                color = textColor;
            face.setTextColor(color);
            if (hasFocus)
            {
                SpannableString ss = new SpannableString(text);
                ss.setSpan(new UnderlineSpan(), 0, text.length(), 0);
                face.setText(ss);
            } else
                face.setText(text);
        }
    }

    @Override
    public boolean onHover(View v, MotionEvent event)
    {
        //      Log.d(LOGTAG, "onHover()");
        String text = (ourData.isEmpty()) ? hint : ourData;
        ColorStateList color;
        if (hintColor != null && ourData.isEmpty())
            color = hintColor;
        else
            color = textColor;
        face.setTextColor(color);
        switch (event.getAction())
        {
            case MotionEvent.ACTION_HOVER_ENTER:
                SpannableString ss = new SpannableString(text);
                ss.setSpan(new UnderlineSpan(), 0, text.length(), 0);
                face.setText(ss);
                break;
            case MotionEvent.ACTION_HOVER_EXIT:
                face.setText(text);
                break;
        }
        return true;
    }

    @Override
    public boolean onLongClick(View v)
    {
        Log.d(LOGTAG, "onLongClick()\tinEditMode = " + inEditMode);
        if (!inEditMode) //implies that getDisplayedChild() == 0, meaning the textview
        {
            setEditable(true);
            return true;
        } else
            return false;
    }

    public void setEditable(boolean value)
    {
        Log.d(LOGTAG, "setEditable(" + value + ")");
        inEditMode = value;
        if (inEditMode)
        {
            //display the editorLayout
            face.setOnLongClickListener(null);
            face.setOnHoverListener(null);
            face.setOnFocusChangeListener(null);    //because of GC.
            face.setOnClickListener(null);
            face.setVisibility(View.GONE);
            setDisplayedChild(1);
            editorLayout.setVisibility(View.VISIBLE);
            editor.setOnFocusChangeListener(this);
            editor.setOnEditorActionListener(this);
            cancel.setOnClickListener(this);
            accept.setOnClickListener(this);
            accept.setOnFocusChangeListener(this);
            cancel.setOnFocusChangeListener(this);
        } else
        {
            editor.setOnFocusChangeListener(null);
            editor.setOnEditorActionListener(null);
            cancel.setOnClickListener(null);
            accept.setOnClickListener(null);
            accept.setOnFocusChangeListener(null);
            cancel.setOnFocusChangeListener(null);
            editorLayout.setVisibility(View.GONE);
            setDisplayedChild(0);
            face.setVisibility(View.VISIBLE);
            face.setOnLongClickListener(this);
            face.setOnHoverListener(this);
            face.setOnFocusChangeListener(this);
            face.setOnClickListener(this);
            face.setFocusable(true);
            face.setFocusableInTouchMode(true);
        }
        updateViews();
    }

    @Override
    public void setNextFocusDownId(int nextFocusDownId)
    {
        super.setNextFocusDownId(nextFocusDownId);
        face.setNextFocusDownId(nextFocusDownId);
        //      editor.setNextFocusDownId(nextFocusDownId);
        accept.setNextFocusDownId(nextFocusDownId);
        cancel.setNextFocusDownId(nextFocusDownId);
    }

    @Override
    public void setNextFocusForwardId(int nextFocusForwardId)
    {
        super.setNextFocusForwardId(nextFocusForwardId);
        face.setNextFocusForwardId(nextFocusForwardId);
        editor.setNextFocusForwardId(nextFocusForwardId);
    }

    @Override
    public void setNextFocusLeftId(int nextFocusLeftId)
    {
        super.setNextFocusLeftId(nextFocusLeftId);
        face.setNextFocusLeftId(nextFocusLeftId);
        editor.setNextFocusLeftId(nextFocusLeftId);
    }

    @Override
    public void setNextFocusRightId(int nextFocusRightId)
    {
        super.setNextFocusRightId(nextFocusRightId);
        face.setNextFocusRightId(nextFocusRightId);
        cancel.setNextFocusRightId(nextFocusRightId);
    }

    @Override
    public void setNextFocusUpId(int nextFocusUpId)
    {
        super.setNextFocusUpId(nextFocusUpId);
        face.setNextFocusUpId(nextFocusUpId);
        //      editor.setNextFocusUpId(nextFocusUpId);
        accept.setNextFocusUpId(nextFocusUpId);
        cancel.setNextFocusUpId(nextFocusUpId);
    }

    public void setPrefillData(String prefillData)
    {
        this.prefillData = new String(prefillData);
    }

    public String setTag()
    {
        return tag;
    }

    public void setText(String text)
    {
        Log.d(LOGTAG, "setText(" + text + ")");
        ourData = text;
        updateViews();
    }

    private void build(Context context)
    {
        Log.d(LOGTAG, "build()");
        addView(View.inflate(context, R.layout.textboxondemand, null));
        setFocusable(true);
        setFocusableInTouchMode(true);
        setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);
        setOnFocusChangeListener(this);
        setOnLongClickListener(this);

        face = (TextView) findViewById(R.id.TBOD_textview);
        editorLayout = (RelativeLayout) findViewById(R.id.TBOD_layout);
        editor = (EditText) findViewById(R.id.TBOD_edittext);
        accept = (ImageButton) findViewById(R.id.TBOD_accept);
        cancel = (ImageButton) findViewById(R.id.TBOD_cancel);
        topGuard = (View) findViewById(R.id.TBOD_top);
        btmGuard = (View) findViewById(R.id.TBOD_bottom);

        face.setFocusable(true);
        face.setFocusableInTouchMode(true);

        face.setOnLongClickListener(this);
        face.setOnHoverListener(this);
        face.setOnFocusChangeListener(this);
        face.setOnClickListener(this);

        editor.setOnFocusChangeListener(this);
        editor.setOnEditorActionListener(this);
        editor.setHint(hint);
        editor.setFocusable(true);
        editor.setFocusableInTouchMode(true);

        accept.setOnClickListener(this);
        accept.setOnFocusChangeListener(this);
        accept.setFocusable(true);
        cancel.setFocusable(true);
        cancel.setOnFocusChangeListener(this);
        cancel.setOnClickListener(this);

        topGuard.setFocusable(true);
        topGuard.setOnFocusChangeListener(this);
        btmGuard.setFocusable(true);
        btmGuard.setOnFocusChangeListener(this);

        editor.setNextFocusRightId(R.id.TBOD_accept);
        editor.setNextFocusDownId(R.id.TBOD_bottom);
        editor.setNextFocusUpId(R.id.TBOD_top);

        accept.setNextFocusLeftId(R.id.TBOD_edittext);
        accept.setNextFocusRightId(R.id.TBOD_cancel);
        cancel.setNextFocusLeftId(R.id.TBOD_accept);
    }

    private void init(Context context, AttributeSet attrs)
    {
        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.TextBoxOnDemand);
        //Use a
        Log.d(LOGTAG, "init()");
        if (a == null) Log.d(LOGTAG, "Did you include 'xmlns:app=\"http://schemas.android.com/apk/res-auto\"' in your root layout?");

        final int N = a.getIndexCount();
        for (int i = 0; i < N; ++i)
        {
            int attr = a.getIndex(i);
            switch (attr)
            {
                case R.styleable.TextBoxOnDemand_android_hint:
                    hint = new String(a.getString(attr));
                    editor.setHint(a.getString(attr));
                    break;
                case R.styleable.TextBoxOnDemand_android_text:
                    ourData = new String(a.getString(attr));
                    break;
                case R.styleable.TextBoxOnDemand_android_inputType:
                    int inputType = a.getInt(attr, -1);
                    if (inputType != -1) editor.setInputType(inputType);
                    break;
                case R.styleable.TextBoxOnDemand_android_textColor:
                    textColor = a.getColorStateList(attr);
                    face.setTextColor(textColor);
                    break;
                case R.styleable.TextBoxOnDemand_android_linksClickable:
                    face.setLinksClickable(a.getBoolean(attr, true));
                    break;
                case R.styleable.TextBoxOnDemand_android_textColorHint:
                    hintColor = a.getColorStateList(attr);
                    break;
                case R.styleable.TextBoxOnDemand_android_autoLink:
                    autoLinkMask = a.getInt(attr, 0);
                    face.setAutoLinkMask(autoLinkMask);
                    break;

                default:
                    Log.d(LOGTAG, "Skipping attribute " + attr);
            }
        }

        //Don't forget this
        a.recycle();
    }

    private void updateViews()
    {
        Log.d(LOGTAG, "updateViews()");
        //      if (getDisplayedChild() == 0)   //first child - textview
        if (!inEditMode)    //first child - textview
        {
            if (ourData.isEmpty())
            {
                if (hintColor != null) face.setTextColor(hintColor);
                face.setText(hint);
            } else
            {
                face.setTextColor(textColor);
                face.setText(ourData);
            }
            face.setFocusable(true);
            face.setFocusableInTouchMode(true);
            face.setAutoLinkMask(autoLinkMask);
        } else
        {   //second child - edittext
            editor.setFocusable(true);
            editor.setFocusableInTouchMode(true);
            if (ourData.startsWith(prefillData) || ourData.length() >= prefillData.length())
                editor.setText("");
            else
                editor.setText(prefillData);

            editor.append(ourData);
            inputReady = false;

            editor.requestFocus();
        }
    }

    public void setAutoLinkMask(LinkifyEnum linkifyEnumConstant)
    {
        switch (linkifyEnumConstant)
        {
            case ALL:
                autoLinkMask = Linkify.ALL;
                break;
            case EMAIL_ADDRESSES:
                autoLinkMask = Linkify.EMAIL_ADDRESSES;
                break;
            case MAP_ADDRESSES:
                autoLinkMask = Linkify.MAP_ADDRESSES;
                break;
            case PHONE_NUMBERS:
                autoLinkMask = Linkify.PHONE_NUMBERS;
                break;
            case WEB_URLS:
                autoLinkMask = Linkify.WEB_URLS;
                break;

            case NONE:
            default:
                autoLinkMask = 0;
                break;
        }
        //set it now
        face.setAutoLinkMask(autoLinkMask);
    }

    public enum LinkifyEnum
    {
        ALL, EMAIL_ADDRESSES, MAP_ADDRESSES, PHONE_NUMBERS, WEB_URLS, NONE
    };

}

I'm still working out some focus-related issues but this works as intended. When I use onFocuslistener 1, you can't focus from one TextBox to the other; when the textbox itself is focusable, I can focus from one to the other just fine, but I cannot inter-focus thru children and thus can't focus on the edittext to type.

the XML file:

    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content" >

    <TextView
        android:id="@+id/TBOD_textview"
        android:layout_width="wrap_content"
        android:layout_height="fill_parent"
        android:autoLink="email"
        android:focusable="true"
        android:focusableInTouchMode="true"
        android:linksClickable="true"
        android:textAppearance="?android:attr/textAppearanceMedium" />

    <RelativeLayout
        android:id="@+id/TBOD_layout"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" >

        <EditText
            android:id="@+id/TBOD_edittext"
            android:layout_width="300dp"
            android:layout_height="30dp"
            android:layout_below="@+id/TBOD_textview"
            android:focusable="true"
            android:focusableInTouchMode="true"
            android:imeOptions="actionDone"
            android:inputType="none"
            android:maxLines="1"
            android:padding="2dp"
            android:singleLine="true"
            android:textColor="@android:color/black"
            android:textSize="14dp" />

        <ImageButton
            android:id="@+id/TBOD_accept"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignTop="@+id/TBOD_edittext"
            android:layout_marginLeft="15dp"
            android:layout_toRightOf="@+id/TBOD_edittext"
            android:background="@drawable/button_accept_selector" />

        <ImageButton
            android:id="@+id/TBOD_cancel"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignTop="@+id/TBOD_edittext"
            android:layout_marginLeft="5dp"
            android:layout_toRightOf="@+id/TBOD_accept"
            android:background="@drawable/button_cancel_selector" />

        <View
            android:id="@+id/TBOD_top"
            android:layout_width="fill_parent"
            android:layout_height="0dp"
            android:layout_alignParentTop="true"
            android:background="@android:color/transparent" />

        <View
            android:id="@+id/TBOD_bottom"
            android:layout_width="fill_parent"
            android:layout_height="0dp"
            android:layout_alignParentBottom="true"
            android:background="@android:color/transparent" />
    </RelativeLayout>

</RelativeLayout>

and finally, the attrs.xml file:

 <?xml version="1.0" encoding="utf-8"?>
<resources>

    <declare-styleable name="TextBoxOnDemand">
        <attr name="android:text" />
        <attr name="android:inputType" />
        <attr name="android:hint" />
        <attr name="android:textColor" />
        <attr name="android:textColorHint" />
        <attr name="android:linksClickable" />
        <attr name="android:autoLink" />
    </declare-styleable>

</resources>

This is how I used it in my main xml (after including the required namespace add):

 <com.shark.widget.TextBoxOnDemand
    android:id="@+id/profile_email2"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_alignLeft="@+id/profile_skypename"
    android:layout_below="@+id/profile_email_placeholder"
    android:hint="@string/add_email"
    android:inputType="textEmailAddress"
    android:textColor="@android:color/white"
    android:textColorHint="@color/skype_blue" />

EDIT: I've debugged the focus issues. It turns out that giving focus to children is difficult unless you call

setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);

Which kinda remedies the issue but still doesn't solve it. After some while of playing around with the onFocusChange() listener still trying to get the perfect behaviour, I threw in the towel and put in added two focus guards. I realized I cannot track the loss of focus only on my container (due to it never receiving focus) but I might as well track the idea of wanting to move away from the edit field... So i went the dirty route and added two invisible bar-like views to sandwitch the edittext in between. Once they got the focus, I could hide the component and ensure they transition properly.

And there it is, now it works as it should. Thanks to all who participated.

EDIT3: final polished version, i dumped the custom tags because they simply don't work reliably enough. Lesson to be learned: if there is an android tag for something, don't bother cloning it.

Community
  • 1
  • 1
Shark
  • 5,787
  • 3
  • 21
  • 46