¿Hay alguna manera de hacer que ellipsize = “marquee” siempre se desplace?

Quiero utilizar el efecto de marquesina en un TextView, pero el texto solo se desplaza cuando se enfoca el TextView. Eso es un problema, porque en mi caso, no puede.

Estoy usando:

android:ellipsize="marquee" android:marqueeRepeatLimit="marquee_forever" 

¿Hay alguna manera de hacer que TextView siempre desplace su texto? He visto esto hecho en la aplicación Android Market, donde el nombre de la aplicación se desplazará en la barra de título, incluso si no recibe el foco, pero no pude encontrarlo en los documentos de la API.

He estado enfrentando el problema y la solución más corta que he encontrado es crear una nueva clase derivada de TextView. La clase debe anular tres métodos en FocusChanged , onWindowFocusChanged y isFocused para enfocar todo en TextView.

 @Override protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) { if(focused) super.onFocusChanged(focused, direction, previouslyFocusedRect); } @Override public void onWindowFocusChanged(boolean focused) { if(focused) super.onWindowFocusChanged(focused); } @Override public boolean isFocused() { return true; } 

Finalmente me encontré con este problema hoy y por eso encendí el hierarchyviewer en la aplicación Android Market.

Al mirar el título en la pantalla de detalles de una aplicación, usan un viejo TextView . El examen de sus propiedades mostró que no estaba enfocado, no podía enfocarse y que, por lo general, era muy común, excepto por el hecho de que estaba marcado como seleccionado .

Una línea de código más tarde y lo tuve funcionando 🙂

 textView.setSelected(true); 

Esto tiene sentido, dado lo que dice el Javadoc :

Una vista puede ser seleccionada o no. Tenga en cuenta que la selección no es lo mismo que el enfoque. Las vistas se seleccionan típicamente en el contexto de un AdapterView como ListView o GridView.

es decir, cuando se desplaza sobre un elemento en una vista de lista (como en la aplicación Market), solo entonces comienza a desplazarse el texto ahora seleccionado . Y dado que este TextView particular no se puede TextView ni hacer clic, nunca perderá su estado de selección.

Desafortunadamente, hasta donde yo sé, no hay forma de predefinir el estado seleccionado desde el diseño XML.
Pero el one-liner anterior funciona bien para mí.

Simplemente ponga estos parámetros en su TextView. Funciona 🙂

  android:singleLine="true" android:ellipsize="marquee" android:marqueeRepeatLimit ="marquee_forever" android:scrollHorizontally="true" android:focusable="true" android:focusableInTouchMode="true" 

No sé si todavía necesitas la respuesta, pero encontré una manera fácil de hacerlo.

Configura tu animación así:

 Animation mAnimation = new TranslateAnimation(START_POS_X, END_POS_X, START_POS_Y, END_POS_Y); mAnimation.setDuration(TICKER_DURATION); mAnimation.setRepeatMode(Animation.RESTART); mAnimation.setRepeatCount(Animation.INFINITE); 

START_POS_X , END_POS_X , START_POS_Y y END_POS_Y son valores float , mientras TICKER_DURATION es un int que declare con mis otras constantes.

Entonces ahora puede aplicar esta animación a su TextView:

 TextView tickerText = (TextView) findViewById(R.id.ticker); tickerText.setAnimation(mAnimation); 

Y eso es. 🙂

Mi animación comienza en el lado derecho fuera de la pantalla (300f) y termina en el lado izquierdo fuera de la pantalla (-300f), con una duración de 15s (15000).

TranslateAnimation funciona “tirando” la Vista en una dirección en una cantidad específica. Puede establecer dónde comenzar este “tiro” y dónde terminar.

 TranslateAnimation(fromXDelta, toXDelta, fromYDelta, toYDelta); 

fromXDelta establece el desplazamiento de la posición inicial del movimiento en el eje X.

 fromXDelta = 0 //no offset. fromXDelta = 300 //the movement starts at 300px to the right. fromXDelta = -300 //the movement starts at 300px to the left 

toXDelta define la posición final de desplazamiento del movimiento en el eje X.

 toXDelta = 0 //no offset. toXDelta = 300 //the movement ends at 300px to the right. toXDelta = -300 //the movement ends at 300px to the left. 

