Git Product home page Git Product logo

android-rich-text-editor's Introduction

Download GitHub license

Android Rich text Editor (中文说明见这里)

If you are looking for a good rich text editor on Android, DON'T MISS THIS ONE!

And, I am open to work. Feel free to contact me at [email protected] if you have an opening. Thank you!

It's still in progress now, welcome fork and join me!

I published colorpicker and emojipicker as standalone components so they can be reused in other projects easily.

Hey guys! If you're reading this, I believe you are kind of struggling to find out some solution for rich text editor on Android, and specifically, you want to use the Android native APIs to implement. If I was right, maybe I can help. But starting from the very beginning of this project, I open-sourced all of the code, and have helped tens to even hundreds of friends out from their troubles, for free! Now I have to charge for the future helps, starting from $49 per feature implementation; $19 per feature contact. And the improvement will all be open-source to help others out. Reach out [email protected] for details. Thank you!

This is implemented by Java

image

Supported styles:

  • Bold
  • Italic
  • Underline
  • Strikethrough
  • Numeric list
  • Bullet list
  • Align left
  • Align center
  • Align right
  • Insert image
  • Background color
  • Hyper link
  • @
  • Quote
  • Foreground color
  • Emoji icon
  • Superscript
  • Subscript
  • Font size
  • Video
  • Image from internet
  • Dividing line
  • All styles support save as HTML file
  • Load from HTML then continue editing or displaying - New in 0.1.0

Released 0.2.0: migrated to AndroidX

  • Update Glide to 4.11.0
  • The last non-androidx version is 0.1.10

Released 0.1.8: added support for multiple ARE instances in one page

Released 0.1.7: update glide to 4.9.0

In 0.1.6

  • Maintain release, includes:
  • Added foreground color style to ARE_Toolbar_Default
  • Added background color style to ARE_Toolbar_Default
  • Bugs fixing

image

Plan for next version:
  • Undo
  • Redo

Let me know what features you want to have in the next version if there is any, thanks.

