联系人气泡编辑框

91

我正在尝试在MultiAutoCompleteTextView中创建类似Google+应用程序中实现的联系气泡。以下是屏幕截图:

Google+ Compose Post Screenshot .

我尝试扩展DynamicDrawableSpan类,以便在文本范围的背景中获得可跨度绘制。

public class BubbleSpan extends DynamicDrawableSpan {
  private Context c;

  public BubbleSpan(Context context) {
    super();
    c = context;
  }

  @Override
  public Drawable getDrawable() {
    Resources res = c.getResources();
    Drawable d = res.getDrawable(R.drawable.oval);
    d.setBounds(0, 0, 100, 20);
    return d;
  }
}

我的 oval.xml drawable 的定义如下:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
  <solid android:color="#352765"/>
  <padding android:left="7dp" android:top="7dp"
    android:right="7dp" android:bottom="7dp" />
  <corners android:radius="6dp" />
</shape>

在我的Activity类中有一个MulitAutoCompleteTextView,我像下面这样设置气泡范围:

final Editable e = tv.getEditableText();
final SpannableStringBuilder sb = new SpannableStringBuilder();
sb.append("some sample text");
sb.setSpan(new BubbleSpan(getApplicationContext()), 0, 6, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
e.append(sb); 

然而,与其在字符串中的前6个字符后面显示椭圆形状,这些字符不可见且没有背景中的椭圆图案。

如果我将BubbleSpan的getDrawable()方法更改为使用.png文件而不是形状可绘制对象:

public Drawable getDrawable() {
  Resources res = c.getResources();
  Drawable d = res.getDrawable(android.R.drawable.bottom_bar);
  d.setBounds(0, 0, 100, 20);
  return d;
}
然后 .png 文件会显示出来,但是字符串中属于 span 的字符不会显示出来。我该如何使 span 中的字符在前景中显示,同时让自定义形状的 drawable 显示在背景中呢?
我曾尝试使用 ImageSpan 而非继承 DynamicDrawableSpan,但未成功。

可能是Android EditText中的标签或气泡的重复问题。 - Peter O.
你是如何获取选定的联系人号码的? - srihari
https://github.com/splitwise/TokenAutoComplete 这可能对一些人有帮助。 - john-salib
4个回答

55

感谢 @chrish 的所有帮助。 这就是我做的方式:

final SpannableStringBuilder sb = new SpannableStringBuilder();
TextView tv = createContactTextView(contactName);
BitmapDrawable bd = (BitmapDrawable) convertViewToDrawable(tv);
bd.setBounds(0, 0, bd.getIntrinsicWidth(),bd.getIntrinsicHeight());

sb.append(contactName + ",");
sb.setSpan(new ImageSpan(bd), sb.length()-(contactName.length()+1), sb.length()-1,Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
to_input.setText(sb);

public TextView createContactTextView(String text){
  //creating textview dynamically
  TextView tv = new TextView(this);
  tv.setText(text);
  tv.setTextSize(20);
  tv.setBackgroundResource(R.drawable.oval);
  tv.setCompoundDrawablesWithIntrinsicBounds(0, 0, R.drawable.ic_clear_search_api_holo_light, 0);
  return tv;
}

public static Object convertViewToDrawable(View view) {
  int spec = MeasureSpec.makeMeasureSpec(0, 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(viewBmp);

}

13
我发现由于缩放问题,这种方法在所有设备上都不起作用。更好的方法是从convertView返回位图,然后在设置可绘制的边缘时使用实际位图的尺寸。例如:BitmapDrawable bd = new BitmapDrawable(bitmap); bd.setBounds(0,0,bitmap.getWidth(), bitmap.getHeight());这种方法适用于所有设备。 - dmon
嗨,我可能需要在一个新问题中发布这个内容,但想知道你是否遇到了多行TextView的问题。我正在Android 3.2上运行它,“有时候”,似乎气泡的宽度没有被正确地计算。也就是说,文本不会继续到下一行,而是会继续超出TextView。如果你见过这种情况,有什么提示吗? - Johann Hilbold
1
如果气泡中的文本比自动完成更宽,则气泡将在两行中创建两次。您应该将其设置为textView:textView.setMaxWidth(this.getWidth() - SOME_PADDING); 我还使用了以下内容(如果文本太大,则在气泡中有两行,末尾带有...):textView.setEllipsize(TruncateAt.END); textView.setSingleLine(false); textView.setMaxLines(2); - Koso
当您从下拉列表中选择时,此代码可以正常工作。但是如果您想将一些预定义值设置为EditText,则如何用边框和交叉按钮包装该文本? - Braj
6
如何在TextView中为不同的项添加点击监听器以删除文本。 - Nambi

21

这里是完整的解决方案

//creating textview dynamicalyy
TextView textView=new TextView(context);
textview.setText("Lauren amos");
textview.setbackgroundResource(r.color.urovalshape);
textView.setCompoundDrawablesWithIntrinsicBounds(0, 0, R.drawable.icon_cross, 0);


BitmapDrawable dd = (BitmapDrawable) SmsUtil.getDrawableFromTExtView(textView);
edittext.settext(addSmily(dd));

//convert image to spannableString
public SpannableStringBuilder addSmily(Drawable dd) {
 dd.setBounds(0, 0, dd.getIntrinsicWidth(),dd.getIntrinsicHeight());
SpannableStringBuilder builder = new SpannableStringBuilder();
builder.append(":-)");
builder.setSpan(new ImageSpan(dd), builder.length() - ":-)".length(),builder.length(),Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);

return builder;
}

  //convert view to drawable
  public static Object getDrawableFromTExtView(View view) {

    int spec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
    view.measure(spec, spec);
    view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight());
    Bitmap b = Bitmap.createBitmap(view.getWidth(), view.getHeight(),
            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(viewBmp);

}

这里是完整的项目文件,如果你们中有人想要使用Spannable


1
谢谢,你的解决方案很好用,但我的问题是当我按下“添加”按钮时,气泡文本会添加到编辑文本中,但是当我清除编辑文本并尝试重新输入(按Add按钮)之前添加的气泡仍然存在,并且新的气泡也被附加了。当我按右侧的交叉符号时,我也会删除气泡。 - GovindRathod
解决第一个问题。我猜你把文本存储在列表中,但在清除编辑文本时没有清除列表。我可能不正确,这只是一个猜测,因为我不知道你的代码如何编写。 - Krishna Shrestha
我知道这是一个旧帖子,但我需要回答我的问题。我正在构建与上面展示的相同的应用程序。我正在使用MultiAutoCompleteTextView来获取建议,然后在单击项目时创建气泡。气泡已经成功创建,但我的问题是我不想让光标出现在两个气泡之间。我正在为MultiAutoCompleteTextView使用空格分词器。有什么线索可以实现这一点吗? - VijayRaj
@chrish 感谢您提供的代码,但我还有一个问题。我现在有一些值,我会将它们直接粘贴到可编辑文本中作为可扩展文本。但是,我想知道是否可以将您的可扩展项目代码与此类功能结合起来。我希望能够在运行时添加和删除名称,添加可能会一次性进行,但删除通常是逐个进行的...谢谢。 - kuldeep

5

我有一个库,可以用以下方式实现你想要的功能:

  • 默认或完全可自定义(你甚至可以使用自己的布局)
  • 多行支持
  • 点击监听器

请在这里查看

这里是快速入门:

将ChipView添加到你的布局中或者通过编程创建它:

<com.plumillonforge.android.chipview.ChipView
    android:id="@+id/chipview"
    android:layout_width="match_parent"
    android:layout_height="wrap_content" />

使用扩展抽象芯片的数据列表和点击监听器(如果需要),对其进行初始化:

List<Chip> chipList = new ArrayList<>();
chipList.add(new Tag("Lorem"));
chipList.add(new Tag("Ipsum dolor"));
chipList.add(new Tag("Sit amet"));
chipList.add(new Tag("Consectetur"));
chipList.add(new Tag("adipiscing elit"));
ChipView chipDefault = (ChipView) findViewById(R.id.chipview);
chipDefault.setChipList(chipList);
chipDefault.setOnChipClickListener(new OnChipClickListener() {
        @Override
        public void onChipClick(Chip chip) {
            // Action here !
        }
    });

默认的ChipView呈现如下:

Default ChipView

但你可以根据自己的喜好进行整体和单个Chip的定制:

Overall ChipView Custom ChipView

虽然这不是一个MultiAutocomplete,但你可以模仿它(我实际上就是这样使用的)


2

材料 https://material.io/components/chips/android#using-chips

上述答案已过时

<com.google.android.material.chip.ChipGroup
    android:id="@+id/tags"
    android:layout_height="wrap_content"
    android:layout_width="match_parent"/>

然后在代码中:

val chip = Chip(context)
chip.text = text
chip.chipBackgroundColor =
ContextCompat.getColorStateList(
    requireContext(),
    R.color.color_sky_blue
)
chip.setOnCloseIconClickListener {
    //clicking close
}
chip.isCloseIconVisible = true
chip.closeIcon =
ContextCompat.getDrawable(requireContext(), R.drawable.ic_cross_chip)
chip.closeIconTint = ContextCompat.getColorStateList(requireContext(), R.color.colorAzure)
chip.closeIconSize = requireContext().resources.getDimension(R.dimen.dp12)
chip.setTextAppearance(R.style.ChipTextAppearance)
chipGroup.addView(chip)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接