├── .gitignore ├── .idea ├── .name ├── codeStyles │ └── Project.xml ├── dbnavigator.xml ├── gradle.xml ├── misc.xml ├── runConfigurations.xml └── vcs.xml ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── leo │ │ └── copytoutiao │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── assets │ │ ├── editor.html │ │ ├── normalize.css │ │ ├── rich_editor.js │ │ └── style.css │ ├── java │ │ └── com │ │ │ └── leo │ │ │ └── copytoutiao │ │ │ ├── MainActivity.java │ │ │ ├── activity │ │ │ ├── PublishActivity.java │ │ │ └── ShowArtActivity.java │ │ │ ├── utils │ │ │ ├── KeyBoardUtils.java │ │ │ ├── RichUtils.java │ │ │ ├── Utils.java │ │ │ └── popup │ │ │ │ ├── CommonPopupWindow.java │ │ │ │ └── PopupController.java │ │ │ └── view │ │ │ └── RichEditor.java │ └── res │ │ ├── anim │ │ ├── translate_pop_in.xml │ │ └── translate_pop_out.xml │ │ ├── color │ │ └── text_newapp_publish.xml │ │ ├── drawable-v24 │ │ └── ic_launcher_foreground.xml │ │ ├── drawable │ │ ├── bg_dotted_line.xml │ │ ├── ic_launcher_background.xml │ │ └── shape_white_top.xml │ │ ├── layout │ │ ├── activity_main.xml │ │ ├── activity_publish.xml │ │ ├── activity_show_art.xml │ │ └── newapp_pop_picture.xml │ │ ├── menu │ │ └── menu_.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── bold.png │ │ ├── bold_.png │ │ ├── ic_launcher.png │ │ ├── ic_launcher_round.png │ │ ├── list_ol.png │ │ ├── list_ol_.png │ │ ├── list_ul.png │ │ ├── list_ul_.png │ │ ├── picture_icon.png │ │ ├── underline.png │ │ └── underline_.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_round.png │ │ ├── rich_do.png │ │ └── rich_undo.png │ │ ├── values-sw1024dp │ │ └── dimens.xml │ │ ├── values-sw1280dp │ │ └── dimens.xml │ │ ├── values-sw1365dp │ │ └── dimens.xml │ │ ├── values-sw320dp │ │ └── dimens.xml │ │ ├── values-sw360dp │ │ └── dimens.xml │ │ ├── values-sw375dp │ │ └── dimens.xml │ │ ├── values-sw384dp │ │ └── dimens.xml │ │ ├── values-sw392dp │ │ └── dimens.xml │ │ ├── values-sw400dp │ │ └── dimens.xml │ │ ├── values-sw410dp │ │ └── dimens.xml │ │ ├── values-sw411dp │ │ └── dimens.xml │ │ ├── values-sw432dp │ │ └── dimens.xml │ │ ├── values-sw480dp │ │ └── dimens.xml │ │ ├── values-sw533dp │ │ └── dimens.xml │ │ ├── values-sw592dp │ │ └── dimens.xml │ │ ├── values-sw600dp │ │ └── dimens.xml │ │ ├── values-sw640dp │ │ └── dimens.xml │ │ ├── values-sw662dp │ │ └── dimens.xml │ │ ├── values-sw720dp │ │ └── dimens.xml │ │ ├── values-sw768dp │ │ └── dimens.xml │ │ ├── values-sw800dp │ │ └── dimens.xml │ │ ├── values-sw811dp │ │ └── dimens.xml │ │ ├── values-sw820dp │ │ └── dimens.xml │ │ ├── values-sw960dp │ │ └── dimens.xml │ │ ├── values-sw961dp │ │ └── dimens.xml │ │ └── values │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── leo │ └── copytoutiao │ └── ExampleUnitTest.java ├── build.gradle ├── gif ├── richEditText1.gif ├── richEditText2.gif ├── richEditText3.gif ├── richEditText4.gif ├── richEditText5.gif ├── richEditText6.gif ├── richEditText7.gif └── wx.png ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── imagepicker ├── .gitattributes ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── lzy │ │ └── imagepicker │ │ ├── DataHolder.java │ │ ├── ImageDataSource.java │ │ ├── ImagePicker.java │ │ ├── ImagePickerProvider.java │ │ ├── adapter │ │ ├── ImageFolderAdapter.java │ │ ├── ImagePageAdapter.java │ │ └── ImageRecyclerAdapter.java │ │ ├── bean │ │ ├── ImageFolder.java │ │ └── ImageItem.java │ │ ├── loader │ │ ├── GlideImageLoader.java │ │ └── ImageLoader.java │ │ ├── ui │ │ ├── ImageBaseActivity.java │ │ ├── ImageCropActivity.java │ │ ├── ImageGridActivity.java │ │ ├── ImagePreviewActivity.java │ │ ├── ImagePreviewBaseActivity.java │ │ └── ImagePreviewDelActivity.java │ │ ├── util │ │ ├── BitmapUtil.java │ │ ├── NavigationBarChangeListener.java │ │ ├── ProviderUtil.java │ │ └── Utils.java │ │ └── view │ │ ├── CropImageView.java │ │ ├── FolderPopUpWindow.java │ │ ├── GridSpacingItemDecoration.java │ │ ├── SuperCheckBox.java │ │ ├── SystemBarTintManager.java │ │ └── ViewPagerFixed.java │ └── res │ ├── anim │ ├── fade_in.xml │ ├── fade_out.xml │ ├── hide_to_bottom.xml │ ├── show_from_bottom.xml │ ├── top_in.xml │ └── top_out.xml │ ├── drawable-v21 │ └── bg_folder_item.xml │ ├── drawable │ ├── bg_btn_dis.xml │ ├── bg_btn_nor.xml │ ├── bg_btn_pre.xml │ ├── bg_folder_item.xml │ ├── bg_image_folder.xml │ ├── ic_arrow_back.xml │ ├── ic_cover_shade.xml │ ├── ic_default_image.xml │ ├── ic_vector_check.xml │ ├── ic_vector_delete.xml │ ├── selector_back_press.xml │ ├── selector_grid_camera_bg.xml │ ├── selector_item_checked.xml │ └── selector_top_ok.xml │ ├── layout │ ├── activity_image_crop.xml │ ├── activity_image_grid.xml │ ├── activity_image_preview.xml │ ├── adapter_camera_item.xml │ ├── adapter_folder_list_item.xml │ ├── adapter_image_list_item.xml │ ├── include_top_bar.xml │ └── pop_folder.xml │ ├── mipmap-xxhdpi │ ├── checkbox_checked.png │ ├── checkbox_normal.png │ ├── default_error.png │ ├── grid_camera.png │ └── text_indicator.png │ ├── values-v19 │ └── styles.xml │ ├── values │ ├── attrs.xml │ ├── colors.xml │ ├── strings.xml │ └── styles.xml │ └── xml │ └── provider_paths.xml ├── screenMatch.properties ├── screenMatch_example_dimens.xml ├── settings.gradle └── ucrop ├── .gitignore ├── build.gradle ├── gradle.properties ├── proguard-rules.pro └── src └── main ├── AndroidManifest.xml ├── java └── com │ └── yalantis │ └── ucrop │ ├── ImagePickerLeoProvider.java │ ├── ImageUtils.java │ ├── UCrop.java │ ├── UCropActivity.java │ ├── UCropFragment.java │ ├── UCropFragmentCallback.java │ ├── callback │ ├── BitmapCropCallback.java │ ├── BitmapLoadCallback.java │ ├── CropBoundsChangeListener.java │ └── OverlayViewChangeListener.java │ ├── model │ ├── AspectRatio.java │ ├── CropParameters.java │ ├── ExifInfo.java │ └── ImageState.java │ ├── task │ ├── BitmapCropTask.java │ └── BitmapLoadTask.java │ ├── util │ ├── BitmapLoadUtils.java │ ├── CubicEasing.java │ ├── EglUtils.java │ ├── FastBitmapDrawable.java │ ├── FileUtils.java │ ├── ImageHeaderParser.java │ ├── RectUtils.java │ ├── RotationGestureDetector.java │ └── SelectedStateListDrawable.java │ └── view │ ├── CropImageView.java │ ├── GestureCropImageView.java │ ├── OverlayView.java │ ├── TransformImageView.java │ ├── UCropView.java │ └── widget │ ├── AspectRatioTextView.java │ └── HorizontalProgressWheelView.java ├── jni ├── Android.mk ├── Application.mk ├── CImg.h ├── com_yalantis_ucrop_task_BitmapCropTask.h └── uCrop.cpp ├── jniLibs ├── arm64-v8a │ └── libucrop.so ├── armeabi-v7a │ └── libucrop.so ├── armeabi │ └── libucrop.so ├── x86 │ └── libucrop.so └── x86_64 │ └── libucrop.so └── res ├── anim ├── ucrop_loader_circle_path.xml └── ucrop_loader_circle_scale.xml ├── color ├── text_color_tab.xml └── ucrop_scale_text_view_selector.xml ├── drawable-hdpi ├── ucrop_ic_angle.png └── ucrop_ic_done.png ├── drawable-ldpi ├── ucrop_ic_angle.png └── ucrop_ic_done.png ├── drawable-mdpi ├── ucrop_ic_angle.png └── ucrop_ic_done.png ├── drawable-xhdpi ├── ucrop_ic_angle.png ├── ucrop_ic_done.png └── wangze1.png ├── drawable-xxhdpi ├── ucrop_ic_angle.png └── ucrop_ic_done.png ├── drawable-xxxhdpi ├── ucrop_ic_angle.png └── ucrop_ic_done.png ├── drawable ├── selector_shape.xml ├── shape_false.xml ├── shape_true.xml ├── ucrop_crop.xml ├── ucrop_ic_crop.xml ├── ucrop_ic_crop_unselected.xml ├── ucrop_ic_cross.xml ├── ucrop_ic_next.xml ├── ucrop_ic_reset.xml ├── ucrop_ic_rotate.xml ├── ucrop_ic_rotate_unselected.xml ├── ucrop_ic_scale.xml ├── ucrop_ic_scale_unselected.xml ├── ucrop_rotate.xml ├── ucrop_scale.xml ├── ucrop_shadow_upside.xml ├── ucrop_vector_ic_crop.xml ├── ucrop_vector_loader.xml ├── ucrop_vector_loader_animated.xml └── ucrop_wrapper_controls_shape.xml ├── layout ├── ucrop_activity_photobox.xml ├── ucrop_aspect_ratio.xml ├── ucrop_controls.xml ├── ucrop_fragment_photobox.xml ├── ucrop_layout_rotate_wheel.xml ├── ucrop_layout_scale_wheel.xml └── ucrop_view.xml ├── menu └── ucrop_menu_activity.xml ├── values-de └── strings.xml ├── values-es └── strings.xml ├── values-fi └── strings.xml ├── values-fr └── strings.xml ├── values-it └── strings.xml ├── values-ja └── strings.xml ├── values-ko └── strings.xml ├── values-nl └── strings.xml ├── values-pt-br ├── values-sk └── strings.xml ├── values-zh-rTW └── strings.xml ├── values-zh └── strings.xml ├── values ├── attrs.xml ├── colors.xml ├── dimens.xml ├── public.xml ├── strings.xml ├── styles.xml └── values.xml └── xml └── file_provider_paths.xml /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | .cxx 15 | -------------------------------------------------------------------------------- /.idea/.name: -------------------------------------------------------------------------------- 1 | RichEditTextCopyToutiao -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 15 | 16 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 14 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 此项目已停止维护,后续会出个商业版移动前端富文本。完善所有功能。敬请期待... 2 |
3 | 4 | # RichEditTextCopyToutiao 5 | android高仿今日头条富文本编辑,如果图片显示不出来,可以去我的掘金[掘金地址](https://juejin.im/post/6874875969292173320/) 6 | 7 |
8 | 9 | ## 一、先来看看都实现了补全了哪些功能 10 | (这里不会重复richeditor-android原有功能,只会详细讲解补全的细节功能) 11 | 12 |
13 | 14 | ### 1.1、在没有焦点时,主动获取焦点并弹出软键盘 15 | 16 | **修改后:** 在第一打开富文本编辑时,点击加粗,下划线或上传图片时,主动获取焦点并弹出软键盘。 17 | ![](https://github.com/lihangleo2/RichEditTextCopyToutiao/blob/master/gif/richEditText1.gif) 18 | 19 |
20 | 21 | ### 1.2、在光标主动回到某处,下方tab选中当前的样式,同理撤销和回退 22 | 23 | **修改后:** 在光标变化,撤销回退,或者主动点删除时,下方tab会根据当前样式选中要选中的样式 24 | ![](https://github.com/lihangleo2/RichEditTextCopyToutiao/blob/master/gif/richEditText2.gif) 25 | 26 |
27 | 28 | ### 1.3、上传图片,自动换行,且自动滚动到底部。且充满屏幕 29 | 30 | **修改后:** 选中图片后,插入图片自动换行,自动滚动到底部,弹出软键盘。(图片充满屏幕) 31 | ![](https://github.com/lihangleo2/RichEditTextCopyToutiao/blob/master/gif/richEditText3.gif) 32 | 33 |
34 | 35 | ### 1.4、点击图片,可编辑图片(剪裁),或删除图片 36 | 37 | **修改后:** 点击图片增加了删除和编辑功能,编辑剪裁图片也是模仿头条的。(注:这里的图片选择和图片剪裁是其他第三方,后续给上链接,作者修改后增加了比例切换的动画,且适配了全面屏和处理了虚拟键) 38 | ![](https://github.com/lihangleo2/RichEditTextCopyToutiao/blob/master/gif/richEditText4.gif) 39 | 40 |
41 | 42 | 43 | ### 1.5、根据头条的排版样式,修改了文字,图片还有序号的排版样式 44 | 45 | **修改后:** 原样式,比较不理想。在看了头条的排版样式后,修改了css样式。尽量模仿出头条的感觉 46 | ![](https://github.com/lihangleo2/RichEditTextCopyToutiao/blob/master/gif/richEditText5.gif) 47 | 48 |
49 | 50 | ### 1.6、选中文字后,在不点击屏幕前,选中文字都不变。高度模仿头条 51 | 52 | **修改后:** 之前点击1下或者2下加粗时,选中文字会取消。现在不是主动点击屏幕,选中文字不会取消 53 | ![](https://github.com/lihangleo2/RichEditTextCopyToutiao/blob/master/gif/richEditText6.gif) 54 | 55 |
56 | 57 | ### 1.7、在作者在实现这块功能时,还遇到了缺少ssl证书,图片没法显示出来。还加了忽略ssl证书 58 | 在华为和小米都有出现过。出现的时候,我用头条app去上传图片也是遇到上传图片失败。过一会,手机又正常了。反正加上忽略ssl是一定可以成功的。 59 | 60 |
61 | 62 | ## 二、现在让我们一起看看发布文章重新编辑的功能吧、 63 | ![](https://github.com/lihangleo2/RichEditTextCopyToutiao/blob/master/gif/richEditText7.gif) 64 | 65 |
66 | 67 | ## 三、总结。 68 | 至此一个高仿头条的富文本就此而生。如果你开发遇到了或者有什么古怪的功能还不满足你的要求,请联系我。 69 |
70 | * [文中用到的图片选择框架,建议用demo的,是作者适配全面屏修改过的](https://github.com/jeasonlzy/ImagePicker) 71 | * [文章用到的剪裁框架,建议用demo的,是作者适配全面屏修改过的](https://github.com/Yalantis/uCrop) 72 | * 请注意:文中用到本地路径的图片,如果是网图,则可直接替换成网图即可 73 | 74 | ## 想和我一起成长吗?请关注我的公众号 75 | 76 | 77 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 29 5 | buildToolsVersion "29.0.2" 6 | defaultConfig { 7 | applicationId "com.leo.copytoutiao" 8 | minSdkVersion 22 9 | targetSdkVersion 29 10 | versionCode 1 11 | versionName "1.0" 12 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 13 | } 14 | 15 | dataBinding { 16 | enabled = true 17 | } 18 | 19 | buildTypes { 20 | release { 21 | minifyEnabled false 22 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 23 | } 24 | } 25 | compileOptions { 26 | sourceCompatibility = 1.8 27 | targetCompatibility = 1.8 28 | } 29 | } 30 | 31 | dependencies { 32 | implementation fileTree(dir: 'libs', include: ['*.jar']) 33 | implementation 'androidx.appcompat:appcompat:1.2.0' 34 | implementation 'androidx.constraintlayout:constraintlayout:2.0.1' 35 | testImplementation 'junit:junit:4.12' 36 | androidTestImplementation 'androidx.test.ext:junit:1.1.2' 37 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' 38 | implementation project(path: ':imagepicker') 39 | implementation 'com.github.tbruyelle:rxpermissions:0.10.2' 40 | implementation 'io.reactivex.rxjava2:rxjava:2.1.7' 41 | implementation project(path: ':ucrop') 42 | } 43 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/leo/copytoutiao/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.leo.copytoutiao; 2 | 3 | import android.content.Context; 4 | 5 | import androidx.test.platform.app.InstrumentationRegistry; 6 | import androidx.test.ext.junit.runners.AndroidJUnit4; 7 | 8 | import org.junit.Test; 9 | import org.junit.runner.RunWith; 10 | 11 | import static org.junit.Assert.*; 12 | 13 | /** 14 | * Instrumented test, which will execute on an Android device. 15 | * 16 | * @see Testing documentation 17 | */ 18 | @RunWith(AndroidJUnit4.class) 19 | public class ExampleInstrumentedTest { 20 | @Test 21 | public void useAppContext() { 22 | // Context of the app under test. 23 | Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); 24 | 25 | assertEquals("com.leo.richedittextall", appContext.getPackageName()); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 20 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 36 | 37 | 42 | 43 | 44 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /app/src/main/assets/editor.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /app/src/main/assets/style.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2017 Wasabeef 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | @charset "UTF-8"; 18 | 19 | 20 | html { 21 | height: 100%; 22 | } 23 | 24 | body { 25 | overflow: scroll; 26 | display: table; 27 | table-layout: fixed; 28 | width: 100%; 29 | min-height:100%; 30 | } 31 | 32 | #editor { 33 | display: table-cell; 34 | outline: 0px solid transparent; 35 | background-repeat: no-repeat; 36 | background-position: center; 37 | background-size: cover; 38 | } 39 | 40 | #editor[placeholder]:empty:not(:focus):before { 41 | content: attr(placeholder); 42 | opacity: .5; 43 | }} 44 | -------------------------------------------------------------------------------- /app/src/main/java/com/leo/copytoutiao/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.leo.copytoutiao; 2 | 3 | import androidx.appcompat.app.AppCompatActivity; 4 | import androidx.databinding.DataBindingUtil; 5 | 6 | import android.content.Intent; 7 | import android.content.SharedPreferences; 8 | import android.os.Bundle; 9 | import android.text.TextUtils; 10 | import android.widget.Toast; 11 | 12 | import com.leo.copytoutiao.activity.PublishActivity; 13 | import com.leo.copytoutiao.activity.ShowArtActivity; 14 | import com.leo.copytoutiao.databinding.ActivityMainBinding; 15 | 16 | public class MainActivity extends AppCompatActivity { 17 | ActivityMainBinding binding; 18 | 19 | @Override 20 | protected void onCreate(Bundle savedInstanceState) { 21 | super.onCreate(savedInstanceState); 22 | binding = DataBindingUtil.setContentView(this, R.layout.activity_main); 23 | 24 | binding.buttonPublish.setOnClickListener(v -> { 25 | startActivity(new Intent(MainActivity.this, PublishActivity.class)); 26 | }); 27 | 28 | binding.buttonLook.setOnClickListener(v -> { 29 | SharedPreferences sharedPreferences = getSharedPreferences("art", MODE_PRIVATE); 30 | String title = sharedPreferences.getString("title", "title"); 31 | if (TextUtils.isEmpty(title)) { 32 | Toast.makeText(MainActivity.this, "暂无发布的文章~", Toast.LENGTH_SHORT).show(); 33 | } else { 34 | startActivity(new Intent(MainActivity.this, ShowArtActivity.class)); 35 | } 36 | }); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /app/src/main/java/com/leo/copytoutiao/utils/KeyBoardUtils.java: -------------------------------------------------------------------------------- 1 | package com.leo.copytoutiao.utils; 2 | 3 | /** 4 | * Created by lihang on 2017/9/13. 5 | */ 6 | 7 | import android.content.Context; 8 | import android.view.MotionEvent; 9 | import android.view.View; 10 | import android.view.inputmethod.InputMethodManager; 11 | import android.widget.EditText; 12 | 13 | /** 14 | * 打开或关闭软键盘 15 | * 16 | * @author zhy 17 | */ 18 | public class KeyBoardUtils { 19 | /** 20 | * 打开软键盘 21 | * 22 | * @param mEditText 输入框 23 | * @param mContext 上下文 24 | */ 25 | public static void openKeybord(EditText mEditText, Context mContext) { 26 | InputMethodManager imm = (InputMethodManager) mContext 27 | .getSystemService(Context.INPUT_METHOD_SERVICE); 28 | imm.showSoftInput(mEditText, InputMethodManager.RESULT_SHOWN); 29 | imm.toggleSoftInput(InputMethodManager.SHOW_FORCED, 30 | InputMethodManager.HIDE_IMPLICIT_ONLY); 31 | } 32 | 33 | /** 34 | * 关闭软键盘 35 | * 36 | * @param mEditText 输入框 37 | * @param mContext 上下文 38 | */ 39 | public static void closeKeybord(EditText mEditText, Context mContext) { 40 | if (mContext != null) { 41 | InputMethodManager imm = (InputMethodManager) mContext 42 | .getSystemService(Context.INPUT_METHOD_SERVICE); 43 | imm.hideSoftInputFromWindow(mEditText.getWindowToken(), 0); 44 | } 45 | } 46 | 47 | 48 | /** 49 | * 判断当前点击屏幕的地方是否是软键盘: 50 | * 51 | * @param v 52 | * @param event 53 | * @return 54 | */ 55 | public static boolean isShouldHideInput(View v, MotionEvent event) { 56 | if (v != null && (v instanceof EditText)) { 57 | int[] leftTop = {0, 0}; 58 | v.getLocationInWindow(leftTop); 59 | int left = leftTop[0], top = leftTop[1], bottom = top + v.getHeight(), right = left 60 | + v.getWidth(); 61 | if (event.getX() > left && event.getX() < right 62 | && event.getY() > top && event.getY() < bottom) { 63 | // 保留点击EditText的事件 64 | return false; 65 | } else { 66 | return true; 67 | } 68 | } 69 | return false; 70 | } 71 | 72 | //点击屏幕空白处,隐藏软键盘 73 | // @Override 74 | // public boolean dispatchTouchEvent(MotionEvent ev) { 75 | // if (ev.getAction() == MotionEvent.ACTION_DOWN) { 76 | // View v = getCurrentFocus(); 77 | // if (isShouldHideInput(v, ev)) { 78 | // KeyBoardUtils.closeKeybord(binding.editPhone, LoginActivity.this); 79 | // } 80 | // } 81 | // return super.dispatchTouchEvent(ev); 82 | // } 83 | 84 | } 85 | -------------------------------------------------------------------------------- /app/src/main/java/com/leo/copytoutiao/utils/RichUtils.java: -------------------------------------------------------------------------------- 1 | package com.leo.copytoutiao.utils; 2 | 3 | import android.text.TextUtils; 4 | 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | import java.util.regex.Matcher; 8 | import java.util.regex.Pattern; 9 | 10 | /** 11 | * Created by leo 12 | * on 2020/9/18. 13 | */ 14 | 15 | public class RichUtils { 16 | 17 | /** 18 | * 截取富文本中的图片链接 19 | */ 20 | public static ArrayList returnImageUrlsFromHtml(String content) { 21 | List imageSrcList = new ArrayList(); 22 | if (TextUtils.isEmpty(content)) { 23 | return (ArrayList) imageSrcList; 24 | } 25 | String htmlCode = content; 26 | Pattern p = Pattern.compile("]*\\bsrc\\b\\s*=\\s*('|\")?([^'\"\n\r\f>]+(\\.jpg|\\.bmp|\\.eps|\\.gif|\\.mif|\\.miff|\\.png|\\.tif|\\.tiff|\\.svg|\\.wmf|\\.jpe|\\.jpeg|\\.dib|\\.ico|\\.tga|\\.cut|\\.pic|\\b)\\b)[^>]*>", Pattern.CASE_INSENSITIVE); 27 | Matcher m = p.matcher(htmlCode); 28 | String quote = null; 29 | String src = null; 30 | while (m.find()) { 31 | quote = m.group(1); 32 | src = (quote == null || quote.trim().length() == 0) ? m.group(2).split("//s+")[0] : m.group(2); 33 | imageSrcList.add(src); 34 | } 35 | 36 | return (ArrayList) imageSrcList; 37 | } 38 | 39 | 40 | /** 41 | * 截取富文本中的纯文本内容 42 | */ 43 | public static String returnOnlyText(String htmlStr) { 44 | if (TextUtils.isEmpty(htmlStr)) { 45 | return ""; 46 | } else { 47 | String regFormat = "\\s*|\t|\r|\n"; 48 | String regTag = "<[^>]*>"; 49 | String text = htmlStr.replaceAll(regFormat, "").replaceAll(regTag, ""); 50 | text = text.replace(" ", ""); 51 | return text; 52 | } 53 | } 54 | 55 | 56 | public static boolean isEmpty(String htmlStr) { 57 | ArrayList images = returnImageUrlsFromHtml(htmlStr); 58 | String text = returnOnlyText(htmlStr); 59 | if (TextUtils.isEmpty(text) && images.size() == 0) { 60 | return true; 61 | } else { 62 | return false; 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /app/src/main/java/com/leo/copytoutiao/utils/Utils.java: -------------------------------------------------------------------------------- 1 | package com.leo.copytoutiao.utils; 2 | 3 | import android.content.Context; 4 | import android.graphics.Bitmap; 5 | import android.graphics.BitmapFactory; 6 | import android.graphics.Canvas; 7 | import android.graphics.drawable.BitmapDrawable; 8 | import android.graphics.drawable.Drawable; 9 | import android.util.Base64; 10 | 11 | import java.io.ByteArrayOutputStream; 12 | 13 | /** 14 | * Created by ZQiong on 2018/3/22. 15 | */ 16 | 17 | public final class Utils { 18 | 19 | private Utils() throws InstantiationException { 20 | throw new InstantiationException("This class is not for instantiation"); 21 | } 22 | 23 | public static String toBase64(Bitmap bitmap) { 24 | ByteArrayOutputStream baos = new ByteArrayOutputStream(); 25 | bitmap.compress(Bitmap.CompressFormat.PNG, 100, baos); 26 | byte[] bytes = baos.toByteArray(); 27 | 28 | return Base64.encodeToString(bytes, Base64.NO_WRAP); 29 | } 30 | 31 | public static Bitmap toBitmap(Drawable drawable) { 32 | if (drawable instanceof BitmapDrawable) { 33 | return ((BitmapDrawable) drawable).getBitmap(); 34 | } 35 | 36 | int width = drawable.getIntrinsicWidth(); 37 | width = width > 0 ? width : 1; 38 | int height = drawable.getIntrinsicHeight(); 39 | height = height > 0 ? height : 1; 40 | 41 | Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); 42 | Canvas canvas = new Canvas(bitmap); 43 | drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); 44 | drawable.draw(canvas); 45 | 46 | return bitmap; 47 | } 48 | 49 | public static Bitmap decodeResource(Context context, int resId) { 50 | return BitmapFactory.decodeResource(context.getResources(), resId); 51 | } 52 | 53 | public static long getCurrentTime() { 54 | return System.currentTimeMillis(); 55 | } 56 | 57 | 58 | } 59 | -------------------------------------------------------------------------------- /app/src/main/java/com/leo/copytoutiao/utils/popup/PopupController.java: -------------------------------------------------------------------------------- 1 | package com.leo.copytoutiao.utils.popup; 2 | 3 | import android.content.Context; 4 | import android.graphics.drawable.ColorDrawable; 5 | import android.view.LayoutInflater; 6 | import android.view.View; 7 | import android.view.ViewGroup; 8 | import android.view.Window; 9 | import android.widget.PopupWindow; 10 | 11 | /** 12 | * Created by MQ on 2017/5/2. 13 | */ 14 | 15 | class PopupController { 16 | private int layoutResId;//布局id 17 | Context context; 18 | private PopupWindow popupWindow; 19 | View mPopupView;//弹窗布局View 20 | private View mView; 21 | private Window mWindow; 22 | 23 | PopupController(Context context, PopupWindow popupWindow) { 24 | this.context = context; 25 | this.popupWindow = popupWindow; 26 | } 27 | 28 | public void setView(int layoutResId) { 29 | mView = null; 30 | this.layoutResId = layoutResId; 31 | installContent(); 32 | } 33 | 34 | public void setView(View view) { 35 | mView = view; 36 | this.layoutResId = 0; 37 | installContent(); 38 | } 39 | 40 | private void installContent() { 41 | if (layoutResId != 0) { 42 | mPopupView = LayoutInflater.from(context).inflate(layoutResId, null); 43 | } else if (mView != null) { 44 | mPopupView = mView; 45 | } 46 | popupWindow.setContentView(mPopupView); 47 | } 48 | 49 | /** 50 | * 设置宽度 51 | * 52 | * @param width 宽 53 | * @param height 高 54 | */ 55 | private void setWidthAndHeight(int width, int height) { 56 | if (width == 0 || height == 0) { 57 | //如果没设置宽高,默认是WRAP_CONTENT 58 | popupWindow.setWidth(ViewGroup.LayoutParams.WRAP_CONTENT); 59 | popupWindow.setHeight(ViewGroup.LayoutParams.WRAP_CONTENT); 60 | } else { 61 | popupWindow.setWidth(width); 62 | popupWindow.setHeight(height); 63 | } 64 | } 65 | 66 | 67 | /** 68 | * 设置动画 69 | */ 70 | private void setAnimationStyle(int animationStyle) { 71 | popupWindow.setAnimationStyle(animationStyle); 72 | } 73 | 74 | 75 | /** 76 | * 设置Outside是否可点击 77 | * 78 | * @param touchable 是否可点击 79 | */ 80 | private void setOutsideTouchable(boolean touchable) { 81 | popupWindow.setBackgroundDrawable(new ColorDrawable(0x00000000));//设置透明背景 82 | popupWindow.setOutsideTouchable(touchable);//设置outside可点击 83 | popupWindow.setFocusable(touchable); 84 | } 85 | 86 | 87 | static class PopupParams { 88 | public int layoutResId;//布局id 89 | public Context mContext; 90 | public int mWidth, mHeight;//弹窗的宽和高 91 | public boolean isShowAnim; 92 | public int animationStyle;//动画Id 93 | public View mView; 94 | public boolean isTouchable = true; 95 | 96 | public PopupParams(Context mContext) { 97 | this.mContext = mContext; 98 | } 99 | 100 | public void apply(PopupController controller) { 101 | if (mView != null) { 102 | controller.setView(mView); 103 | } else if (layoutResId != 0) { 104 | controller.setView(layoutResId); 105 | } else { 106 | throw new IllegalArgumentException("PopupView's contentView is null"); 107 | } 108 | controller.setWidthAndHeight(mWidth, mHeight); 109 | controller.setOutsideTouchable(isTouchable);//设置outside可点击 110 | 111 | if (isShowAnim) { 112 | controller.setAnimationStyle(animationStyle); 113 | } 114 | } 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /app/src/main/res/anim/translate_pop_in.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 7 | 8 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/anim/translate_pop_out.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 13 | 14 | -------------------------------------------------------------------------------- /app/src/main/res/color/text_newapp_publish.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 13 | 19 | 22 | 25 | 26 | 27 | 28 | 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/bg_dotted_line.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/shape_white_top.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 15 | 16 | 23 | 24 |