How ARE works

  • Two usage modes

    1. AREditor. EditText (the input area) and Toolbar (the styling tools) are in the same component. I.e.: if you include <com.chinalwb.are.AREditor .../> in layout XML, you'll see the input area and styling tools (Bold / Italic / Alignment / Bullet list / etc..) in your activity.

    2. AREditText + IARE_Toolbar (it's an interface). EditText (the input area) and Toolbar (the styling tools) are standalone component themselves. I.e.: you have the choice to decide whether to show the Toolbar, and where to place it, below or bottom of the AREditText, left or right to the AREditText, or any other alignment because the toolbar itself is a HorizontalScrollView (default implementation is ARE_ToolbarDefault), and also you can decide what toolitem to be added to the toolbar.

  • Inside these two modes:

    1. AREditor: it extends RelativeLayout, it contains two child components: AREditText and ARE_Toolbar.

      1. AREditText extends AppCompatEditText, in its afterTextChanged method of TextWatcher, it calls the applyStyle of IARE_Style.

      2. ARE_Toolbar extends LinearLayout, the tool items inside are IARE_Style instances, each instance contains an ImageView, which is being shown in the toolbar.

    2. AREditText + IARE_Toolbar.

      1. No need to repeat about AREditText

      2. IARE_Toolbar, it is an interface, which defines what a toolbar needs to implement. Such as: addToolbarItem, getToolItems, etc.. Inside are, there is a default implementation: ARE_ToolbarDefault, it extends HorizontalScrollView, and has the implementation of all the methods in the IARE_Toolbar interface.

      3. ARE_ToolbarDefault, it is just a toolbar, i.e.: tool item container, what the component does the real work is IARE_ToolItem, which is another interface defines the behaviors of a tool item should have, like: getView returns a view to be shown in the toolbar, getStyle returns an IARE_Style to response to the text change. Currently there are 19 toolitems implementation:

        1. ARE_ToolItem_Abstract ARE_ToolItem_AlignmentCenter ARE_ToolItem_AlignmentLeft ARE_ToolItem_AlignmentRight ARE_ToolItem_At ARE_ToolItem_Bold ARE_ToolItem_Hr ARE_ToolItem_Image ARE_ToolItem_Italic ARE_ToolItem_Link ARE_ToolItem_ListBullet ARE_ToolItem_ListNumber ARE_ToolItem_Quote ARE_ToolItem_Strikethrough ARE_ToolItem_Subscript ARE_ToolItem_Superscript ARE_ToolItem_Underline ARE_ToolItem_UpdaterDefault ARE_ToolItem_Video

        2. The above tool items can be added to toolbar by calling: addToolbarItem(IARE_ToolItem toolItem)

        3. If you want to add your own tool item, you just need to implement your IARE_ToolItem, for example, if you want to add a tool item to change font family, then you can define ARE_ToolItem_FontFamily and implements the methods in IARE_ToolItem. You can check out any of the above ToolItems as reference.

        4. Specially if you want to add a new feature like @, such as ##, check out ARE_ToolItem_At.java, which is a demo for tool item without an image at toolbar.

  • All styles are based on Android Spans

Integration

In your gradle.build of app module, add this in the dependencies:

    implementation 'com.github.bumptech.glide:glide:4.9.0'
    implementation 'com.github.chinalwb:are:0.1.7'

or, as are is still not stable enough to handle kinds of issues in different business scenarios, I'd like recommend to import are into your project directly and add it as a local module dependency.

	implementation project(':are')

Customization & Samples

Documentation for AREditor in layout XML

Name Format Description
expandMode enum FULL (default: Full screen editor) / MIN (min height editor, maxLines = 3)
hideToolbar boolean Whether to hide the toolbar, by default toolbar will be shown. You may want to set it as true when you use MIN expand mode, @feature will still be available but other features won't work because those styles on toolbar has been hidden with toolbar.
toolbarAlignment enum BOTTOM (default: at bottom of AREditor) / TOP (at top of AREditor)

APIs for AREditor in Java

Class Method Params Description
AREditor setExpandMode AREditor.ExpandMode Sets the edit area mode. Possible values are: ExpandMode.FULL (default) / ExpandMode.MIN
AREditor setHideToolbar boolean Sets as true to hide toolbar; sets false will show toolbar
AREditor setToolbarAlignment AREditor.ToolbarAlignment Sets the toolbar position. Possible values are: ToolbarAlignment.BOTTOM (default) / ToolbarAlignment.TOP

Samples for AREditor

XML:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:are="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@android:color/holo_blue_dark"
    >

    <TextView
        android:id="@+id/xView"
        android:layout_above="@+id/areditor"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_marginTop="1dp"
        android:background="@color/colorAccent"
        android:gravity="center"
        android:textSize="50sp"
        android:text="Your ListView may go here"
        />

    <com.chinalwb.are.AREditor
        android:id="@+id/areditor"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:background="@android:color/holo_green_dark"
        are:expandMode="MIN"
        are:hideToolbar="true"
        are:toolbarAlignment="TOP" />

</RelativeLayout>

Java:

AREditor arEditor = this.findViewById(R.id.areditor);
arEditor.setExpandMode(AREditor.ExpandMode.FULL);
arEditor.setHideToolbar(false);
arEditor.setToolbarAlignment(AREditor.ToolbarAlignment.BOTTOM);

==========

AREditText it self is an AppCompatEditText subclass, so anything applies to AppCompatEditText also works for AREditText.

==========

Documentation for ARE_ToolbarDefault

It extends HorizontalScrollView, so anything applies to HorizontalScrollView also works for ARE_ToolbarDefault.

APIs for IARE_Toolbar

Class Method Params Description
IARE_Toolbar addToolbarItem IARE_ToolItem Add a toolbar item to toolbar
IARE_Toolbar getToolItems -none- Returns all of the tool items in the toolbar
IARE_Toolbar setEditText AREditText Binds AREditText with toolbar
IARE_Toolbar getEditText -none- Returns the bound AREditText
IARE_Toolbar onActivityResult requestCode(int) resultCode(int) data(Intent) For some styles like insert image or video or @ feature, you need open a new Activity, and need to handle the data via onActivityResult, in this method you can dispatch to the specific styles.

APIs for IARE_ToolItem

Class Method Params Description
IARE_ToolItem getStyle -none- Each tool item is a style, and a style combines with specific span.
IARE_ToolItem getView Context Each tool item has a view. If context is null, return the generated view.
IARE_ToolItem onSelectionChanged int selStart, int selEnd Selection changed call back. Update tool item checked status
IARE_ToolItem getToolbar -none- Returns the toolbar of this tool item.
IARE_ToolItem setToolbar IARE_Toolbar Sets the toolbar for this tool item.
IARE_ToolItem getToolItemUpdater -none- Gets the tool item updater instance, will be called when style being checked and unchecked.
IARE_ToolItem setToolItemUpdater IARE_ToolItem_Updater Sets the tool item updater.
IARE_ToolItem onActivityResult requestCode(int) resultCode(int) data(Intent) Handle the dispatched event from IARE_Toolbar

Samples:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:are="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@android:color/holo_blue_dark">

    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_above="@+id/bottombar"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true"
        android:background="@android:color/white">

        <com.chinalwb.are.AREditText
            android:id="@+id/arEditText"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:gravity="top|left"
            android:hint="Your EditText goes here"
            android:textSize="50sp" />
    </ScrollView>

    <LinearLayout
        android:id="@+id/bottombar"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:layout_alignParentBottom="true"
        android:orientation="horizontal"
        android:weightSum="1000">

        <com.chinalwb.are.styles.toolbar.ARE_ToolbarDefault
            android:id="@+id/areToolbar"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="900"
            android:background="@color/colorPrimary"
            android:gravity="center_vertical" />

        <View
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="5"
            android:background="@color/colorPrimaryDark" />

        <LinearLayout
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="95"
            android:background="@color/colorAccent"
            android:gravity="center">

            <ImageView
                android:id="@+id/arrow"
                android:layout_width="30dp"
                android:layout_height="30dp"
                android:src="@drawable/arrow_right" />
        </LinearLayout>
    </LinearLayout>
</RelativeLayout>

Sample in Java for setting up custom toolbar

public class ARE_DefaultToolbarActivity extends AppCompatActivity {

    private IARE_Toolbar mToolbar;

    private AREditText mEditText;

    private boolean scrollerAtEnd;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_are__default_toolbar);

        initToolbar();
    }


    private void initToolbar() {
        mToolbar = this.findViewById(R.id.areToolbar);
        IARE_ToolItem bold = new ARE_ToolItem_Bold();
        IARE_ToolItem italic = new ARE_ToolItem_Italic();
        IARE_ToolItem underline = new ARE_ToolItem_Underline();
        IARE_ToolItem strikethrough = new ARE_ToolItem_Strikethrough();
        IARE_ToolItem quote = new ARE_ToolItem_Quote();
        IARE_ToolItem listNumber = new ARE_ToolItem_ListNumber();
        IARE_ToolItem listBullet = new ARE_ToolItem_ListBullet();
        IARE_ToolItem hr = new ARE_ToolItem_Hr();
        IARE_ToolItem link = new ARE_ToolItem_Link();
        IARE_ToolItem subscript = new ARE_ToolItem_Subscript();
        IARE_ToolItem superscript = new ARE_ToolItem_Superscript();
        IARE_ToolItem left = new ARE_ToolItem_AlignmentLeft();
        IARE_ToolItem center = new ARE_ToolItem_AlignmentCenter();
        IARE_ToolItem right = new ARE_ToolItem_AlignmentRight();
        IARE_ToolItem image = new ARE_ToolItem_Image();
        IARE_ToolItem video = new ARE_ToolItem_Video();
        IARE_ToolItem at = new ARE_ToolItem_At();
        mToolbar.addToolbarItem(bold);
        mToolbar.addToolbarItem(italic);
        mToolbar.addToolbarItem(underline);
        mToolbar.addToolbarItem(strikethrough);
        mToolbar.addToolbarItem(quote);
        mToolbar.addToolbarItem(listNumber);
        mToolbar.addToolbarItem(listBullet);
        mToolbar.addToolbarItem(hr);
        mToolbar.addToolbarItem(link);
        mToolbar.addToolbarItem(subscript);
        mToolbar.addToolbarItem(superscript);
        mToolbar.addToolbarItem(left);
        mToolbar.addToolbarItem(center);
        mToolbar.addToolbarItem(right);
        mToolbar.addToolbarItem(image);
        mToolbar.addToolbarItem(video);
        mToolbar.addToolbarItem(at);

        mEditText = this.findViewById(R.id.arEditText);
        mEditText.setToolbar(mToolbar);

        setHtml();

        initToolbarArrow();
    }

    private void setHtml() {
        String html = "<p style=\"text-align: center;\"><strong>New Feature in 0.1.2</strong></p>\n" +
                "<p style=\"text-align: center;\">&nbsp;</p>\n" +
                "<p style=\"text-align: left;\"><span style=\"color: #3366ff;\">In this release, you have a new usage with ARE.</span></p>\n" +
                "<p style=\"text-align: left;\">&nbsp;</p>\n" +
                "<p style=\"text-align: left;\"><span style=\"color: #3366ff;\">AREditText + ARE_Toolbar, you are now able to control the position of the input area and where to put the toolbar at and, what ToolItems you'd like to have in the toolbar. </span></p>\n" +
                "<p style=\"text-align: left;\">&nbsp;</p>\n" +
                "<p style=\"text-align: left;\"><span style=\"color: #3366ff;\">You can not only define the Toolbar (and it's style), you can also add your own ARE_ToolItem with your style into ARE.</span></p>\n" +
                "<p style=\"text-align: left;\">&nbsp;</p>\n" +
                "<p style=\"text-align: left;\"><span style=\"color: #ff00ff;\"><em><strong>Why not give it a try now?</strong></em></span></p>";
        mEditText.fromHtml(html);
    }

    private void initToolbarArrow() {
        final ImageView imageView = this.findViewById(R.id.arrow);
        if (this.mToolbar instanceof ARE_ToolbarDefault) {
            ((ARE_ToolbarDefault) mToolbar).getViewTreeObserver().addOnScrollChangedListener(new ViewTreeObserver.OnScrollChangedListener() {
                @Override
                public void onScrollChanged() {
                    int scrollX = ((ARE_ToolbarDefault) mToolbar).getScrollX();
                    int scrollWidth = ((ARE_ToolbarDefault) mToolbar).getWidth();
                    int fullWidth = ((ARE_ToolbarDefault) mToolbar).getChildAt(0).getWidth();

                    if (scrollX + scrollWidth < fullWidth) {
                        imageView.setImageResource(R.drawable.arrow_right);
                        scrollerAtEnd = false;
                    } else {
                        imageView.setImageResource(R.drawable.arrow_left);
                        scrollerAtEnd = true;
                    }
                }
            });
        }

        imageView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if (scrollerAtEnd) {
                    ((ARE_ToolbarDefault) mToolbar).smoothScrollBy(-Integer.MAX_VALUE, 0);
                    scrollerAtEnd = false;
                } else {
                    int hsWidth = ((ARE_ToolbarDefault) mToolbar).getChildAt(0).getWidth();
                    ((ARE_ToolbarDefault) mToolbar).smoothScrollBy(hsWidth, 0);
                    scrollerAtEnd = true;
                }
            }
        });
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        int menuId = item.getItemId();
        if (menuId == com.chinalwb.are.R.id.action_save) {
            String html = this.mEditText.getHtml();
            DemoUtil.saveHtml(this, html);
            return true;
        }
        if (menuId == R.id.action_show_tv) {
            String html = this.mEditText.getHtml();
            Intent intent = new Intent(this, TextViewActivity.class);
            intent.putExtra(HTML_TEXT, html);
            startActivity(intent);
            return true;
        }
        return super.onOptionsItemSelected(item);
    }


    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        mToolbar.onActivityResult(requestCode, resultCode, data);
    }
}

