本文介绍了EditText上点播插件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想有一个TextView显示文字,当你点击它/ longclick,一个文本框应该现身,并允许上述文字编辑。当你完成编辑(安其进入我想)应该恢复与更新的文字一个TextView ...

我不知道,如果是feasable实现这样的小部件,或者我应该砍一个解决方法吗?提示和建议都非常欢迎。

如果你需要我的意思进一步的想法,就到你如(视窗)的Skype个人资料,并看到自己。

编辑:
澄清:我专门要求Widget或这样这是一个TextView,直到点击,然后转变为含有相同的文字一个EditText;一旦完成编辑将其转换回一个TextView重新presenting新的改变的文本。这就是我所说的按需部件的EditText。

但我希望能得到更好的东西比

 公共类的Widget {
     TextView的文本;
     编辑的EditText;
     字符串textToRe present;
     // ...
}


解决方案

就像我上周四表示...尤伯是pretty接近,但不是很密切。他确实有个大致相同的想法,但(理论上)冲进code太早;)

下面列出的TextBoxOnDemand code是生产就绪。这个想法是相似,我想避免的OP和尤伯什么建议,但最佳实现(使用而不是为实例的RelativeLayout的一个ViewSwitcher)

我收集为此所需资源,在下面的文章:

自定义视图

Declaring一个定制的Andr​​oid使用的UI元素XML

How通过在Java和XML 的自定义组件的参数

和决定张贴在这里,因为谷歌官方培训文档是无用的,要么是过时的(de precated)或不包括我需要的东西。我希望你不介意我称我自己的奖金,但是这是我想要的解决方案(和预期,ERGO赏金)。
 我猜code将不得不这样做;)

TextBoxOnDemand.java:

 包com.skype.widget;进口android.content.Context;
