Manejo de spoiler BBcode Android

Estoy desarrollando una aplicación para un foro.

Necesito conseguir un botón de spoiler como el de Tapatalk

Introduzca aquí la descripción de la imagen

Que oculta una parte de texto, que se muestra sólo cuando el usuario hace clic en el botón.

Estoy recibiendo todo el texto del post, incluyendo el spoiler BBCode. He logrado obtener el contenido del alerón con la siguiente expresión regular:

\[SPOILER\](.+?)\[\/SPOILER\] 

Mi problema es que quiero agregar un botón spoiler, pero todo mi texto es HTML-como, como en todos los objetos (imágenes, enlaces, formato de código, etc …) se traduce en HTML, a continuación, manejado por Android con el método Html .fromHtml () .

Aquí está el método de análisis sintáctico, que "traduce" el BBCode en HTML:

 private static String parsePostContent(String text){ String html = text; Map<String,String> bbMap = new HashMap<>(); bbMap.put("(\r\n|\r|\n|\n\r)", "<br/>"); bbMap.put("\\[b\\](.+?)\\[/b\\]", "<strong>$1</strong>"); bbMap.put("\\[i\\](.+?)\\[/i\\]", "<span style='font-style:italic;'>$1</span>"); bbMap.put("\\[u\\](.+?)\\[/u\\]", "<span style='text-decoration:underline;'>$1</span>"); bbMap.put("\\[h1\\](.+?)\\[/h1\\]", "<h1>$1</h1>"); bbMap.put("\\[h2\\](.+?)\\[/h2\\]", "<h2>$1</h2>"); bbMap.put("\\[h3\\](.+?)\\[/h3\\]", "<h3>$1</h3>"); bbMap.put("\\[h4\\](.+?)\\[/h4\\]", "<h4>$1</h4>"); bbMap.put("\\[h5\\](.+?)\\[/h5\\]", "<h5>$1</h5>"); bbMap.put("\\[h6\\](.+?)\\[/h6\\]", "<h6>$1</h6>"); bbMap.put("\\[quote\\](.+?)\\[/quote\\]", "<blockquote>$1</blockquote>"); bbMap.put("(?s)^\\[quote name=\"([^\"]+)\".*\\](.+)\\[\\/quote\\]", "<span style='font-style:italic;'>Citazione di: $1</span> <blockquote>$2</blockquote>"); bbMap.put("\\[p\\](.+?)\\[/p\\]", "<p>$1</p>"); bbMap.put("\\[p=(.+?),(.+?)\\](.+?)\\[/p\\]", "<p style='text-indent:$1px;line-height:$2%;'>$3</p>"); bbMap.put("\\[center\\](.+?)\\[/center\\]", "<div align='center'>$1"); bbMap.put("\\[align=(.+?)\\](.+?)\\[/align\\]", "<div align='$1'>$2"); bbMap.put("\\[color=(.+?)\\](.+?)\\[/color\\]", "<span style='color:$1;'>$2</span>"); bbMap.put("\\[size=(.+?)\\](.+?)\\[/size\\]", "<span style='font-size:$1;'>$2</span>"); bbMap.put("\\[img\\](.+?)\\[/img\\]", "<img src='$1' />"); bbMap.put("\\[img=(.+?),(.+?)\\](.+?)\\[/img\\]", "<img width='$1' height='$2' src='$3' />"); bbMap.put("\\[email\\](.+?)\\[/email\\]", "<a href='mailto:$1'>$1</a>"); bbMap.put("\\[email=(.+?)\\](.+?)\\[/email\\]", "<a href='mailto:$1'>$2</a>"); bbMap.put("\\[url\\](.+?)\\[/url\\]", "<a href='$1'>$1</a>"); bbMap.put("\\[url=(.+?)\\](.+?)\\[/url\\]", "<a href='$1'>$2</a>"); bbMap.put("\\[youtube\\](.+?)\\[/youtube\\]", "<object width='640' height='380'><param name='movie' value='http://www.youtube.com/v/$1'></param><embed src='http://www.youtube.com/v/$1' type='application/x-shockwave-flash' width='640' height='380'></embed></object>"); bbMap.put("\\[video\\](.+?)\\[/video\\]", "<video src='$1' />"); bbMap.put("\\[SPOILER\\](.+?)\\[\\/SPOILER\\]", "$1"); for (Map.Entry entry: bbMap.entrySet()) { html = html.replaceAll(entry.getKey().toString(), entry.getValue().toString()); } return html; } 