APIs for AREditor

Class Method Params Description
AREditor fromHtml String Load html to AREditor
AREditor getHtml -none- Returns the HTML source of current content in AREditor
AREditor getARE -none- Returns the AREditText instance in this AREditor

APIs for AREditText

Class Method Params Description
AREditText fromHtml -none- Load html to AREditor
AREditText getHtml -none- Returns the HTML source of current content in AREditor
AREditText setToolbar IARE_Toolbar Sets the IARE_Toolbar instance (only necessary when works as separated component and works together with custom toolbar)

Available features demo:

image

image

In progress items:

  • Audio
  • Font family
  • Indent right
  • Indent left
  • Save editings to local SQLite
  • Notes list
  • Headline - deferred, can be done with font size and center style

You can download the APK here:

Click ARE_20190524_0.1.6.apk to download

Known issues:

  • Background color - cursor invisible when put it in the range of BackgroundColorSpan

Thanks @Yasujizr providing the logo for ARE. @Yasujizr I hope to get a new logo now :) Could you please help me?


If you find my work is helpful to you or you are start using my code, you don't need to buy me a coffee, just could you please send me a "✨"? Your * encourages me to make more features open source, thanks for your support. You can contact me at [email protected] if you need any customization or any suggestion.

android-rich-text-editor's People