进口android.content.res.ColorStateList;
进口android.content.res.TypedArray;
进口android.text.SpannableString;
进口android.text.style.UnderlineSpan;
进口android.text.util.Linkify;
进口android.util.AttributeSet;
进口android.util.Log;
进口android.view.KeyEvent;
进口android.view.MotionEvent;
进口android.view.View;
进口android.view.View.OnClickListener;
进口android.view.View.OnFocusChangeListener;
进口android.view.View.OnHoverListener;
进口android.view.View.OnLongClickListener;
进口android.widget.EditText;
进口android.widget.ImageButton;
进口android.widget.RelativeLayout;
进口android.widget.TextView;
进口android.widget.TextView.OnEditorActionListener;
进口android.widget.ViewSwitcher;进口com.skype.ref.R;
进口com.skype.ref.RemoteKeys;公共类TextBoxOnDemand扩展ViewSwitcher实现OnClickListener,OnLongClickListener,OnFocusChangeListener,OnHoverListener,
        OnEditorActionListener
{
    公共静态最后弦乐LOGTAG =TextBoxOnDemand;    私人查看btmGuard;
    私人的ImageButton取消,接受;
    私人的EditText编辑;
    私人的RelativeLayout editorLayout;
    私人TextView的脸;
    私人字符串提示=新的String();
    私人布尔inEditMode = FALSE; //通常这是TextView的模式
    私人布尔inputReady = FALSE;
    私人字符串ourData =新的String();
    私人字符串prefillData =新的String();
    私人字符串标记=新的String(); //通常标记为空。
    私人查看topGuard;
    私人诠释autoLinkMask; // = Linkify.EMAIL_ADDRESSES; //Linkify.ALL;
    私人ColorStateList文字颜色,hintColor = NULL;    公共TextBoxOnDemand(上下文的背景下)
    {
        超级(上下文);
        建立(上下文);
        setEditable(假); //在里面
    }    公共TextBoxOnDemand(上下文的背景下,ATTRS的AttributeSet)
    {
        超(背景下,ATTRS);
        建立(上下文);
        的init(背景下,ATTRS);
        setEditable(假); //在里面
    }    公共字符串的get prefillData()
    {
        返回prefillData;
    }    公共字符串getTag()
    {
        返回标签;
    }    公共字符串的getText()
    {
        Log.d(LOGTAG的getText()返回'+ ourData +');
        返回ourData;
    }    公共布尔有prefillData()
    {
        返回prefillData.isEmpty();
    }    公共布尔isEditable()
    {
        Log.d(LOGTAGisEditable()返回+ inEditMode);
        返回inEditMode;
    }    @覆盖
    公共无效的onClick(视图v)
    {
        Log.d(LOGTAG的onClick(+ V +));
        如果(inEditMode)
        {
            如果(v.equals(接受))
            {
                如果(editor.getEditableText()长度()== 0 || editor.getEditableText()长度()方式> 5)
                    。ourData = editor.getEditableText()的toString();                setEditable(假);
            }否则如果(v.equals(取消))
            {
                setEditable(假);
            }
        }
    }    @覆盖
    公共布尔onEditorAction(TextView的V,INT actionId,KeyEvent的事件)
    {
        // Log.d(LOGTAGonEditorAction(+ V +,+ actionId +,+事件+)被解雇了!);
        Log.d(LOGTAGonEditorAction()解雇,inputReady =+ inputReady);
        如果(editor.getEditableText()长()方式> 0安培;&安培; editor.getEditableText()长()<(prefillData.length()+ 2))返回true; //用户需要输入一些
        如果(inputReady&安培;及(event.getKey code()== RemoteKeys.ENTER.key code()|| event.getKey code()== KeyEvent.KEY code_ENTER ))//总是
        {
            如果(editor.getEditableText()长()方式> prefillData.length()|| editor.getEditableText()长()== 0)
                。ourData = editor.getEditableText()的toString();            setEditable(假);
            返回false;
        }        如果((editor.getEditableText()。toString()方法。与compareToIgnoreCase(ourData)== 0 || editor.getEditableText()。的toString()
                .compareToIgnoreCase(prefillData)== 0)
                &功放;&安培; !inputReady)//意味着我们不只是继续持有进入
            返回true;
        其他
            inputReady = TRUE;        返回true;
    }    @覆盖
    公共无效onFocusChange(视图V,布尔hasFocus)
    {
        Log.d(LOGTAGonFocusChange(+ V +,+ hasFocus +)\\ tinEditMode =+ inEditMode);
        如果(inEditMode)
        {
            如果(hasFocus&放大器;及(v.equals(topGuard)|| v.equals(btmGuard)))
            {
                setEditable(假);
                requestFocus()方法;
            }            如果(hasFocus&放大器;及(v.equals(编辑)|| v.equals(接受)|| v.equals(取消)))
            {
                //什么也不做,你应该能够自由地在这里浏览
                如果(ourData.isEmpty()及&放大器; editor.getEditableText()长度()&所述; prefillData.length())
                {
                    Log.d(LOGTAG,并称pre灌注,在此之前=+ editor.getEditableText());
                    editor.setText();
                    editor.append(prefillData);
                    Log.d(LOGTAG,现在是=+ editor.getEditableText());
                }
            }
        }其他
        {
            字符串文本=(ourData.isEmpty())?提示:ourData;
            ColorStateList色;
            如果(hintColor = NULL&放大器;!&安培; ourData.isEmpty())
                颜色= hintColor;
            其他
                颜色=文字颜色;
            face.setTextColor(颜色);
            如果(hasFocus)
            {
                SpannableString SS =新SpannableString(文本);
                ss.setSpan(新UnderlineSpan(),0,text.length(),0);
                face.setText(SS);
            }其他
                face.setText(文本);
        }
    }    @覆盖
    公共布尔onHover(视图V,MotionEvent事件)
    {
        // Log.d(LOGTAGonHover());
        字符串文本=(ourData.isEmpty())?提示:ourData;
        ColorStateList色;
        如果(hintColor = NULL&放大器;!&安培; ourData.isEmpty())
            颜色= hintColor;
        其他
            颜色=文字颜色;
        face.setTextColor(颜色);
        开关(event.getAction())
        {
            案例MotionEvent.ACTION_HOVER_ENTER:
                SpannableString SS =新SpannableString(文本);
                ss.setSpan(新UnderlineSpan(),0,text.length(),0);
                face.setText(SS);
                打破;
            案例MotionEvent.ACTION_HOVER_EXIT:
                face.setText(文本);
                打破;
        }
        返回true;
    }    @覆盖
    公共布尔onLongClick(视图V)
    {
        Log.d(LOGTAGonLongClick()\\ tinEditMode =+ inEditMode);
        如果(!inEditMode)//意味着getDisplayedChild()== 0,表示的TextView
        {
            setEditable(真);
            返回true;
        }其他
            返回false;
    }    公共无效setEditable(布尔值)
    {
        Log.d(LOGTAGsetEditable(+值+));
        inEditMode =价值;
        如果(inEditMode)
        {
            //显示editorLayout
            face.setOnLongClickListener(NULL);
            face.setOnHoverListener(NULL);
            face.setOnFocusChangeListener(NULL); //因为GC的。
            face.setOnClickListener(NULL);
            face.setVisibility(View.GONE);
            setDisplayedChild(1);
            editorLayout.setVisibility(View.VISIBLE);
            editor.setOnFocusChangeListener(本);
            editor.setOnEditorActionListener(本);
            cancel.setOnClickListener(本);
            accept.setOnClickListener(本);
            accept.setOnFocusChangeListener(本);
            cancel.setOnFocusChangeListener(本);
        }其他
        {
            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(本);
            face.setOnHoverListener(本);
            face.setOnFocusChangeListener(本);
            face.setOnClickListener(本);
            face.setFocusable(真);
            face.setFocusableInTouchMode(真);
        }
        updateViews();
    }    @覆盖
    公共无效setNextFocusDownId(INT nextFocusDownId)
    {
        super.setNextFocusDownId(nextFocusDownId);
        face.setNextFocusDownId(nextFocusDownId);
        // editor.setNextFocusDownId(nextFocusDownId);
        accept.setNextFocusDownId(nextFocusDownId);
        cancel.setNextFocusDownId(nextFocusDownId);
    }    @覆盖
    公共无效setNextFocusForwardId(INT nextFocusForwardId)
    {
        super.setNextFocusForwardId(nextFocusForwardId);
        face.setNextFocusForwardId(nextFocusForwardId);
        editor.setNextFocusForwardId(nextFocusForwardId);
    }    @覆盖
    公共无效setNextFocusLeftId(INT nextFocusLeftId)
    {
        super.setNextFocusLeftId(nextFocusLeftId);
        face.setNextFocusLeftId(nextFocusLeftId);
        editor.setNextFocusLeftId(nextFocusLeftId);
    }    @覆盖
    公共无效setNextFocusRightId(INT nextFocusRightId)
    {
        super.setNextFocusRightId(nextFocusRightId);
        face.setNextFocusRightId(nextFocusRightId);
        cancel.setNextFocusRightId(nextFocusRightId);
    }    @覆盖
    公共无效setNextFocusUpId(INT nextFocusUpId)
    {
        super.setNextFocusUpId(nextFocusUpId);
        face.setNextFocusUpId(nextFocusUpId);
        // editor.setNextFocusUpId(nextFocusUpId);
        accept.setNextFocusUpId(nextFocusUpId);
        cancel.setNextFocusUpId(nextFocusUpId);
    }    公共无效集prefillData(字符串prefillData)
    {
        这prefillData =新的String(prefillData);
    }    公共字符串setTag()
    {
        返回标签;
    }    公共无效的setText(字符串文本)
    {
        Log.d(LOGTAG的setText(+文字+));
        ourData =文本;
        updateViews();
    }    私人无效版本(上下文的背景下)
    {
        Log.d(LOGTAG,建立());
        addView(View.inflate(背景下,R.layout.textboxondemand,NULL));
        setFocusable(真);
        setFocusableInTouchMode(真);
        setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);
        setOnFocusChangeListener(本);
        setOnLongClickListener(本);        FACE =(的TextView)findViewById(R.id.TBOD_textview);
        editorLayout =(RelativeLayout的)findViewById(R.id.TBOD_layout);
        编辑=(EditText上)findViewById(R.id.TBOD_edittext);
        接受=(的ImageButton)findViewById(R.id.TBOD_accept);
        取消=(的ImageButton)findViewById(R.id.TBOD_cancel);
        topGuard =(查看)findViewById(R.id.TBOD_top);
        btmGuard =(查看)findViewById(R.id.TBOD_bottom);        face.setFocusable(真);
        face.setFocusableInTouchMode(真);        face.setOnLongClickListener(本);
        face.setOnHoverListener(本);
        face.setOnFocusChangeListener(本);
        face.setOnClickListener(本);        editor.setOnFocusChangeListener(本);
        editor.setOnEditorActionListener(本);
        editor.setHint(提示);
        editor.setFocusable(真);
        editor.setFocusableInTouchMode(真);        accept.setOnClickListener(本);
        accept.setOnFocusChangeListener(本);
        accept.setFocusable(真);
        cancel.setFocusable(真);
        cancel.setOnFocusChangeListener(本);
        cancel.setOnClickListener(本);        topGuard.setFocusable(真);
        topGuard.setOnFocusChangeListener(本);
        btmGuard.setFocusable(真);
        btmGuard.setOnFocusChangeListener(本);        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);
    }    私人无效的init(上下文的背景下,ATTRS的AttributeSet)
    {
        TypedArray A = context.obtainStyledAttributes(ATTRS,R.styleable.TextBoxOnDemand);
        //用一个
        Log.d(LOGTAG的init());
        如果(A == NULL)Log.d(LOGTAG,你有'的xmlns:程序= \\HTTP://schemas.android.com/apk/res-auto \\'?在你的根布局);        最终诠释N = a.getIndexCount();
        的for(int i = 0; I< N ++ I)
        {
            INT ATTR = a.getIndex(ⅰ);
            开关(ATTR)
            {
                案例R.styleable.TextBoxOnDemand_android_hint:
                    提示=新的String(a.getString(attr)使用);
                    editor.setHint(a.getString(attr)使用);
                    打破;
                案例R.styleable.TextBoxOnDemand_android_text:
                    ourData =新的String(a.getString(attr)使用);
                    打破;
                案例R.styleable.TextBoxOnDemand_android_inputType:
                    INT的inputType = a.getInt(ATTR,-1);
                    如果(!inputType下= -1)editor.setInputType(inputType下);
                    打破;
                案例R.styleable.TextBoxOnDemand_android_textColor:
                    文字颜色= a.getColorStateList(attr)使用;
                    face.setTextColor(文字颜色);
                    打破;
                案例R.styleable.TextBoxOnDemand_android_linksClickable:
                    face.setLinksClickable(a.getBoolean(ATTR,真实));
                    打破;
                案例R.styleable.TextBoxOnDemand_android_textColorHint:
                    hintColor = a.getColorStateList(attr)使用;
                    打破;
                案例R.styleable.TextBoxOnDemand_android_autoLink:
                    autoLinkMask = a.getInt(ATTR,0);
                    face.setAutoLinkMask(autoLinkMask);
                    打破;                默认:
                    Log.d(LOGTAG,跳绳属性+ attr)使用;
            }
        }        //不要忘记这一点
        a.recycle();
    }    私人无效updateViews()
    {
        Log.d(LOGTAGupdateViews());
        //如果(getDisplayedChild()== 0)//第一个孩子 - 的TextView
        如果(inEditMode!)//第一个孩子 - 的TextView
        {
            如果(ourData.isEmpty())
            {
                如果(hintColor!= NULL)face.setTextColor(hintColor);
                face.setText(提示);
            }其他
            {
                face.setTextColor(文字颜色);
                face.setText(ourData);
            }
            face.setFocusable(真);
            face.setFocusableInTouchMode(真);
            face.setAutoLinkMask(autoLinkMask);
        }其他
        {//第二个孩子 - 的EditText
            editor.setFocusable(真);
            editor.setFocusableInTouchMode(真);
            如果(ourData.startsWith(prefillData)|| ourData.length()> = prefillData.length())
                editor.setText();
            其他
                editor.setText(prefillData);            editor.append(ourData);
            inputReady = FALSE;            editor.requestFocus();
        }
    }    公共无效setAutoLinkMask(LinkifyEnum linkifyEnumConstant)
    {
        开关(linkifyEnumConstant)
        {
            情况下,所有:
                autoLinkMask = Linkify.ALL;
                打破;
            案例EMAIL_ADDRESSES:
                autoLinkMask = Linkify.EMAIL_ADDRESSES;
                打破;
            案例MAP_ADDRESSES:
                autoLinkMask = Linkify.MAP_ADDRESSES;
                打破;
            案例PHONE_NUMBERS:
                autoLinkMask = Linkify.PHONE_NUMBERS;
                打破;
            案例WEB_URLS:
                autoLinkMask = Linkify.WEB_URLS;
                打破;            案例NONE:
            默认:
                autoLinkMask = 0;
                打破;
        }
        //现在设置
        face.setAutoLinkMask(autoLinkMask);
    }    公共枚举LinkifyEnum
    {
        ALL,EMAIL_ADDRESSES,MAP_ADDRESSES,PHONE_NUMBERS,WEB_URLS,NONE
    };}