Tenga en cuenta que la fila SPOILER está allí sólo para fines de prueba.

A continuación, la cadena se establece en TextView en la clase Adapter:

 postText.setText(Html.fromHtml(post.getPostText())); 

El post tiene esta disposición:

 <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:id="@+id/postAuthor" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentLeft="true" android:layout_alignParentStart="true" android:layout_alignParentTop="true" android:layout_marginLeft="10dp" android:layout_marginTop="10dp" android:text="New Text" android:textStyle="bold" /> <TextView android:id="@+id/postDate" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignLeft="@+id/postAuthor" android:layout_alignStart="@+id/postAuthor" android:layout_below="@+id/postAuthor" android:layout_marginTop="5dp" android:text="New Text" android:textStyle="italic" /> <TextView android:id="@+id/postText" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignLeft="@+id/postDate" android:layout_alignStart="@+id/postDate" android:layout_below="@+id/postDate" android:layout_marginBottom="15dp" android:layout_marginTop="20dp" android:autoLink="web" android:linksClickable="true" android:text="New Text" /> </RelativeLayout> 

Cada objeto de publicación se coloca en un ListView.

Mi pregunta es: ¿cómo puedo crear un diseño similar, incluso con HTML o algún tipo de biblioteca externa?

Gracias.

TextView no admite TextView <span> , <object> ni <button> . Hay una lista no oficial de las etiquetas compatibles.

Para el [spoiler] bbcode, podría renderizar dos TextViews , una con, y otra sin el spoiler. A continuación, agregue un botón para alternar entre los dos.

Demostración en JavaScript:

 var spoilerVisible = false; $('#toggleSpoiler').on('click', function () { spoilerVisible = !spoilerVisible; $('#textview1').toggle(!spoilerVisible); $('#textview2').toggle(spoilerVisible); $('#toggleSpoiler').text(spoilerVisible ? 'Hide spoiler' : 'Show spoiler'); }); 
 <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <div id="textview1">This is a test.</div> <div id="textview2" style="display:none;">This is a test. Spoiled!</div> <button id="toggleSpoiler">Show spoiler</button> 

