私は周りを検索してきましたが、これに対する答えを見つけることができませんでした。私が実装しようとしているのは、ICS gmail アプリの作成画面にある「To」フィールドに似た EditText です。
ここに私が欲しいものを説明する画像があります:
独自のカスタム クラスを拡張EditText
して実装することを考えEditText
ていますが、それを行う方法や、それが最善の解決策であるかどうかもよくわかりません。何かご意見は?
私は周りを検索してきましたが、これに対する答えを見つけることができませんでした。私が実装しようとしているのは、ICS gmail アプリの作成画面にある「To」フィールドに似た EditText です。
ここに私が欲しいものを説明する画像があります:
独自のカスタム クラスを拡張EditText
して実装することを考えEditText
ていますが、それを行う方法や、それが最善の解決策であるかどうかもよくわかりません。何かご意見は?
この回答からソリューションを適応させました。カンマを挿入すると、入力が自動的に区切られます (セパレータは調整可能)。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:
<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>
最後に追加するのは、「x-Button」の png です。これまでのところうまく機能していますが、唯一の問題は、削除キーを長押ししても機能しないことです (誰かがそれを機能させる方法を知っている場合は、お気軽にコメントしてください)
うーん、似たような質問を見つけるのに少し時間がかかりましたが、それでも、私が見つけた最も近い答えはここにあります。私は以前に他の人がこの種の問題を抱えていることを知っていました!私を正しい方向に向けてくれたCommonsWareに感謝します。
良い解決策が見つからなかったので、これを処理するために独自のライブラリを構築しました: 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);
}
}
}
contact_token のレイアウト コード (独自の x ドローアブルを見つける必要があります)
<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>
トークン バックグラウンド ドローアブル
<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>
人物オブジェクトコード
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; }
}
サンプル活動
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", "marshall@example.com"),
new Person("Margaret Smith", "margaret@example.com"),
new Person("Max Jordan", "max@example.com"),
new Person("Meg Peterson", "meg@example.com"),
new Person("Amanda Johnson", "amanda@example.com"),
new Person("Terry Anderson", "terry@example.com")
};
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);
}
}
レイアウトコード
<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>