Diseño de mensajes de Whatsapp – Cómo obtener vista de tiempo en la misma fila

Me preguntaba cómo WhatsApp maneja la hora que se muestra en cada mensaje.

Para aquellos que no saben:

  1. Si el mensaje es muy corto, el texto y la hora están en la misma fila.
  2. Si el mensaje es largo, la hora está en la esquina inferior derecha, el texto está alrededor.

Con RelativeLayout y toLeftOf obtendría 1) pero no 2) ya que las líneas anteriores se “cortarían” en la posición de la vista de tiempo. Mismo comportamiento si uso LinearLayout .

Así que traté de usar FrameLayout o RelativeLayout sin ninguna conexión entre el texto y la hora.

Sin embargo, si el texto es tan largo como la vista de mensaje es grande, ambas vistas se superpondrían. Si pongo caracteres en blanco en mi mensaje, no tendré tiempo a la derecha.

¿Realmente tienen algún tipo de text-wrapping-lib para esto o solo se puede hacer con layouts?

Aquí está la captura de pantalla solicitada:

enter image description here

La adición de espacios HTML sin interrupción hizo el truco. Probé el código en la mayoría de los dispositivos y trabajé absolutamente bien. Quizás whatsapp también está haciendo lo mismo. Debajo está el código de chat:

Vea las imágenes a continuación para ver cómo funciona.

Diseño XML:

         

Código: bg_msg_from.xml

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

** Archivo: bubble_corner.png **

Imagen de flecha derecha enter image description here

enter image description hereenter image description hereenter image description here

 txtMsgFrom.setText(Html.fromHtml(convertToHtml(txtMsgFrom.getText().toString()) + "         ")); // 10 spaces 

@Hisham Muneer es una respuesta muy buena.

Pero hay algunos problemas. Por ejemplo:

  • Si TextView tiene 2 líneas completas (de extremo a extremo), el texto se intersecará con el diseño de texto de fecha y hora. Finalmente, las vistas se verán como efecto de cebolla.
  • La línea de texto no funciona de manera eficiente. Debe controlar estas líneas y reubicar la vista de fecha y hora.

Voy a compartir mi solución, si necesita este problema.

Esta es una captura de pantalla de ejemplo Captura de pantalla de ejemplo

ImFlexboxLayout.java

  public class ImFlexboxLayout extends RelativeLayout { private TextView viewPartMain; private View viewPartSlave; private TypedArray a; private RelativeLayout.LayoutParams viewPartMainLayoutParams; private int viewPartMainWidth; private int viewPartMainHeight; private RelativeLayout.LayoutParams viewPartSlaveLayoutParams; private int viewPartSlaveWidth; private int viewPartSlaveHeight; public ImFlexboxLayout(Context context) { super(context); } public ImFlexboxLayout(Context context, AttributeSet attrs) { super(context, attrs); a = context.obtainStyledAttributes(attrs, R.styleable.ImFlexboxLayout, 0, 0); } @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); try { viewPartMain = (TextView) this.findViewById(a.getResourceId(R.styleable.ImFlexboxLayout_viewPartMain, -1)); viewPartSlave = this.findViewById(a.getResourceId(R.styleable.ImFlexboxLayout_viewPartSlave, -1)); } catch (Exception e) { e.printStackTrace(); } } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int widthSize = MeasureSpec.getSize(widthMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); if (viewPartMain == null || viewPartSlave == null || widthSize < = 0) { return; } int availableWidth = widthSize - getPaddingLeft() - getPaddingRight(); int availableHeight = heightSize - getPaddingTop() - getPaddingBottom(); viewPartMainLayoutParams = (LayoutParams) viewPartMain.getLayoutParams(); viewPartMainWidth = viewPartMain.getMeasuredWidth() + viewPartMainLayoutParams.leftMargin + viewPartMainLayoutParams.rightMargin; viewPartMainHeight = viewPartMain.getMeasuredHeight() + viewPartMainLayoutParams.topMargin + viewPartMainLayoutParams.bottomMargin; viewPartSlaveLayoutParams = (LayoutParams) viewPartSlave.getLayoutParams(); viewPartSlaveWidth = viewPartSlave.getMeasuredWidth() + viewPartSlaveLayoutParams.leftMargin + viewPartSlaveLayoutParams.rightMargin; viewPartSlaveHeight = viewPartSlave.getMeasuredHeight() + viewPartSlaveLayoutParams.topMargin + viewPartSlaveLayoutParams.bottomMargin; int viewPartMainLineCount = viewPartMain.getLineCount(); float viewPartMainLastLineWitdh = viewPartMainLineCount > 0 ? viewPartMain.getLayout().getLineWidth(viewPartMainLineCount - 1) : 0; widthSize = getPaddingLeft() + getPaddingRight(); heightSize = getPaddingTop() + getPaddingBottom(); if (viewPartMainLineCount > 1 && !(viewPartMainLastLineWitdh + viewPartSlaveWidth >= viewPartMain.getMeasuredWidth())) { widthSize += viewPartMainWidth; heightSize += viewPartMainHeight; } else if (viewPartMainLineCount > 1 && (viewPartMainLastLineWitdh + viewPartSlaveWidth >= availableWidth)) { widthSize += viewPartMainWidth; heightSize += viewPartMainHeight + viewPartSlaveHeight; } else if (viewPartMainLineCount == 1 && (viewPartMainWidth + viewPartSlaveWidth >= availableWidth)) { widthSize += viewPartMain.getMeasuredWidth(); heightSize += viewPartMainHeight + viewPartSlaveHeight; } else { widthSize += viewPartMainWidth + viewPartSlaveWidth; heightSize += viewPartMainHeight; } this.setMeasuredDimension(widthSize, heightSize); super.onMeasure(MeasureSpec.makeMeasureSpec(widthSize, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(heightSize, MeasureSpec.EXACTLY)); } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); if (viewPartMain == null || viewPartSlave == null) { return; } viewPartMain.layout( getPaddingLeft(), getPaddingTop(), viewPartMain.getWidth() + getPaddingLeft(), viewPartMain.getHeight() + getPaddingTop()); viewPartSlave.layout( right - left - viewPartSlaveWidth - getPaddingRight(), bottom - top - getPaddingBottom() - viewPartSlaveHeight, right - left - getPaddingRight(), bottom - top - getPaddingBottom()); } } 

