处理剧透BBcode Android

时间:2015-09-11 23:44:20

标签: android html regex bbcode

我正在为论坛开发一个应用程序。

我需要像Tapatalk的那样获得一个剧透按钮

enter image description here

隐藏文本部分,仅在用户点击按钮时显示。

我收到了帖子的所有文字,包括剧透BBCode。我已设法通过以下正则表达式获取剧透的内容:

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

我的问题是我想添加一个剧透按钮,但我的所有文本都是类似HTML的,因为每个对象(图像,链接,格式代码等等)都是用HTML翻译的,然后由Android处理方法 Html.fromHtml()

这是解析方法,"翻译"将BBCode转换为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;
}

请注意,SPOILER行仅用于测试目的。

然后在Adapter类的TextView中设置字符串:

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

帖子有这样的布局:

<?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>

每个帖子对象都放在ListView中。

我的问题是:如何使用HTML或某种外部库创建类似的布局?

感谢。

2 个答案:

答案 0 :(得分:0)

TextView不支持<span><object><button>标记。有unofficial list支持的代码。

对于[spoiler] bbcode,您可以呈现两个TextViews,一个带有,而一个没有扰流板。然后添加一个按钮以在两者之间切换。

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>

答案 1 :(得分:0)

经过几个小时,代码版本和一些诅咒,我设法得到了这个。这是最终的代码,但我认为它仍然需要更多的修复才能完美。 (如果用户将滚动到列表底部,则包含SPOILER按钮的帖子将消失。)

        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);