Contributors

chinalwb avatar kant avatar lfork avatar linxueyuanstdio avatar starshipcoder avatar tejnote avatar vishalx4 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

android-rich-text-editor's Issues

希望能够加入再次编辑功能

作为富文本编辑器,肯定需要有对文本进行二次展示和编辑的需求,目前虽然能够通过edittext简单展示,但是基本上不能再次编辑,希望可以加入这个功能。
我个人目前的想法是通过解析HTML然后在根据相应的标签调用不同控件实现(想法),不知道还有没有更好的方案。
富文本编辑器看了好多都是基于webview实现的。还有不少需要选中文本才能实现更改样式,这个非常不错,希望能够保持更新。

Change font size incorrect

I see that you are setting default font size is 18.
When I change the font size to other value (for example 24) and write some text. If I back to font size 18, all of the text before also change the font size to 18

How to Add Toolbar Item Emoji and Font Foregound/Background Color in ARE_ToolbarDefault

Hello guys,
This library is very cool, but how to apply add Toolbar Item Emoji and Font Foregound/Background Color in ARE_ToolbarDefault ?


    private void initToolbar() {
        IARE_ToolItem bold = new ARE_ToolItem_Bold();
        IARE_ToolItem italic = new ARE_ToolItem_Italic();
        IARE_ToolItem underline = new ARE_ToolItem_Underline();
        IARE_ToolItem strikethrough = new ARE_ToolItem_Strikethrough();
        ARE_ToolItem_FontSize fontSize = new ARE_ToolItem_FontSize();
        IARE_ToolItem quote = new ARE_ToolItem_Quote();
        IARE_ToolItem listNumber = new ARE_ToolItem_ListNumber();
        IARE_ToolItem listBullet = new ARE_ToolItem_ListBullet();
        IARE_ToolItem hr = new ARE_ToolItem_Hr();
        IARE_ToolItem link = new ARE_ToolItem_Link();
        IARE_ToolItem subscript = new ARE_ToolItem_Subscript();
        IARE_ToolItem superscript = new ARE_ToolItem_Superscript();
        IARE_ToolItem left = new ARE_ToolItem_AlignmentLeft();
        IARE_ToolItem center = new ARE_ToolItem_AlignmentCenter();
        IARE_ToolItem right = new ARE_ToolItem_AlignmentRight();
        IARE_ToolItem image = new ARE_ToolItem_Image();
        areToolbar.addToolbarItem(bold);
        areToolbar.addToolbarItem(italic);
        areToolbar.addToolbarItem(underline);
        areToolbar.addToolbarItem(strikethrough);
        areToolbar.addToolbarItem(quote);
        areToolbar.addToolbarItem(fontSize);
        areToolbar.addToolbarItem(listNumber);
        areToolbar.addToolbarItem(listBullet);
        areToolbar.addToolbarItem(hr);
        areToolbar.addToolbarItem(link);
        areToolbar.addToolbarItem(subscript);
        areToolbar.addToolbarItem(superscript);
        areToolbar.addToolbarItem(left);
        areToolbar.addToolbarItem(center);
        areToolbar.addToolbarItem(right);
        areToolbar.addToolbarItem(image);

        arEditText.setToolbar(areToolbar);

        setHtml();

        initToolbarArrow();
    }