attrs.xml

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

Ejemplo de diseño de globos a la derecha (globo.xml)

            

Puede modificar el diseño xml y algunas secciones relacionadas con su escenario.

Hay 2 puntos importantes : debe definir en el diseño los atributos xml “viewPartMain” , “viewPartSlave” . Porque el código decidirá la medida a través de los elementos principales (vista de texto de chat) y esclavo (vista de texto de fecha y hora).

Deseo tener buenos días. Saludos

Es más fácil con Unicode desde aquí .

Entonces con esto puedes archivar el formato Unicode

  new TextView("Hello\u00A0world"); 

mejor que la cadena HTML

fuente: https://stackoverflow.com/a/6565049

Propongo otra solución

 public static final String TAG = "MainActivity"; private TextView mText; private RelativeLayout relativeLayout; private Boolean mFirstTime = true; private static final int WIDH_HOUR = 382; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); final int width = getScreensWidh(); mText = (TextView) findViewById(R.id.activity_main_text); relativeLayout = (RelativeLayout) findViewById(R.id.activity_main_relative); mText.setText("aaaaa dfsafsa afdsfa fdsafas adfas fdasf adfsa dsa aaaa dfsafsa afdsfa fdsafas adfas fdasf adfsa"); ViewTreeObserver vto = mText.getViewTreeObserver(); vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { if (mFirstTime) { Layout layout = mText.getLayout(); int lines = layout.getLineCount(); int offset = layout.layout.getLineWidth(lines - 1); int freeSpace = width - offset; TextView hour = new TextView(MainActivity.this); hour.setText("12:20"); RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT); params.addRule(RelativeLayout.ALIGN_PARENT_RIGHT); if (freeSpace > WIDH_HOUR) { params.addRule(RelativeLayout.ALIGN_BOTTOM, R.id.activity_main_text); } else { params.addRule(RelativeLayout.BELOW, R.id.activity_main_text); } hour.setLayoutParams(params); relativeLayout.addView(hour); Log.d(TAG, String.valueOf(freeSpace)); mFirstTime = false; } } }); } public int getScreensWidh() { Display display = getWindowManager().getDefaultDisplay(); Point size = new Point(); display.getSize(size); return size.x; } 

Dos métodos públicos

  • public abstract int getLineCount ()

Devuelve la cantidad de líneas de texto en este diseño.

  • public int getLineWidth (línea int)

Obtiene la extensión horizontal sin signo de la línea especificada, incluida la sangría del margen inicial y el espacio en blanco posterior.

Puede usar el diseño y el código a continuación para lograr el efecto deseado. Fuente clave del código

Lo que he usado es obtener el ancho del texto + el diseño del tiempo y verificar si esto excede el ancho del diseño del contenedor, y ajustar la altura del contenedor en consecuencia. Tenemos que extendernos desde FrameLayout ya que este es el que permite la superposición de dos vistas secundarias.

Se ha probado que esto funciona en inglés. Sugerencias y mejoras siempre son bienvenidas 🙂

Espero haber ayudado a alguien que busca la misma solución.

Supongo que la forma más fácil de lograr este tipo de diseño sería agregar suficiente espacio en blanco en su mensaje para asegurarse de que hay suficiente espacio a la derecha para no cubrir el tiempo (no veo otra manera fácil de tener un margen / relleno / posicionamiento para la última línea de su texto solamente) Luego, simplemente coloque el tiempo en el pariente como una alineación inferior derecha

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

El código de color de fondo del mensaje de Whatsapp es: #b2cf97