Manejo de spoiler BBcode Android
Estoy desarrollando una aplicación para un foro.
Necesito conseguir un botón de spoiler como el de Tapatalk
- PHP - RegEx Números de teléfono con o sin código de país
- ReplaceAll no reemplaza string
- Android: ¿dividido en un salto de línea en String? (Párrafos)
- Palabra árabe Buscando en el archivo de texto en árabe
- cómo encontrar si el texto de entrada es una url usando regex
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.
- Regex para eliminar el número de pista del nombre de la canción?
- Editar texto aceptar sólo números no números decimales en android
- ¿Qué hace (?! A) {0}? Significa en un regex de Java?
- Divide la cadena en la última aparición del carácter
- Validación de la fecha de Regex para aaaa-mm-dd
- Android, String.split (String regex) no divide toda la cadena
- Cómo validar el formato de número de teléfono
- Android / Java: comprueba si url es url válida de youtube
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);
- En móvil, ¿cómo puedo obtener "enter" para enviar un formulario dentro de un tipo = "número" de entrada?
- ¿Por qué mi aplicación de Android utiliza CPU en segundo plano?