Above code just implement some toolbar item , i cant find how to add Toolbar Item Emoji and Font Foregound/Background Color.
Please Advice ..
Thanks

New Design

android rich text

Hi,

I have studied your work and I think a new design will make you more visible. I designed for you, I hope you like it and you want to use it. If you want to reach me, you can look at the my profile. I can give you all the formats of the design free. If you want a change please specify.

Andorid in A and Fountain Pen combine

怎么解决java.lang.RuntimeException: Unable to start activity ComponentInfo{}

我的project原本就有使用glide了, 但是implemented 你的library 后就有这个package 不一样导致的问题。请问怎么解决?
java.lang.RuntimeException: Unable to start activity ComponentInfo{}: java.lang.ClassCastException: com.chinalwb.are.glidesupport.GlideRequests cannot be cast to com.xxxx.xxx.xxx.GlideRequests

有一个闪退bug

在选中图片后有闪退,第一张没问题,加载第二或第三张就闪退了。我觉得是不是控件内存消耗太大导致内存溢出

More features for Default Toolbar

I think AREditText + Default Toolbar are very flexibility. However, Default Toolbar now have less feature than full AREditor. I really want to you add more features. I know you are busy now but I hope you can complete these in the near future. I also try to custom own my toolbar but that seem quite difficult for me :)

图片URI问题

在高版本系统中直接读 URI 会崩溃
比如这样的 URI

content://media/external/images/media/846589
content://com.android.providers.media.documents/document/image%3A283030

事实上用 Glide 是可以在 AreImageGetter 读到图片并加载成功的,但是插入ImageSpan 的时候又不用 Glide 了,搞成直接加载 Uri:

// Html.java
package com.chinalwb.are.android.inner;
...
// 1309 行 startImg(Editable text, Attributes attributes, Html.ImageGetter img)方法片段
        String src = attributes.getValue("", "src");
        Drawable d = null;
        ImageSpan imageSpan = null;
        if (img != null) {
            d = img.getDrawable(src);
            if (src.startsWith(Constants.EMOJI)) {
                String resIdStr = src.substring(6);
                int resId = Integer.parseInt(resIdStr);
                imageSpan = new AreImageSpan(sContext, resId);
            } else if (src.startsWith("http")) {
                imageSpan = new AreImageSpan(sContext, d, src);
            } else {
                // content://com.android.providers.media.documents/document/image%3A33
                // Such uri cannot be loaded from AreImageGetter.
                imageSpan = new AreImageSpan(sContext, Uri.parse(src));
            }
        }