Después de horas, versiones de código y algunas maldiciones, he conseguido aclarar esto. Aquí está el código final, pero creo que todavía necesita algunas correcciones más para ser perfecto. (El mensaje que contiene el botón SPOILER desaparecerá si el usuario se desplaza al final de la lista.)

  String[] textArray = postText.split("<spoiler>"); Spanned spoilerText; String preSpoilerText = ""; String postSpoilerText = ""; List<String> noSpoilerTextList = new ArrayList<>(); for (int i = index - 1; i < textArray.length; i += 2) { if (!textArray[i].equals("</p>")) { String temp = textArray[i].replace("\n", ""); temp = temp.replace("[/SPOILER]", ""); if (!temp.equals("")) { noSpoilerTextList.add(temp); } } } List<String> spoilerNameList = new ArrayList<>(); List<String> spoilerTextList = new ArrayList<>(); for (int i = index; i < textArray.length; i += 2) { if (!textArray[i].contains("</p>") || !textArray[i].equals("")) { String temp = textArray[i].replace("\n", ""); temp = temp.replace("[SPOILER]", ""); if (!temp.equals("")) { if (temp.contains("<name>")) { String[] spoilerTextArray = temp.split("<name>"); spoilerNameList.add(spoilerTextArray[1]); spoilerTextList.add(spoilerTextArray[2]); } else { spoilerNameList.add("SPOILER"); spoilerTextList.add(temp); } } } } if (noSpoilerTextList.size() == spoilerTextList.size()) { noSpoilerTextList.add(new String("")); } for (int i = 0; i < spoilerTextList.size(); i++) { int buttonHeight = 0; float metrics = context.getResources().getDisplayMetrics().density; if (metrics == 3.0) { Log.i("DYSPLAYSIZE: ", "xxhdpi"); buttonHeight = 140; } else if (metrics == 2.0) { Log.i("DYSPLAYSIZE: ", "xhdpi"); buttonHeight = 120; } else if (metrics == 1.5) { Log.i("DYSPLAYSIZE: ", "hdpi"); buttonHeight = 100; } final TextView spoilerTextView = new TextView(context); TextView preSpoilerTextView = new TextView(context); TextView postSpoilerTextView = new TextView(context); spoilerTextView.setLinksClickable(true); preSpoilerTextView.setLinksClickable(true); postSpoilerTextView.setLinksClickable(true); spoilerTextView.setAutoLinkMask(Linkify.WEB_URLS); preSpoilerTextView.setAutoLinkMask(Linkify.WEB_URLS); postSpoilerTextView.setAutoLinkMask(Linkify.WEB_URLS); Button spoilerButton = new Button(context); spoilerButton.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, buttonHeight)); spoilerText = Html.fromHtml(spoilerTextList.get(i), new Html.ImageGetter() { @Override public Drawable getDrawable(String source) { LevelListDrawable d = new LevelListDrawable(); Drawable empty = context.getResources().getDrawable(R.drawable.abc_ab_share_pack_mtrl_alpha); d.addLevel(0, 0, empty); d.setBounds(0, 0, empty.getIntrinsicWidth(), empty.getIntrinsicHeight()); new ImageGetterAsyncTask(context, source, d).execute(spoilerTextView); return d; } }, null); spoilerTextView.setText(spoilerText); spoilerTextView.setTypeface(null, Typeface.ITALIC); spoilerTextView.setVisibility(View.GONE); LinearLayout.LayoutParams spoilerMargins = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT); spoilerMargins.setMargins(50, 5, 15, 15); final boolean[] visible = {false}; spoilerButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (!visible[0]) { spoilerTextView.setVisibility(View.VISIBLE); visible[0] = true; } else { spoilerTextView.setVisibility(View.GONE); visible[0] = false; } } }); if (index == 1) { preSpoilerText = noSpoilerTextList.get(0); postSpoilerText = noSpoilerTextList.get(1); preSpoilerTextView.setText(Html.fromHtml(preSpoilerText)); postSpoilerTextView.setText(Html.fromHtml(postSpoilerText)); postContent.addView(preSpoilerTextView); } else { if (i != spoilerTextList.size() - 1) { postSpoilerText = noSpoilerTextList.get(i + 1); postSpoilerTextView.setText(Html.fromHtml(postSpoilerText)); } else { if (spoilerTextList.size() != noSpoilerTextList.size()) { postSpoilerText = noSpoilerTextList.get(i + 1); } else { postSpoilerText = noSpoilerTextList.get(i); } postSpoilerTextView.setText(Html.fromHtml(postSpoilerText)); } } spoilerButton.setText(spoilerNameList.get(i)); postContent.addView(spoilerButton); postContent.addView(spoilerTextView, spoilerMargins); postContent.addView(postSpoilerTextView); 
  • Expresión regular en Android para el campo de contraseña
  • Java Regex lookahead toma demasiado tiempo
  • Cómo utilizar SpannableString con Regex en android?
  • ¿Cómo lograr límite de palabras en Sqlite Android?
  • ¿Cómo puedo eliminar el "+" y el código del país de un número de teléfono?
  • Java no funciona con regex \ s, dice: inválido secuencia de escape
  • ¿Hay una manera rápida de reconocer códigos HTML ASCII en una cadena o TextView?
  • Expresión regular de Android para obtener enlaces de la fuente de la página web
  • Cómo colorear texto utilizando Regex en android
  • Grupos java regex con android
  • Permitir sólo charcters seleccionados basados ​​en regex en un EditText
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.