我还在一些重点相关的问题,但这个工程按预期。当我使用onFocuslistener 1,你不能从关注一个TextBox之分;当文本框本身是可聚焦,我可以集中精力从一个到另一个就好了,但我不能跨重点纯洁了孩子,因此无法集中的EditText键入

XML文件:

 <的RelativeLayout的xmlns:机器人=htt​​p://schemas.android.com/apk/res/android
    机器人:layout_width =WRAP_CONTENT
    机器人:layout_height =WRAP_CONTENT>    <的TextView
        机器人:ID =@ + ID / TBOD_textview
        机器人:layout_width =WRAP_CONTENT
        机器人:layout_height =FILL_PARENT
        机器人:自动链接=电子邮件
        机器人:可聚焦=真
        机器人:focusableInTouchMode =真
        机器人:linksClickable =真
        机器人:textAppearance =机器人:ATTR / textAppearanceMedium/>    < RelativeLayout的
        机器人:ID =@ + ID / TBOD_layout
        机器人:layout_width =WRAP_CONTENT
        机器人:layout_height =WRAP_CONTENT>        <的EditText
            机器人:ID =@ + ID / TBOD_edittext
            机器人:layout_width =300dp
            机器人:layout_height =30dp
            机器人:layout_below =@ + ID / TBOD_textview
            机器人:可聚焦=真
            机器人:focusableInTouchMode =真
            机器人:imeOptions =actionDone
            安卓的inputType =无
            机器人:MAXLINES =1
            机器人:填充=2DP
            机器人:单线=真
            机器人:文字颜色=@机器人:彩色/黑白
            机器人:TEXTSIZE =14dp/>        <的ImageButton
            机器人:ID =@ + ID / TBOD_accept
            机器人:layout_width =WRAP_CONTENT
            机器人:layout_height =WRAP_CONTENT
            机器人:layout_alignTop =@ + ID / TBOD_edittext
            机器人:layout_marginLeft =15dp
            机器人:layout_toRightOf =@ + ID / TBOD_edittext
            机器人:背景=@绘制/ button_accept_selector/>        <的ImageButton
            机器人:ID =@ + ID / TBOD_cancel
            机器人:layout_width =WRAP_CONTENT
            机器人:layout_height =WRAP_CONTENT
            机器人:layout_alignTop =@ + ID / TBOD_edittext
            机器人:layout_marginLeft =5DP
            机器人:layout_toRightOf =@ + ID / TBOD_accept
            机器人:背景=@绘制/ button_cancel_selector/>        <查看
            机器人:ID =@ + ID / TBOD_top
            机器人:layout_width =FILL_PARENT
            机器人:layout_height =0dp
            机器人:layout_alignParentTop =真
            机器人:背景=@机器人:彩色/透明/>        <查看
            机器人:ID =@ + ID / TBOD_bottom
            机器人:layout_width =FILL_PARENT
            机器人:layout_height =0dp
            机器人:layout_alignParentBottom =真
            机器人:背景=@机器人:彩色/透明/>
    < / RelativeLayout的>< / RelativeLayout的>