这里是直接搞进 Uri 了 imageSpan = new AreImageSpan(sContext, Uri.parse(src));
很奇怪为什么要这样做。。。
然后 AreImageGetter 成功读到的 Drawable 转成 Bitmap 又会出错。。。
所以我不得不自己修改了 Html.java

    static ImageSpan imageSpan = null;

    private static void startImg(Editable text, Attributes attributes, Html.ImageGetter img) {
        String src = attributes.getValue("", "src");
        Drawable d = null;
        if (img != null) {
            d = img.getDrawable(src);
            if (src.startsWith(Constants.EMOJI)) {
                String resIdStr = src.substring(6);
                int resId = Integer.parseInt(resIdStr);
                imageSpan = new AreImageSpan(sContext, resId);
            } else if (src.startsWith("http")) {
                imageSpan = new AreImageSpan(sContext, d, src);
            } else {
                // content://com.android.providers.media.documents/document/image%3A33
                // Such uri cannot be loaded from AreImageGetter.
                Uri uri = Uri.parse(src);
                String path = PictureHelper.getPath(sContext, uri);
                if (path != null) {
                    File file = new File(path);
                    Glide.with(sContext)
                            .asBitmap()
                            .load(file)
                            .into(new SimpleTarget<Bitmap>() {
                                @Override
                                public void onResourceReady(@NonNull Bitmap resource, Transition<? super Bitmap> transition) {
                                    Bitmap bitmap = scaleBitmapToFitWidth(resource,
                                            (int) (0.8 * ScreenUtil.getScreenWidth(sContext)));
                                    imageSpan = new ImageSpan(sContext, bitmap);
                                }
                            });
                }
            }
        }

        if (d == null) {
            if (sContext == null) {
                d = Resources.getSystem().getDrawable(R.drawable.ic_launcher);
            } else {
                d = sContext.getResources().getDrawable(R.drawable.ic_launcher);
            }

            d.setBounds(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight());
        }

        int len = text.length();
        text.append("\uFFFC");

        text.setSpan(imageSpan, len, text.length(),
                Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
    }

    public static Bitmap scaleBitmapToFitWidth(Bitmap bitmap, int maxWidth) {
        int w = bitmap.getWidth();
        int h = bitmap.getHeight();
        int newWidth = maxWidth;
        int newHeight = maxWidth * h / w;
        Matrix matrix = new Matrix();
        float scaleWidth = ((float) newWidth / w);
        float scaleHeight = ((float) newHeight / h);
        if (w < maxWidth * 0.2) {
            return bitmap;
        }
        matrix.postScale(scaleWidth, scaleHeight);
        return Bitmap.createBitmap(bitmap, 0, 0, w, h, matrix, true);
    }

    /**
     * 获取屏幕宽度 单位:像素
     *
     * @return 屏幕宽度
     */
    public static int getScreenWidth(Context context) {
        DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
        return displayMetrics.widthPixels;
    }
//  PictureHelper .java
// 解析 Uri 成 filepath 字符串
public class PictureHelper {

    // get the absolute path from the uri
    @SuppressLint("NewApi")
    public static String getPath(final Context context, final Uri uri) {
        final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
        // DocumentProvider
        if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {
            // ExternalStorageProvider
            if (isExternalStorageDocument(uri)) {
                final String docId = DocumentsContract.getDocumentId(uri);
                final String[] split = docId.split(":");
                final String type = split[0];
                if ("primary".equalsIgnoreCase(type)) {
                    return Environment.getExternalStorageDirectory() + "/"
                            + split[1];
                }
                // TODO handle non-primary volumes
            }
            // DownloadsProvider
            else if (isDownloadsDocument(uri)) {
                final String id = DocumentsContract.getDocumentId(uri);
                final Uri contentUri = ContentUris.withAppendedId(
                        Uri.parse("content://downloads/public_downloads"),
                        Long.valueOf(id));
                return getDataColumn(context, contentUri, null, null);
            }
            // MediaProvider
            else if (isMediaDocument(uri)) {
                final String docId = DocumentsContract.getDocumentId(uri);
                final String[] split = docId.split(":");
                final String type = split[0];
                Uri contentUri = null;
                if ("image".equals(type)) {
                    contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
                } else if ("video".equals(type)) {
                    contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
                } else if ("audio".equals(type)) {
                    contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
                }
                final String selection = "_id=?";
                final String[] selectionArgs = new String[] { split[1] };
                return getDataColumn(context, contentUri, selection,
                        selectionArgs);
            }
        }
        // MediaStore (and general)
        else if ("content".equalsIgnoreCase(uri.getScheme())) {
            // Return the remote address
            if (isGooglePhotosUri(uri))
                return uri.getLastPathSegment();
            return getDataColumn(context, uri, null, null);
        }
        // File
        else if ("file".equalsIgnoreCase(uri.getScheme())) {
            return uri.getPath();
        }
        return null;
    }


    public static String getDataColumn(Context context, Uri uri,
                                       String selection, String[] selectionArgs) {
        Cursor cursor = null;
        final String column = "_data";
        final String[] projection = { column };
        try {
            cursor = context.getContentResolver().query(uri, projection,
                    selection, selectionArgs, null);
            if (cursor != null && cursor.moveToFirst()) {
                final int index = cursor.getColumnIndexOrThrow(column);
                return cursor.getString(index);
            }
        } finally {
            if (cursor != null)
                cursor.close();
        }
        return null;
    }


    public static boolean isExternalStorageDocument(Uri uri) {
        return "com.android.externalstorage.documents".equals(uri
                .getAuthority());
    }


    public static boolean isDownloadsDocument(Uri uri) {
        return "com.android.providers.downloads.documents".equals(uri
                .getAuthority());
    }


    public static boolean isMediaDocument(Uri uri) {
        return "com.android.providers.media.documents".equals(uri
                .getAuthority());
    }


    private static boolean isGooglePhotosUri(Uri uri) {
        return "com.google.android.apps.photos.content".equals(uri
                .getAuthority());
    }

}

貌似在 Android 5.0 以后是要 ACTION_OPEN_DOCUMENT 这些才能从 Uri 拿图片,很麻烦。
我建议不要搞 Uri 了,直接面向文件多好,统一管理资源文件,像图片、视频这些,如果不是 url ,备份起来很麻烦了。

然后还有一些问题。
比如插入录音等等,希望有一个统一的 API 去做这些事

  1. 插入 Bitmap 的大小宽高
  2. 点击插入的 span 回调

feature request

  1. AREToolbarAREditext 分开,它们耦合太严重了。现在编辑器的布局是写死的,如果想移动 toolbar 到别的位置 或者 自定义键盘操作会很麻烦。
  2. emoji键盘里引用的图片资源会静态打包到apk里,但是像表情包这些,应该通过网络或其他途径加载。现在引入项目的话导致apk太大。
  3. 还是emoiji键盘的问题,我想抽象一下,可以绑定其他自定义键盘。参考time cat里的做法:
    /**
     * 设置软键盘和选择面板的平滑交互
     */
    private void setKeyboardManager() {
        mSmartKeyboardManager = new SmartKeyboardManager.Builder(this)
                .setContentView(dialog_add_task_ll_content)
                .setEditText(dialog_add_task_et_content)
                .addKeyboard(dialog_add_task_tv_important_urgent, dialog_add_task_select_ll_important_urgent)
                .addKeyboard(dialog_add_task_tv_date, dialog_add_task_select_gv_date)
                .addKeyboard(dialog_add_task_tv_time, dialog_add_task_select_ll_time)
                .addKeyboard(dialog_add_task_tv_remind, dialog_add_task_select_sv_container)
                .addKeyboard(dialog_add_task_tv_tag, dialog_add_task_select_ll_tag)
                .addKeyboard(dialog_add_task_tv_notebook, dialog_add_task_select_ll_notebook)
                .addKeyboard(dialog_add_task_tv_plan, dialog_add_task_select_ll_plan)
                .create();
    }

让toolbar暴露类似void addTriggerButton(View view)的接口,允许自定义添加按钮

发现严重的闪退BUG

文字在使用多种格式(例如:下划线,删除线,颜色)之后,在不换行情况下,直接导入图片,再次输入文字会闪退。
FULL TOP模式下

image

另外。添加图片之后,添加第二张或者第三张偶尔会出现闪退。

最后希望大神能出个保存草稿箱的功能,即点击按钮后,退出依然可以看到该文章

find the image button id

when I clicked on image button, it redirects to radio button. but i have to find onclicklistener of image button

GlideSupport

Where are the GlideApp and GlideRequests class ?

Bold Italic and underline not working

Hello,
First of all amazing efforts on the tool.

I am trying to use the tool and it works fine for my requirement. The issue is when I select either bold/italic/underline, there is not change in the text I select or the the text I type after selecting these options. Rest all of the stylings work fine. Do I need to add something else to it?

image not save

try to insert image, in edit mode, image is show. but when it save. no image url in html.

问个简单问题

在哪里可以修改默认的字体大小?当是纯文字时,在哪里设置fontsize 属性。我知道在html.java中,怕改错里面逻辑了

这个库很棒

theme color change

After using your library in my app, my app theme color colour changing. so,what should i do for not changing the colour?

新功能,视频上传

你好,您能否支持和知乎一样的添加视频上传功能,我想这会是个很强大的编辑器

Not update repo in my project

implementation 'com.github.bumptech.glide:glide:4.3.1'
implementation 'com.github.chinalwb:are:0.1.2'

I am used this, but you have done the update of color is not working in my app(project).So what I have to do for that.

0.1.2 版 glide 冲突...

AGPBI: {"kind":"error","text":"Program type already present: com.bumptech.glide.GeneratedRequestManagerFactory","sources":[{}],"tool":"D8"}

隐藏不需要的按钮

如题 想隐藏不需要的功能按钮 只能修改源码了吗 或许有开放api会灵活一点?

Callback for image upload

I like this rte. One thing that is stopping me from fully trying this out is the image upload functionality. Such that, if I upload an image to the server I get an image url(server location), then I want to insert the image using this server image url. Do you have any way to achieve this functionality? Currently there doesn't seem to be any callback/method to achieve this.

Force closed ARE app

Some time, ARE app was forced close.
Below is error log I got:
08-06 14:57:07.079 6261 6261 E AndroidRuntime: FATAL EXCEPTION: main 08-06 14:57:07.079 6261 6261 E AndroidRuntime: Process: com.chinalwb.are.demo, PID: 6261 08-06 14:57:07.079 6261 6261 E AndroidRuntime: java.lang.IndexOutOfBoundsException: setSpan (-1 ... -1) starts before 0 08-06 14:57:07.079 6261 6261 E AndroidRuntime: at android.text.SpannableStringBuilder.checkRange(SpannableStringBuilder.java:1314) 08-06 14:57:07.079 6261 6261 E AndroidRuntime: at android.text.SpannableStringBuilder.setSpan(SpannableStringBuilder.java:680) 08-06 14:57:07.079 6261 6261 E AndroidRuntime: at android.text.SpannableStringBuilder.setSpan(SpannableStringBuilder.java:672) 08-06 14:57:07.079 6261 6261 E AndroidRuntime: at com.chinalwb.are.styles.ARE_ListBullet.changeListNumberSpanToListBulletSpan(ARE_ListBullet.java:462) 08-06 14:57:07.079 6261 6261 E AndroidRuntime: at com.chinalwb.are.styles.ARE_ListBullet.access$000(ARE_ListBullet.java:22) 08-06 14:57:07.079 6261 6261 E AndroidRuntime: at com.chinalwb.are.styles.ARE_ListBullet$1.onClick(ARE_ListBullet.java:68) 08-06 14:57:07.079 6261 6261 E AndroidRuntime: at android.view.View.performClick(View.java:6897) 08-06 14:57:07.079 6261 6261 E AndroidRuntime: at android.view.View$PerformClick.run(View.java:26100) 08-06 14:57:07.079 6261 6261 E AndroidRuntime: at android.os.Handler.handleCallback(Handler.java:789) 08-06 14:57:07.079 6261 6261 E AndroidRuntime: at android.os.Handler.dispatchMessage(Handler.java:98) 08-06 14:57:07.079 6261 6261 E AndroidRuntime: at android.os.Looper.loop(Looper.java:164) 08-06 14:57:07.079 6261 6261 E AndroidRuntime: at android.app.ActivityThread.main(ActivityThread.java:6944) 08-06 14:57:07.079 6261 6261 E AndroidRuntime: at java.lang.reflect.Method.invoke(Native Method) 08-06 14:57:07.079 6261 6261 E AndroidRuntime: at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:327) 08-06 14:57:07.079 6261 6261 E AndroidRuntime: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1374)

I don't know exactly when error occur. However, for example, you can reproduce the issue as following:
1> Build ARE app -> run on real device
2> Select all -> remove
3> Use ListNumber to write some list
4> Click ListBullet -> app FC

Copy Paste removes formatting

Hi

I added some bullet text in the editor and then tried to cut all the content and then paste it, now pasted content doesn't show bulleted list. Can you guide me where to fix this?

Thanks

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.