Si el ancho de su texto es mayor que el módulo de la diferencia entre fromXDelta y toXDelta, el texto no podrá moverse total y competentemente dentro de la pantalla.


Ejemplo

Supongamos que nuestro tamaño de pantalla es de 320×240 px. Tenemos un TextView con un texto que tiene un ancho de 700px y deseamos crear una animación que “jale” el texto para que podamos ver el final de la frase.

  (screen) +---------------------------+ |<----------320px---------->| | | |+---------------------------<<<< X px >>>> movement<-----|| some TextView with text that goes out... |+--------------------------- | unconstrained size 700px | | | | | +---------------------------+ +---------------------------+ | | | | <<<< X px >>>>---------------------------+| movement<----- some TextView with text that goes out... || ---------------------------+| | | | | | | +---------------------------+ 

Primero establecemos desde fromXDelta = 0 para que el movimiento no tenga un desplazamiento inicial. Ahora necesitamos calcular el valor toDelta. Para lograr el efecto deseado, necesitamos "tirar" del texto exactamente el mismo px que se extiende fuera de la pantalla. (en el esquema está representado por <<<< X px >>>>) Dado que nuestro texto tiene 700 de ancho, y el área visible es de 320px (ancho de pantalla) establecemos:

 tXDelta = 700 - 320 = 380 

¿Y cómo calculamos el ancho de la pantalla y el ancho del texto?


Código

Tomando el fragmento de Zarah como punto de partida:

  /** * @param view The Textview or any other view we wish to apply the movement * @param margin A margin to take into the calculation (since the view * might have any siblings in the same "row") * **/ public static Animation scrollingText(View view, float margin){ Context context = view.getContext(); //gets the context of the view // measures the unconstrained size of the view // before it is drawn in the layout view.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED); // takes the unconstrained wisth of the view float width = view.getMeasuredWidth(); // gets the screen width float screenWidth = ((Activity) context).getWindowManager().getDefaultDisplay().getWidth(); // perfrms the calculation float toXDelta = width - (screenWidth - margin); // sets toXDelta to 0 if the text width is smaller that the screen size if (toXDelta < 0) {toXDelta = 0; } else { toXDelta = 0 - toXDelta;} // Animation parameters Animation mAnimation = new TranslateAnimation(0, toXDelta, 0, 0); mAnimation.setDuration(15000); mAnimation.setRepeatMode(Animation.RESTART); mAnimation.setRepeatCount(Animation.INFINITE); return mAnimation; } 

Puede haber formas más sencillas de realizar esto, pero esto funciona para cada vista que se te ocurra y es reutilizable. Es especialmente útil si desea animar un TextView en un ListView sin romper las capacidades habilitadas / onFocus de textView. También se desplaza continuamente incluso si la Vista no está enfocada.

Escribí el siguiente código para un ListView con elementos de texto de marquesina. Se basa en la solución setSelected descrita anteriormente. Básicamente, amplío la clase ArrayAdapter y anulo el método getView para seleccionar TextView antes de devolverlo:

  // Create an ArrayAdapter which selects its TextViews before returning // them. This would enable marqueeing while still making the list item // clickable. class SelectingAdapter extends ArrayAdapter { public SelectingAdapter( Context context, int resource, int textViewResourceId, LibraryItem[] objects ) { super(context, resource, textViewResourceId, objects); } @Override public View getView(int position, View convertView, ViewGroup parent) { View view = super.getView(position, convertView, parent); TextView textview = (TextView) view.findViewById( R.id.textview_playlist_item_title ); textview.setSelected(true); textview.setEnabled(true); textview.setFocusable(false); textview.setTextColor(0xffffffff); return view; } } 

Esta es la respuesta que aparece en la parte superior de mi búsqueda de Google, así que pensé que podría publicar una respuesta útil aquí, ya que tengo problemas para recordar esto con bastante frecuencia. De todos modos, esto funciona para mí y requiere atributos XML y A an onFocusChangeListener.

 //XML  //JAVA titleText.setOnFocusChangeListener(new View.OnFocusChangeListener() { @Override public void onFocusChange(View v, boolean hasFocus) { if (!hasFocus) { titleText.setSelected(true); } } });