Android 使用类似标签的功能创建自定义 editText
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/11318551/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Creating a custom editText with tag-like feature
提问by bill-x
I've been searching around but could not find any answers to this. What I'm trying to implement is an EditText similar to the 'To' field found in the composing screen of the ICS gmail app.
我一直在四处寻找,但找不到任何答案。我试图实现的是一个 EditText 类似于在 ICS gmail 应用程序的撰写屏幕中找到的“收件人”字段。
Here's an image describing what I want:
这是描述我想要的图像:
I'm thinking of extending EditText
and implementing my own custom EditText
class but I'm not really sure how to do that or even if that's the best solution. Any thoughts?
我正在考虑扩展EditText
和实现我自己的自定义EditText
类,但我不确定如何做到这一点,或者即使这是最好的解决方案。有什么想法吗?
采纳答案by bill-x
Hm, took a while to find a similar question but nonetheless, here's the closest answer I found. I knew other people had this kind of problem before! Thanks to CommonsWare for pointing me in the right direction.
嗯,花了一段时间才找到一个类似的问题,但尽管如此,这是我找到的最接近的答案。我以前就知道其他人也有这种问题!感谢 CommonsWare 为我指明了正确的方向。
回答by AljoSt
Adapted the solution from this answer. Separates the input automatically when inserting a comma (separator can be adjusted). Creates a an ImageSpan and a ClickableSpan (entries can be removed by clicking on the right part).
根据这个答案改编了解决方案。插入逗号时自动分隔输入(分隔符可以调整)。创建一个 ImageSpan 和一个 ClickableSpan(可以通过单击右侧的部分来删除条目)。
public class TagEditText extends EditText {
TextWatcher textWatcher;
String lastString;
String separator = ",";
public TagEditText(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
setMovementMethod(LinkMovementMethod.getInstance());
textWatcher = new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void afterTextChanged(Editable s) {
String thisString = s.toString();
if (thisString.length() > 0 && !thisString.equals(lastString)) {
format();
}
}
};
addTextChangedListener(textWatcher);
}
private void format() {
SpannableStringBuilder sb = new SpannableStringBuilder();
String fullString = getText().toString();
String[] strings = fullString.split(separator);
for (int i = 0; i < strings.length; i++) {
String string = strings[i];
sb.append(string);
if (fullString.charAt(fullString.length() - 1) != separator.charAt(0) && i == strings.length - 1) {
break;
}
BitmapDrawable bd = (BitmapDrawable) convertViewToDrawable(createTokenView(string));
bd.setBounds(0, 0, bd.getIntrinsicWidth(), bd.getIntrinsicHeight());
int startIdx = sb.length() - (string.length());
int endIdx = sb.length();
sb.setSpan(new ImageSpan(bd), startIdx, endIdx, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
MyClickableSpan myClickableSpan = new MyClickableSpan(startIdx, endIdx);
sb.setSpan(myClickableSpan, Math.max(endIdx-2, startIdx), endIdx, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
if (i < strings.length - 1) {
sb.append(separator);
} else if (fullString.charAt(fullString.length() - 1) == separator.charAt(0)) {
sb.append(separator);
}
}
lastString = sb.toString();
setText(sb);
setSelection(sb.length());
}
public View createTokenView(String text) {
LinearLayout l = new LinearLayout(getContext());
l.setOrientation(LinearLayout.HORIZONTAL);
l.setBackgroundResource(R.drawable.bordered_rectangle_rounded_corners);
TextView tv = new TextView(getContext());
l.addView(tv);
tv.setText(text);
tv.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14);
ImageView im = new ImageView(getContext());
l.addView(im);
im.setImageResource(R.drawable.ic_cross_15dp);
im.setScaleType(ImageView.ScaleType.FIT_CENTER);
return l;
}
public Object convertViewToDrawable(View view) {
int spec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
view.measure(spec, spec);
view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight());
Bitmap b = Bitmap.createBitmap(view.getMeasuredWidth(), view.getMeasuredHeight(), Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(b);
c.translate(-view.getScrollX(), -view.getScrollY());
view.draw(c);
view.setDrawingCacheEnabled(true);
Bitmap cacheBmp = view.getDrawingCache();
Bitmap viewBmp = cacheBmp.copy(Bitmap.Config.ARGB_8888, true);
view.destroyDrawingCache();
return new BitmapDrawable(getContext().getResources(), viewBmp);
}
private class MyClickableSpan extends ClickableSpan{
int startIdx;
int endIdx;
public MyClickableSpan(int startIdx, int endIdx) {
super();
this.startIdx = startIdx;
this.endIdx = endIdx;
}
@Override
public void onClick(View widget) {
String s = getText().toString();
String s1 = s.substring(0, startIdx);
String s2 = s.substring(Math.min(endIdx+1, s.length()-1), s.length() );
TagEditText.this.setText(s1 + s2);
}
}
}
R.drawable.bordered_rectangle_rounded_corners:
R.drawable.bordered_rectangle_rounded_corners:
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid
android:color="@color/transparent"/>
<stroke android:width="1dp" android:color="#AAAAAA" />
<corners
android:radius="100dp" />
<padding
android:left="5dp"
android:top="5dp"
android:right="5dp"
android:bottom="5dp" />
</shape>
Last thing to add is png for the "x-Button". Works well so far, only problem is that pressing long on the delete-key doesn't work (if someone has an idea how to make it work, feel free to comment)
最后要添加的是“x-Button”的png。到目前为止效果很好,唯一的问题是长按删除键不起作用(如果有人知道如何使它工作,请随时发表评论)
回答by Marshall Weir
I couldn't find a good solution, so I would up building my own library to handle this: TokenAutoComplete. Here's a basic example:
我找不到一个好的解决方案,所以我会建立自己的库来处理这个问题:TokenAutoComplete。这是一个基本示例:
public class ContactsCompletionView extends TokenCompleteTextView {
public ContactsCompletionView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected View getViewForObject(Object object) {
Person p = (Person)object;
LayoutInflater l = (LayoutInflater)getContext().getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
LinearLayout view = (LinearLayout)l.inflate(R.layout.contact_token, (ViewGroup)ContactsCompletionView.this.getParent(), false);
((TextView)view.findViewById(R.id.name)).setText(p.getName());
return view;
}
@Override
protected Object defaultObject(String completionText) {
//Stupid simple example of guessing if we have an email or not
int index = completionText.indexOf('@');
if (index == -1) {
return new Person(completionText, completionText.replace(" ", "") + "@example.com");
} else {
return new Person(completionText.substring(0, index), completionText);
}
}
}
Layout code for contact_token (you'll need to find your own x drawable)
contact_token 的布局代码(你需要找到你自己的 x drawable)
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:background="@drawable/token_background">
<TextView android:id="@+id/name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@android:color/white"
android:textSize="14sp"
android:text="Test Me"
android:padding="2dp" />
<ImageView
android:layout_height="10dp"
android:layout_width="10dp"
android:src="@drawable/x"
android:layout_gravity="center_vertical"
android:layout_marginLeft="3dp"
android:layout_marginRight="5dp" />
</LinearLayout>
Token backgound drawable
令牌背景可绘制
<shape xmlns:android="http://schemas.android.com/apk/res/android" >
<solid android:color="#ffafafaf" />
<corners
android:topLeftRadius="5dp"
android:bottomLeftRadius="5dp"
android:topRightRadius="5dp"
android:bottomRightRadius="5dp" />
</shape>
Person object code
人物对象代码
public class Person implements Serializable {
private String name;
private String email;
public Person(String n, String e) { name = n; email = e; }
public String getName() { return name; }
public String getEmail() { return email; }
@Override
public String toString() { return name; }
}
Sample activity
示例活动
public class TokenActivity extends Activity {
ContactsCompletionView completionView;
Person[] people;
ArrayAdapter<Person> adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
people = new Person[]{
new Person("Marshall Weir", "[email protected]"),
new Person("Margaret Smith", "[email protected]"),
new Person("Max Jordan", "[email protected]"),
new Person("Meg Peterson", "[email protected]"),
new Person("Amanda Johnson", "[email protected]"),
new Person("Terry Anderson", "[email protected]")
};
adapter = new ArrayAdapter<Person>(this, android.R.layout.simple_list_item_1, people);
completionView = (ContactsCompletionView)findViewById(R.id.searchView);
completionView.setAdapter(adapter);
completionView.setTokenClickStyle(TokenCompleteTextView.TokenClickStyle.Delete);
}
}
Layout code
布局代码
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.tokenautocomplete.ContactsCompletionView
android:id="@+id/searchView"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</RelativeLayout>