和最后,attrs.xml文件:

 <?XML版本=1.0编码=UTF-8&GT?;
<资源>    <申报-设置样式名称=TextBoxOnDemand>
        < attr指示名=机器人:文字/>
        < attr指示名=机器人:inputType下/>
        < attr指示名=机器人:提示/>
        < attr指示名=机器人:文字颜色/>
        < attr指示名=机器人:textColorHint/>
        < attr指示名=机器人:linksClickable/>
        < attr指示名=机器人:自动链接/>
    < /申报,设置样式>< /资源>

这是我用它在我的主XML(包括所需的名称空间中添加后):

 < com.shark.widget.TextBoxOnDemand
    机器人:ID =@ + ID / profile_email2
    机器人:layout_width =WRAP_CONTENT
    机器人:layout_height =WRAP_CONTENT
    机器人:layout_alignLeft =@ + ID / profile_skypename
    机器人:layout_below =@ + ID / profile_email_placeholder
    机器人:提示=@字符串/ add_email
    安卓的inputType =textEmailAddress
    机器人:文字颜色=@机器人:彩色/白
    机器人:textColorHint =@色/ skype_blue/>

编辑:我调试的焦点问题。事实证明,给予重点孩子很难,除非你叫

  setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);

这有点儿补救这个问题,但仍然没有解决。一些而玩弄的 onFocusChange()监听器仍试图获得完美的表现后,我认输,放入加了两个重点看守。我意识到,我无法跟踪对焦的损失只在我的容器(由于它永远不会获得焦点),但我还不如追踪想从编辑字段移开的想法...让我去脏的路线,增加了两个隐形条状意见sandwitch中的EditText之间。一旦他们得到了关注的焦点,我可以隐藏组件,并确保它们正常过渡。

和那里的是,现在它的作品,因为它应该。感谢所有谁参加。

EDIT3:最后的打磨版本,我甩了自定义标签,因为他们根本就没有足够可靠工作。忠告就是:如果有东西的机器人标签,不要打扰它克隆

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;
     //...
}
解决方案

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.

这篇关于EditText上点播插件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-28 01:56
查看更多