Commit 7b25b119 authored by ShengDecheng's avatar ShengDecheng

更新1.1版本,优化内存占用,支持RxJava

parent f082222b
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="MarkdownProjectSettings">
<PreviewSettings splitEditorLayout="SPLIT" splitEditorPreview="PREVIEW" useGrayscaleRendering="false" zoomFactor="1.0" maxImageWidth="0" showGitHubPageIfSynced="false" allowBrowsingInPreview="false" synchronizePreviewPosition="true" highlightPreviewType="NONE" highlightFadeOut="5" highlightOnTyping="true" synchronizeSourcePosition="true">
<PanelProvider>
<provider providerId="com.vladsch.idea.multimarkdown.editor.swing.html.panel" providerName="Default - Swing" />
</PanelProvider>
</PreviewSettings>
<ParserSettings>
<PegdownExtensions>
<option name="ABBREVIATIONS" value="false" />
<option name="ANCHORLINKS" value="true" />
<option name="ATXHEADERSPACE" value="true" />
<option name="AUTOLINKS" value="true" />
<option name="DEFINITIONS" value="false" />
<option name="FENCED_CODE_BLOCKS" value="true" />
<option name="FOOTNOTES" value="false" />
<option name="HARDWRAPS" value="false" />
<option name="INSERTED" value="false" />
<option name="QUOTES" value="false" />
<option name="RELAXEDHRULES" value="true" />
<option name="SMARTS" value="false" />
<option name="STRIKETHROUGH" value="true" />
<option name="SUBSCRIPT" value="false" />
<option name="SUPERSCRIPT" value="false" />
<option name="SUPPRESS_HTML_BLOCKS" value="false" />
<option name="SUPPRESS_INLINE_HTML" value="false" />
<option name="TABLES" value="true" />
<option name="TASKLISTITEMS" value="true" />
<option name="TOC" value="false" />
<option name="WIKILINKS" value="true" />
</PegdownExtensions>
<ParserOptions>
<option name="COMMONMARK_LISTS" value="false" />
<option name="DUMMY" value="false" />
<option name="EMOJI_SHORTCUTS" value="true" />
<option name="FLEXMARK_FRONT_MATTER" value="false" />
<option name="GFM_TABLE_RENDERING" value="true" />
<option name="GITBOOK_URL_ENCODING" value="false" />
<option name="GITHUB_EMOJI_URL" value="false" />
<option name="GITHUB_LISTS" value="true" />
<option name="GITHUB_WIKI_LINKS" value="true" />
<option name="JEKYLL_FRONT_MATTER" value="false" />
<option name="SIM_TOC_BLANK_LINE_SPACER" value="true" />
</ParserOptions>
</ParserSettings>
<HtmlSettings headerTopEnabled="false" headerBottomEnabled="false" bodyTopEnabled="false" bodyBottomEnabled="false" embedUrlContent="false" addPageHeader="true">
<GeneratorProvider>
<provider providerId="com.vladsch.idea.multimarkdown.editor.swing.html.generator" providerName="Default Swing HTML Generator" />
</GeneratorProvider>
<headerTop />
<headerBottom />
<bodyTop />
<bodyBottom />
</HtmlSettings>
<CssSettings previewScheme="UI_SCHEME" cssUri="" isCssUriEnabled="false" isCssTextEnabled="false" isDynamicPageWidth="true">
<StylesheetProvider>
<provider providerId="com.vladsch.idea.multimarkdown.editor.swing.html.css" providerName="Default Swing Stylesheet" />
</StylesheetProvider>
<ScriptProviders />
<cssText />
</CssSettings>
<HtmlExportSettings updateOnSave="false" parentDir="$ProjectFileDir$" targetDir="$ProjectFileDir$" cssDir="" scriptDir="" plainHtml="false" imageDir="" copyLinkedImages="false" imageUniquifyType="0" targetExt="" useTargetExt="false" noCssNoScripts="false" linkToExportedHtml="true" exportOnSettingsChange="true" regenerateOnProjectOpen="false" />
<LinkMapSettings>
<textMaps />
</LinkMapSettings>
</component>
</project>
\ No newline at end of file
...@@ -2,8 +2,11 @@ ...@@ -2,8 +2,11 @@
<project version="4"> <project version="4">
<component name="ProjectModuleManager"> <component name="ProjectModuleManager">
<modules> <modules>
<module fileurl="file://$PROJECT_DIR$/XRichText.iml" filepath="$PROJECT_DIR$/XRichText.iml" />
<module fileurl="file://$PROJECT_DIR$/XRichTextDemo.iml" filepath="$PROJECT_DIR$/XRichTextDemo.iml" /> <module fileurl="file://$PROJECT_DIR$/XRichTextDemo.iml" filepath="$PROJECT_DIR$/XRichTextDemo.iml" />
<module fileurl="file://$PROJECT_DIR$/app/app.iml" filepath="$PROJECT_DIR$/app/app.iml" /> <module fileurl="file://$PROJECT_DIR$/app/app.iml" filepath="$PROJECT_DIR$/app/app.iml" />
<module fileurl="file://$PROJECT_DIR$/app/app.iml" filepath="$PROJECT_DIR$/app/app.iml" />
<module fileurl="file://$PROJECT_DIR$/xrichtext/xrichtext.iml" filepath="$PROJECT_DIR$/xrichtext/xrichtext.iml" />
<module fileurl="file://$PROJECT_DIR$/xrichtext/xrichtext.iml" filepath="$PROJECT_DIR$/xrichtext/xrichtext.iml" /> <module fileurl="file://$PROJECT_DIR$/xrichtext/xrichtext.iml" filepath="$PROJECT_DIR$/xrichtext/xrichtext.iml" />
</modules> </modules>
</component> </component>
......
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>
\ No newline at end of file
apply plugin: 'com.android.application' apply plugin: 'com.android.application'
android { android {
compileSdkVersion 24 compileSdkVersion 25
buildToolsVersion "24.0.0" buildToolsVersion "25.0.1"
defaultConfig { defaultConfig {
applicationId "com.sendtion.xrichtext" applicationId "com.sendtion.xrichtext"
minSdkVersion 14 minSdkVersion 14
...@@ -27,10 +27,19 @@ dependencies { ...@@ -27,10 +27,19 @@ dependencies {
testCompile 'junit:junit:4.12' testCompile 'junit:junit:4.12'
compile project(':xrichtext') compile project(':xrichtext')
compile 'com.android.support:appcompat-v7:24.2.1' compile 'com.android.support:appcompat-v7:25.0.1'
compile 'com.android.support:design:24.2.1' compile 'com.android.support:design:25.0.1'
compile 'com.android.support:cardview-v7:24.2.1' compile 'com.android.support:cardview-v7:25.0.1'
//recyclerview列表 //recyclerview列表 https://github.com/jianghejie/XRecyclerView
compile 'com.jcodecraeer:xrecyclerview:1.2.7' compile 'com.jcodecraeer:xrecyclerview:1.3.2'
//图片选择器 https://github.com/donglua/PhotoPicker
compile 'me.iwf.photopicker:PhotoPicker:0.8.4@aar'
//图片加载框架 https://github.com/bumptech/glide
//compile 'com.github.bumptech.glide:glide:3.7.0'
//旧版本动画兼容库
compile 'com.nineoldandroids:library:2.4.0'
compile 'io.reactivex:rxjava:1.1.0'
compile 'io.reactivex:rxandroid:1.1.0'
} }
...@@ -15,3 +15,10 @@ ...@@ -15,3 +15,10 @@
#-keepclassmembers class fqcn.of.javascript.interface.for.webview { #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *; # public *;
#} #}
# Glide
-keep public class * implements com.bumptech.glide.module.GlideModule
-keep public enum com.bumptech.glide.load.resource.bitmap.ImageHeaderParser$** {
**[] $VALUES;
public *;
}
\ No newline at end of file
...@@ -13,22 +13,26 @@ ...@@ -13,22 +13,26 @@
android:theme="@style/AppTheme"> android:theme="@style/AppTheme">
<activity <activity
android:name=".ui.MainActivity" android:name=".ui.MainActivity"
android:label="@string/app_name" android:label="@string/app_name">
android:theme="@style/AppTheme.NoActionBar">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.LAUNCHER" />
</intent-filter> </intent-filter>
</activity> </activity>
<activity <activity
android:name=".ui.NewActivity" android:name=".ui.NewActivity"
android:label="@string/title_activity_new" android:label="@string/title_activity_new"
android:theme="@style/AppTheme.NoActionBar" /> android:windowSoftInputMode="stateHidden|adjustResize" />
<activity <activity
android:name=".ui.NoteActivity" android:name=".ui.NoteActivity"
android:label="@string/title_activity_note" android:label="@string/title_activity_note"/>
android:theme="@style/AppTheme.NoActionBar"></activity>
<activity
android:name="me.iwf.photopicker.PhotoPickerActivity"
android:screenOrientation="portrait" />
<activity
android:name="me.iwf.photopicker.PhotoPagerActivity"
android:screenOrientation="portrait" />
</application> </application>
</manifest> </manifest>
\ No newline at end of file
...@@ -12,6 +12,7 @@ import android.view.Menu; ...@@ -12,6 +12,7 @@ import android.view.Menu;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.View; import android.view.View;
import com.jcodecraeer.xrecyclerview.ProgressStyle;
import com.jcodecraeer.xrecyclerview.XRecyclerView; import com.jcodecraeer.xrecyclerview.XRecyclerView;
import com.sendtion.xrichtextdemo.R; import com.sendtion.xrichtextdemo.R;
import com.sendtion.xrichtextdemo.adapter.MyNoteListAdapter; import com.sendtion.xrichtextdemo.adapter.MyNoteListAdapter;
...@@ -72,11 +73,10 @@ public class MainActivity extends BaseActivity { ...@@ -72,11 +73,10 @@ public class MainActivity extends BaseActivity {
layoutManager.setOrientation(LinearLayoutManager.VERTICAL);//竖向列表 layoutManager.setOrientation(LinearLayoutManager.VERTICAL);//竖向列表
rv_list_main.setLayoutManager(layoutManager); rv_list_main.setLayoutManager(layoutManager);
rv_list_main.setLoadingMoreEnabled(false);//禁止上拉加载 rv_list_main.setLoadingMoreEnabled(true);//开启上拉加载
rv_list_main.setPullRefreshEnabled(false);//禁止下拉刷新 rv_list_main.setPullRefreshEnabled(true);//开启下拉刷新
//rv_list_question.setRefreshProgressStyle(ProgressStyle.BallSpinFadeLoader); rv_list_main.setRefreshProgressStyle(ProgressStyle.SquareSpin);
//rv_list_main.setLoadingMoreProgressStyle(ProgressStyle.BallRotate); rv_list_main.setLoadingMoreProgressStyle(ProgressStyle.BallScale);
//rv_list_question.setArrowImageView(R.drawable.icon_arrow_downgrey);
/****************** 设置XRecyclerView属性 **************************/ /****************** 设置XRecyclerView属性 **************************/
mNoteListAdapter = new MyNoteListAdapter(); mNoteListAdapter = new MyNoteListAdapter();
...@@ -124,12 +124,22 @@ public class MainActivity extends BaseActivity { ...@@ -124,12 +124,22 @@ public class MainActivity extends BaseActivity {
@Override @Override
public void onRefresh() {//下拉刷新 public void onRefresh() {//下拉刷新
rv_list_main.postDelayed(new Runnable() {
@Override
public void run() {
rv_list_main.refreshComplete();
}
}, 1000);
} }
@Override @Override
public void onLoadMore() {//上拉加载 public void onLoadMore() {//上拉加载
rv_list_main.postDelayed(new Runnable() {
@Override
public void run() {
rv_list_main.loadMoreComplete();
}
}, 1000);
} }
} }
......
package com.sendtion.xrichtextdemo.ui; package com.sendtion.xrichtextdemo.ui;
import android.app.ProgressDialog;
import android.content.Intent; import android.content.Intent;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.os.Bundle; import android.os.Bundle;
import android.provider.MediaStore;
import android.support.design.widget.FloatingActionButton; import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar; import android.support.design.widget.Snackbar;
import android.support.v7.widget.Toolbar; import android.support.v7.widget.Toolbar;
...@@ -15,6 +15,7 @@ import android.widget.TextView; ...@@ -15,6 +15,7 @@ import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import com.sendtion.xrichtext.RichTextEditor; import com.sendtion.xrichtext.RichTextEditor;
import com.sendtion.xrichtext.SDCardUtil;
import com.sendtion.xrichtextdemo.R; import com.sendtion.xrichtextdemo.R;
import com.sendtion.xrichtextdemo.bean.Group; import com.sendtion.xrichtextdemo.bean.Group;
import com.sendtion.xrichtextdemo.bean.Note; import com.sendtion.xrichtextdemo.bean.Note;
...@@ -23,13 +24,25 @@ import com.sendtion.xrichtextdemo.db.NoteDao; ...@@ -23,13 +24,25 @@ import com.sendtion.xrichtextdemo.db.NoteDao;
import com.sendtion.xrichtextdemo.util.CommonUtil; import com.sendtion.xrichtextdemo.util.CommonUtil;
import com.sendtion.xrichtextdemo.util.DateUtils; import com.sendtion.xrichtextdemo.util.DateUtils;
import com.sendtion.xrichtextdemo.util.ImageUtils; import com.sendtion.xrichtextdemo.util.ImageUtils;
import com.sendtion.xrichtextdemo.util.SDCardUtil;
import com.sendtion.xrichtextdemo.util.ScreenUtils; import com.sendtion.xrichtextdemo.util.ScreenUtils;
import com.sendtion.xrichtextdemo.util.StringUtils; import com.sendtion.xrichtextdemo.util.StringUtils;
import java.io.File;
import java.util.ArrayList;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import me.iwf.photopicker.PhotoPicker;
import rx.Observable;
import rx.Observer;
import rx.Subscriber;
import rx.Subscription;
import rx.android.schedulers.AndroidSchedulers;
import rx.schedulers.Schedulers;
/**
* 新建笔记
*/
public class NewActivity extends BaseActivity { public class NewActivity extends BaseActivity {
private EditText et_new_title; private EditText et_new_title;
...@@ -48,6 +61,13 @@ public class NewActivity extends BaseActivity { ...@@ -48,6 +61,13 @@ public class NewActivity extends BaseActivity {
private static final int cutTitleLength = 20;//截取的标题长度 private static final int cutTitleLength = 20;//截取的标题长度
private ProgressDialog loadingDialog;
private ProgressDialog insertDialog;
private int screenWidth;
private int screenHeight;
private Subscription subsLoading;
private Subscription subsInsert;
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
...@@ -82,6 +102,17 @@ public class NewActivity extends BaseActivity { ...@@ -82,6 +102,17 @@ public class NewActivity extends BaseActivity {
noteDao = new NoteDao(this); noteDao = new NoteDao(this);
note = new Note(); note = new Note();
screenWidth = ScreenUtils.getScreenWidth(this);
screenHeight = ScreenUtils.getScreenHeight(this);
insertDialog = new ProgressDialog(this);
insertDialog.setMessage("正在插入图片...");
insertDialog.setCanceledOnTouchOutside(false);
loadingDialog = new ProgressDialog(this);
loadingDialog.setMessage("图片解析中...");
loadingDialog.setCanceledOnTouchOutside(false);
et_new_title = (EditText) findViewById(R.id.et_new_title); et_new_title = (EditText) findViewById(R.id.et_new_title);
et_new_content = (RichTextEditor) findViewById(R.id.et_new_content); et_new_content = (RichTextEditor) findViewById(R.id.et_new_content);
tv_new_time = (TextView) findViewById(R.id.tv_new_time); tv_new_time = (TextView) findViewById(R.id.tv_new_time);
...@@ -106,7 +137,9 @@ public class NewActivity extends BaseActivity { ...@@ -106,7 +137,9 @@ public class NewActivity extends BaseActivity {
et_new_content.post(new Runnable() { et_new_content.post(new Runnable() {
@Override @Override
public void run() { public void run() {
showEditData(note.getContent()); //showEditData(note.getContent());
et_new_content.clearAllLayout();
showDataSync(note.getContent());
} }
}); });
} else { } else {
...@@ -122,35 +155,68 @@ public class NewActivity extends BaseActivity { ...@@ -122,35 +155,68 @@ public class NewActivity extends BaseActivity {
} }
/** /**
* 显示数据 * 异步方式显示数据
* @param html
*/ */
protected void showEditData(String content) { private void showDataSync(final String html){
et_new_content.clearAllLayout(); loadingDialog.show();
List<String> textList = StringUtils.cutStringByImgTag(content);
for (int i = 0; i < textList.size(); i++) { subsLoading = Observable.create(new Observable.OnSubscribe<String>() {
String text = textList.get(i); @Override
if (text.contains("<img")) { public void call(Subscriber<? super String> subscriber) {
String imagePath = StringUtils.getImgSrc(text); showEditData(subscriber, html);
int width = ScreenUtils.getScreenWidth(this); }
int height = ScreenUtils.getScreenHeight(this); })
et_new_content.measure(0,0); .onBackpressureBuffer()
Bitmap bitmap = ImageUtils.getSmallBitmap(imagePath, width, height); .subscribeOn(Schedulers.io())//生产事件在io
//String imagePath = SDCardUtil.saveToSdCard(bitmap); .observeOn(AndroidSchedulers.mainThread())//消费事件在UI线程
//Log.i("NewActivity", "###imagePath="+imagePath); .subscribe(new Observer<String>() {
//et_content.insertImage(imagePath, et_content.getMeasuredWidth()); @Override
public void onCompleted() {
//editor.insertImage(imagePath, editor.getWidth()); loadingDialog.dismiss();
//Bitmap bmp = et_content.getScaledBitmap(imagePath, et_content.getWidth()); }
if (bitmap != null){
et_new_content.addImageViewAtIndex(et_new_content.getLastIndex(), bitmap, imagePath); @Override
public void onError(Throwable e) {
loadingDialog.dismiss();
showToast("解析错误:图片不存在或已损坏");
}
@Override
public void onNext(String text) {
if (text.contains(SDCardUtil.getPictureDir())){
et_new_content.addImageViewAtIndex(et_new_content.getLastIndex(), text);
} else { } else {
et_new_content.addEditTextAtIndex(et_new_content.getLastIndex(), text); et_new_content.addEditTextAtIndex(et_new_content.getLastIndex(), text);
} }
} else {
// Log.e("MainActivity", "###textIndex=" + editor.getLastIndex());
// Log.e("MainActivity", "###text=" + text);
et_new_content.addEditTextAtIndex(et_new_content.getLastIndex(), text);
} }
});
}
/**
* 显示数据
*/
protected void showEditData(Subscriber<? super String> subscriber, String html) {
try{
List<String> textList = StringUtils.cutStringByImgTag(html);
for (int i = 0; i < textList.size(); i++) {
String text = textList.get(i);
if (text.contains("<img")) {
String imagePath = StringUtils.getImgSrc(text);
if (new File(imagePath).exists()) {
subscriber.onNext(imagePath);
} else {
showToast("图片"+i+"已丢失,请重新插入!");
}
} else {
subscriber.onNext(text);
}
}
subscriber.onCompleted();
}catch (Exception e){
e.printStackTrace();
subscriber.onError(e);
} }
} }
...@@ -252,25 +318,90 @@ public class NewActivity extends BaseActivity { ...@@ -252,25 +318,90 @@ public class NewActivity extends BaseActivity {
* 调用图库选择 * 调用图库选择
*/ */
private void callGallery(){ private void callGallery(){
Intent intent = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI); // //调用系统图库
intent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*");// 相片类型 // Intent intent = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
startActivityForResult(intent, 1); // intent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*");// 相片类型
// startActivityForResult(intent, 1);
//调用第三方图库选择
PhotoPicker.builder()
.setPhotoCount(5)//可选择图片数量
.setShowCamera(true)//是否显示拍照按钮
.setShowGif(true)//是否显示动态图
.setPreviewEnabled(true)//是否可以预览
.start(this, PhotoPicker.REQUEST_CODE);
} }
@Override @Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) { protected void onActivityResult(int requestCode, int resultCode, final Intent data) {
super.onActivityResult(requestCode, resultCode, data); super.onActivityResult(requestCode, resultCode, data);
if (resultCode == RESULT_OK && requestCode == 1) { if (resultCode == RESULT_OK) {
if (data != null) { if (data != null) {
et_new_content.measure(0,0); if (requestCode == 1){
String imagePath = SDCardUtil.getFilePathByUri(this, data.getData()); //处理调用系统图库
//Log.i("NewActivity", "###imagePath="+imagePath); } else if (requestCode == PhotoPicker.REQUEST_CODE){
et_new_content.insertImage(imagePath, et_new_content.getMeasuredWidth()); //异步方式插入图片
insertImagesSync(data);
}
} }
} }
} }
/**
* 异步方式插入图片
* @param data
*/
private void insertImagesSync(final Intent data){
insertDialog.show();
subsInsert = Observable.create(new Observable.OnSubscribe<String>() {
@Override
public void call(Subscriber<? super String> subscriber) {
try{
et_new_content.measure(0, 0);
int width = ScreenUtils.getScreenWidth(NewActivity.this);
int height = ScreenUtils.getScreenHeight(NewActivity.this);
ArrayList<String> photos = data.getStringArrayListExtra(PhotoPicker.KEY_SELECTED_PHOTOS);
//可以同时插入多张图片
for (String imagePath : photos) {
//Log.i("NewActivity", "###path=" + imagePath);
Bitmap bitmap = ImageUtils.getSmallBitmap(imagePath, width, height);//压缩图片
//bitmap = BitmapFactory.decodeFile(imagePath);
imagePath = SDCardUtil.saveToSdCard(bitmap);
//Log.i("NewActivity", "###imagePath="+imagePath);
subscriber.onNext(imagePath);
}
subscriber.onCompleted();
}catch (Exception e){
e.printStackTrace();
subscriber.onError(e);
}
}
})
.onBackpressureBuffer()
.subscribeOn(Schedulers.io())//生产事件在io
.observeOn(AndroidSchedulers.mainThread())//消费事件在UI线程
.subscribe(new Observer<String>() {
@Override
public void onCompleted() {
insertDialog.dismiss();
et_new_content.addEditTextAtIndex(et_new_content.getLastIndex(), " ");
showToast("图片插入成功");
}
@Override
public void onError(Throwable e) {
insertDialog.dismiss();
showToast("图片插入失败:"+e.getMessage());
}
@Override
public void onNext(String imagePath) {
et_new_content.insertImage(imagePath, et_new_content.getMeasuredWidth());
}
});
}
@Override @Override
protected void onResume() { protected void onResume() {
super.onResume(); super.onResume();
......
package com.sendtion.xrichtextdemo.ui; package com.sendtion.xrichtextdemo.ui;
import android.app.ProgressDialog;
import android.content.Intent; import android.content.Intent;
import android.graphics.Bitmap;
import android.os.Bundle; import android.os.Bundle;
import android.support.design.widget.FloatingActionButton; import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar; import android.support.design.widget.Snackbar;
...@@ -18,10 +18,22 @@ import com.sendtion.xrichtextdemo.bean.Note; ...@@ -18,10 +18,22 @@ import com.sendtion.xrichtextdemo.bean.Note;
import com.sendtion.xrichtextdemo.db.GroupDao; import com.sendtion.xrichtextdemo.db.GroupDao;
import com.sendtion.xrichtextdemo.db.NoteDao; import com.sendtion.xrichtextdemo.db.NoteDao;
import com.sendtion.xrichtextdemo.util.CommonUtil; import com.sendtion.xrichtextdemo.util.CommonUtil;
import com.sendtion.xrichtextdemo.util.SDCardUtil;
import com.sendtion.xrichtextdemo.util.StringUtils; import com.sendtion.xrichtextdemo.util.StringUtils;
import java.io.File;
import java.util.List; import java.util.List;
import rx.Observable;
import rx.Observer;
import rx.Subscriber;
import rx.Subscription;
import rx.android.schedulers.AndroidSchedulers;
import rx.schedulers.Schedulers;
/**
* 笔记详情
*/
public class NoteActivity extends BaseActivity { public class NoteActivity extends BaseActivity {
private TextView tv_note_title;//笔记标题 private TextView tv_note_title;//笔记标题
...@@ -36,6 +48,9 @@ public class NoteActivity extends BaseActivity { ...@@ -36,6 +48,9 @@ public class NoteActivity extends BaseActivity {
private NoteDao noteDao; private NoteDao noteDao;
private GroupDao groupDao; private GroupDao groupDao;
private ProgressDialog loadingDialog;
private Subscription subsLoading;
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
...@@ -69,6 +84,10 @@ public class NoteActivity extends BaseActivity { ...@@ -69,6 +84,10 @@ public class NoteActivity extends BaseActivity {
noteDao = new NoteDao(this); noteDao = new NoteDao(this);
groupDao = new GroupDao(this); groupDao = new GroupDao(this);
loadingDialog = new ProgressDialog(this);
loadingDialog.setMessage("数据加载中...");
loadingDialog.setCanceledOnTouchOutside(false);
tv_note_title = (TextView) findViewById(R.id.tv_note_title);//标题 tv_note_title = (TextView) findViewById(R.id.tv_note_title);//标题
tv_note_title.setTextIsSelectable(true); tv_note_title.setTextIsSelectable(true);
tv_note_content = (RichTextView) findViewById(R.id.tv_note_content);//内容 tv_note_content = (RichTextView) findViewById(R.id.tv_note_content);//内容
...@@ -88,7 +107,9 @@ public class NoteActivity extends BaseActivity { ...@@ -88,7 +107,9 @@ public class NoteActivity extends BaseActivity {
tv_note_content.post(new Runnable() { tv_note_content.post(new Runnable() {
@Override @Override
public void run() { public void run() {
showEditData(myContent); //showEditData(myContent);
tv_note_content.clearAllLayout();
showDataSync(myContent);
} }
}); });
tv_note_time.setText(note.getCreateTime()); tv_note_time.setText(note.getCreateTime());
...@@ -98,25 +119,70 @@ public class NoteActivity extends BaseActivity { ...@@ -98,25 +119,70 @@ public class NoteActivity extends BaseActivity {
} }
/** /**
* 显示数据 * 异步方式显示数据
* @param html * @param html
*/ */
private void showEditData(String html) { private void showDataSync(final String html){
tv_note_content.clearAllLayout(); loadingDialog.show();
List<String> textList = StringUtils.cutStringByImgTag(html);
for (int i = 0; i < textList.size(); i++) { subsLoading = Observable.create(new Observable.OnSubscribe<String>() {
String text = textList.get(i); @Override
if (text.contains("<img") && text.contains("src=")) { public void call(Subscriber<? super String> subscriber) {
String imagePath = StringUtils.getImgSrc(text); showEditData(subscriber, html);
Bitmap bmp = tv_note_content.getScaledBitmap(imagePath, tv_note_content.getWidth()); }
if (bmp != null){ })
tv_note_content.addImageViewAtIndex(tv_note_content.getLastIndex(), bmp, imagePath); .onBackpressureBuffer()
.subscribeOn(Schedulers.io())//生产事件在io
.observeOn(AndroidSchedulers.mainThread())//消费事件在UI线程
.subscribe(new Observer<String>() {
@Override
public void onCompleted() {
loadingDialog.dismiss();
}
@Override
public void onError(Throwable e) {
loadingDialog.dismiss();
e.printStackTrace();
showToast("解析错误:图片不存在或已损坏");
}
@Override
public void onNext(String text) {
if (text.contains(SDCardUtil.getPictureDir())){
tv_note_content.addImageViewAtIndex(tv_note_content.getLastIndex(), text);
} else { } else {
tv_note_content.addTextViewAtIndex(tv_note_content.getLastIndex(), text); tv_note_content.addTextViewAtIndex(tv_note_content.getLastIndex(), text);
} }
} else {
tv_note_content.addTextViewAtIndex(tv_note_content.getLastIndex(), text);
} }
});
}
/**
* 显示数据
* @param html
*/
private void showEditData(Subscriber<? super String> subscriber, String html) {
try {
List<String> textList = StringUtils.cutStringByImgTag(html);
for (int i = 0; i < textList.size(); i++) {
String text = textList.get(i);
if (text.contains("<img") && text.contains("src=")) {
String imagePath = StringUtils.getImgSrc(text);
if (new File(imagePath).exists()) {
subscriber.onNext(imagePath);
} else {
showToast("图片"+1+"已丢失,请重新插入!");
}
} else {
subscriber.onNext(text);
}
}
subscriber.onCompleted();
} catch (Exception e){
e.printStackTrace();
subscriber.onError(e);
} }
} }
......
...@@ -137,7 +137,7 @@ public class ImageUtils { ...@@ -137,7 +137,7 @@ public class ImageUtils {
options.inJustDecodeBounds = false; options.inJustDecodeBounds = false;
Bitmap bitmap = BitmapFactory.decodeFile(filePath, options); Bitmap bitmap = BitmapFactory.decodeFile(filePath, options);
Bitmap newBitmap = compressImage(bitmap, 300); Bitmap newBitmap = compressImage(bitmap, 500);
if (bitmap != null){ if (bitmap != null){
bitmap.recycle(); bitmap.recycle();
} }
......
...@@ -2,22 +2,16 @@ package com.sendtion.xrichtextdemo.util; ...@@ -2,22 +2,16 @@ package com.sendtion.xrichtextdemo.util;
import android.content.ContentResolver; import android.content.ContentResolver;
import android.content.Context; import android.content.Context;
import android.content.Intent;
import android.database.Cursor; import android.database.Cursor;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.net.Uri; import android.net.Uri;
import android.os.Environment; import android.os.Environment;
import android.provider.MediaStore; import android.provider.MediaStore;
import android.text.format.Time;
import android.widget.Toast;
import java.io.File; import java.io.File;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
public class SDCardUtil { public class SDCardUtil {
public static String SDCardRoot = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator; public static String SDCardRoot = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator;
...@@ -40,7 +34,7 @@ public class SDCardUtil { ...@@ -40,7 +34,7 @@ public class SDCardUtil {
* @return * @return
*/ */
public static String getPictureDir(){ public static String getPictureDir(){
String imageCacheUrl = SDCardRoot + "TimeNote" + File.separator + "Picture" + File.separator ; String imageCacheUrl = SDCardRoot + "XRichText" + File.separator;
File file = new File(imageCacheUrl); File file = new File(imageCacheUrl);
if(!file.exists()) if(!file.exists())
file.mkdir(); //如果不存在则创建 file.mkdir(); //如果不存在则创建
...@@ -48,63 +42,12 @@ public class SDCardUtil { ...@@ -48,63 +42,12 @@ public class SDCardUtil {
} }
/** /**
* 获得图片缓存路径 * 图片保存到SD卡
* @return
*/
public static String getImageCacheDir(){
String imageCacheUrl = SDCardRoot + "TimeNote" + File.separator + "ImageCache" ;
File file = new File(imageCacheUrl);
if(!file.exists())
file.mkdir(); //如果不存在则创建
return imageCacheUrl;
}
/**
* 获得缓存目录
* @return
*/
public static String getCacheDir(){
String cacheUrl = SDCardRoot + "TimeNote" + File.separator + "Cache" ;
File file = new File(cacheUrl);
if(!file.exists())
file.mkdir(); //如果不存在则创建
return cacheUrl;
}
/**
* 笔记保存路径
* @return
*/
public static String getDownloadDir(){
String cacheUrl = SDCardRoot + "TimeNote" + File.separator + "Download" ;
File file = new File(cacheUrl);
if(!file.exists())
file.mkdir(); //如果不存在则创建
return cacheUrl;
}
/**
* 获得笔记插入的图片地址名称
* @return
*/
public static String getNoteImageName(){
Time time = new Time();
time.setToNow();
Random r = new Random();
String imgName = time.year + "" + (time.month + 1) + ""
+ time.monthDay + "" + time.minute + "" + time.second
+ "" + r.nextInt(1000) + ".jpg";
//String imagePath = getImageCacheDir() + "/" + imgName;
return imgName;
}
/**
* 图片保存到SD卡,用户头像
* @param bitmap * @param bitmap
* @return * @return
*/ */
public static String saveToSdCard(Bitmap bitmap) { public static String saveToSdCard(Bitmap bitmap) {
String imageUrl = getPictureDir() + getNoteImageName(); String imageUrl = getPictureDir() + System.currentTimeMillis() + "-";
File file = new File(imageUrl); File file = new File(imageUrl);
try { try {
FileOutputStream out = new FileOutputStream(file); FileOutputStream out = new FileOutputStream(file);
...@@ -127,7 +70,6 @@ public class SDCardUtil { ...@@ -127,7 +70,6 @@ public class SDCardUtil {
* @return * @return
*/ */
public static String saveToSdCard(Bitmap bitmap, String path) { public static String saveToSdCard(Bitmap bitmap, String path) {
//String fileUrl = getImageCacheDir() + File.separator + "head" + DateUtil.getTime2FileName() + ".jpg";
File file = new File(path); File file = new File(path);
try { try {
FileOutputStream out = new FileOutputStream(file); FileOutputStream out = new FileOutputStream(file);
...@@ -173,40 +115,6 @@ public class SDCardUtil { ...@@ -173,40 +115,6 @@ public class SDCardUtil {
return data; return data;
} }
/**
* 把图片保存到系统图库,有问题
* @param context
* @param bitmap
*/
public static void saveImageToGallery(Context context,Bitmap bitmap) {
// 首先保存图片
String path = getDownloadDir();
String fileName = getNoteImageName();
File file = new File(path, fileName);
try {
FileOutputStream out = new FileOutputStream(file);
if (bitmap.compress(Bitmap.CompressFormat.JPEG, 90, out)) {
out.flush();
out.close();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
// 其次把文件插入到系统图库
try {
MediaStore.Images.Media.insertImage(context.getContentResolver(),
file.getAbsolutePath(), fileName, null);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
// 最后通知图库更新
//context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.parse("file://" + path)));
context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE,Uri.fromFile(new File(file.getPath()))));
}
/** 删除文件 **/ /** 删除文件 **/
public static void deleteFile(String filePath) { public static void deleteFile(String filePath) {
File file = new File(filePath); File file = new File(filePath);
...@@ -214,55 +122,4 @@ public class SDCardUtil { ...@@ -214,55 +122,4 @@ public class SDCardUtil {
file.delete(); // 删除文件 file.delete(); // 删除文件
} }
/** 删除文件夹 **/
public static boolean deleteDir(String filePath) {
File dir = sdCardOk(filePath);
if (dir == null || !dir.exists() || !dir.isDirectory())
return false;
for (File file : dir.listFiles()) {
if (file.isFile())
file.delete(); // 删除所有文件
else if (file.isDirectory())
deleteDir(file.getName()); // 递规的方式删除文件夹
}
return dir.delete();
}
/** 获得备份文件列表 **/
public static List<String> getFileList() {
List<String> folderList = new ArrayList<>();
File file = sdCardOk(null);
if (file != null) {
File[] list = file.listFiles();
if (list != null && list.length > 0) {
for (int i = 0; i < list.length; i++) {
folderList.add(list[i].getName());
}
}
}
return folderList;
}
public static File sdCardOk(String dir) {
File bkFile = null;
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state)) {
String sp = File.separator;
String backUpPath = "";
if (dir == null){
backUpPath = Environment.getExternalStorageDirectory() + sp
+ "TimeNote" + sp + "backup";
} else {
backUpPath = Environment.getExternalStorageDirectory() + sp
+ "TimeNote" + sp + "backup" + sp + dir;
}
bkFile = new File(backUpPath);
if (!bkFile.exists()) {
bkFile.mkdirs();
} else
return bkFile;
}
return bkFile;
}
} }
<resources> <resources>
<!-- Base application theme. --> <!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar"> <style name="AppBaseTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. --> <!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item> <item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item> <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item> <item name="colorAccent">@color/colorAccent</item>
</style> </style>
<style name="AppTheme.NoActionBar"> <style name="AppTheme" parent="AppBaseTheme">
<item name="windowActionBar">false</item> <item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item> <item name="windowNoTitle">true</item>
</style> </style>
......
apply plugin: 'com.android.library' apply plugin: 'com.android.library'
apply plugin: 'com.github.dcendents.android-maven'
apply plugin: 'com.jfrog.bintray'
android { android {
compileSdkVersion 24 compileSdkVersion 25
buildToolsVersion "24.0.0" buildToolsVersion "25.0.1"
defaultConfig { defaultConfig {
minSdkVersion 14 minSdkVersion 14
targetSdkVersion 24 targetSdkVersion 22
versionCode 1 versionCode 2
versionName "1.0" versionName "1.1"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
...@@ -28,80 +26,9 @@ dependencies { ...@@ -28,80 +26,9 @@ dependencies {
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations' exclude group: 'com.android.support', module: 'support-annotations'
}) })
compile 'com.android.support:appcompat-v7:24.2.1' compile 'com.android.support:appcompat-v7:25.0.1'
testCompile 'junit:junit:4.12' testCompile 'junit:junit:4.12'
}
def siteUrl = 'https://github.com/sendtion/xrichtext' // 项目的主页
def gitUrl = 'https://github.com/sendtion/xrichtext.git' // Git仓库的url
group = "com.sendtion.library" // Maven Group ID for the artifact,一般填你唯一的包名
// 这个version是区分library版本的,因此当我们需要更新library时记得修改这个version
version = "1.0.0"
install { //图片加载框架 https://github.com/bumptech/glide
repositories.mavenInstaller { compile 'com.github.bumptech.glide:glide:3.7.0'
// This generates POM.xml with proper parameters
pom {
project {
packaging 'aar'
// Add your description here
description 'A Rich Text Library For Android. '
name 'A Rich Text Library For Android. ' //项目描述
url siteUrl
// Set your license
licenses {
license {
name 'The Apache Software License, Version 2.0'
url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
}
}
developers {
developer { // 开发者信息
id 'sdc'
name 'sendtion'
email 'sendtion@126.com'
}
}
scm {
connection gitUrl
developerConnection gitUrl
url siteUrl
}
}
}
}
}
task sourcesJar(type: Jar) {
from android.sourceSets.main.java.srcDirs
classifier = 'sources'
}
task javadoc(type: Javadoc) {
options.encoding = 'UTF-8'
source = 'src/main/java'
} }
task javadocJar(type: Jar, dependsOn: javadoc) {
classifier = 'javadoc'
from javadoc.destinationDir
}
artifacts {
archives javadocJar
archives sourcesJar
}
Properties properties = new Properties()
// 加载本地配置
properties.load(project.rootProject.file('local.properties').newDataInputStream())
bintray {
user = properties.getProperty("bintray.user")
key = properties.getProperty("bintray.apikey")
configurations = ['archives']
pkg {
repo = "Maven" //发布到Bintray的那个仓库里,默认账户有四个库,我们这里上传到maven库
name = "xrichtext" //发布到Bintray上的项目名字
websiteUrl = siteUrl
vcsUrl = gitUrl
licenses = ["Apache-2.0"]
publish = true
}
}
\ No newline at end of file
...@@ -15,3 +15,10 @@ ...@@ -15,3 +15,10 @@
#-keepclassmembers class fqcn.of.javascript.interface.for.webview { #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *; # public *;
#} #}
# Glide
-keep public class * implements com.bumptech.glide.module.GlideModule
-keep public enum com.bumptech.glide.load.resource.bitmap.ImageHeaderParser$** {
**[] $VALUES;
public *;
}
\ No newline at end of file
...@@ -13,7 +13,6 @@ import android.widget.ImageView; ...@@ -13,7 +13,6 @@ import android.widget.ImageView;
public class DataImageView extends ImageView { public class DataImageView extends ImageView {
private String absolutePath; private String absolutePath;
private Bitmap bitmap; private Bitmap bitmap;
public DataImageView(Context context) { public DataImageView(Context context) {
......
package com.sendtion.xrichtext; package com.sendtion.xrichtext;
import android.animation.LayoutTransition;
import android.content.Context; import android.content.Context;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.BitmapFactory; import android.graphics.BitmapFactory;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.util.Log;
import android.view.KeyEvent; import android.view.KeyEvent;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
...@@ -16,19 +14,16 @@ import android.widget.LinearLayout; ...@@ -16,19 +14,16 @@ import android.widget.LinearLayout;
import android.widget.RelativeLayout; import android.widget.RelativeLayout;
import android.widget.ScrollView; import android.widget.ScrollView;
import com.bumptech.glide.Glide;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
/** /**
* 这是一个富文本编辑器,给外部提供insertImage接口,添加的图片跟当前光标所在位置有关 * 可编辑富文本
*
* @author xmuSistone
*
*/ */
public class RichTextEditor extends ScrollView { public class RichTextEditor extends ScrollView {
private static final int EDIT_PADDING = 10; // edittext常规padding是10dp private static final int EDIT_PADDING = 10; // edittext常规padding是10dp
//private static final int EDIT_FIRST_PADDING_TOP = 10; // 第一个EditText的paddingTop值
private int viewTagIndex = 1; // 新生的view都会打一个tag,对每个view来说,这个tag是唯一的。 private int viewTagIndex = 1; // 新生的view都会打一个tag,对每个view来说,这个tag是唯一的。
private LinearLayout allLayout; // 这个是所有子view的容器,scrollView内部的唯一一个ViewGroup private LinearLayout allLayout; // 这个是所有子view的容器,scrollView内部的唯一一个ViewGroup
...@@ -37,7 +32,6 @@ public class RichTextEditor extends ScrollView { ...@@ -37,7 +32,6 @@ public class RichTextEditor extends ScrollView {
private OnClickListener btnListener; // 图片右上角红叉按钮监听器 private OnClickListener btnListener; // 图片右上角红叉按钮监听器
private OnFocusChangeListener focusListener; // 所有EditText的焦点监听listener private OnFocusChangeListener focusListener; // 所有EditText的焦点监听listener
private EditText lastFocusEdit; // 最近被聚焦的EditText private EditText lastFocusEdit; // 最近被聚焦的EditText
private LayoutTransition mTransitioner; // 只在图片View添加或remove时,触发transition动画
private int editNormalPadding = 0; // private int editNormalPadding = 0; //
private int disappearingImageIndex = 0; private int disappearingImageIndex = 0;
...@@ -57,7 +51,6 @@ public class RichTextEditor extends ScrollView { ...@@ -57,7 +51,6 @@ public class RichTextEditor extends ScrollView {
allLayout = new LinearLayout(context); allLayout = new LinearLayout(context);
allLayout.setOrientation(LinearLayout.VERTICAL); allLayout.setOrientation(LinearLayout.VERTICAL);
//allLayout.setBackgroundColor(Color.WHITE); //allLayout.setBackgroundColor(Color.WHITE);
//setupLayoutTransitions();//禁止载入动画
LayoutParams layoutParams = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams layoutParams = new LayoutParams(LayoutParams.MATCH_PARENT,
LayoutParams.WRAP_CONTENT); LayoutParams.WRAP_CONTENT);
allLayout.setPadding(50,15,50,15);//设置间距,防止生成图片时文字太靠边,不能用margin,否则有黑边 allLayout.setPadding(50,15,50,15);//设置间距,防止生成图片时文字太靠边,不能用margin,否则有黑边
...@@ -134,10 +127,7 @@ public class RichTextEditor extends ScrollView { ...@@ -134,10 +127,7 @@ public class RichTextEditor extends ScrollView {
EditText preEdit = (EditText) preView; EditText preEdit = (EditText) preView;
String str2 = preEdit.getText().toString(); String str2 = preEdit.getText().toString();
// 合并文本view时,不需要transition动画
//allLayout.setLayoutTransition(null);
allLayout.removeView(editTxt); allLayout.removeView(editTxt);
//allLayout.setLayoutTransition(mTransitioner); // 恢复transition动画
// 文本合并 // 文本合并
preEdit.setText(str2 + str1); preEdit.setText(str2 + str1);
...@@ -157,17 +147,15 @@ public class RichTextEditor extends ScrollView { ...@@ -157,17 +147,15 @@ public class RichTextEditor extends ScrollView {
* @type 删除类型 0代表backspace删除 1代表按红叉按钮删除 * @type 删除类型 0代表backspace删除 1代表按红叉按钮删除
*/ */
private void onImageCloseClick(View view) { private void onImageCloseClick(View view) {
//if (!mTransitioner.isRunning()) { disappearingImageIndex = allLayout.indexOfChild(view);
disappearingImageIndex = allLayout.indexOfChild(view); //删除文件夹里的图片
//删除文件夹里的图片 List<EditData> dataList = buildEditData();
// List<EditData> dataList = buildEditData(); EditData editData = dataList.get(disappearingImageIndex);
// EditData editData = dataList.get(disappearingImageIndex); //Log.i("", "editData: "+editData);
// //Log.i("", "editData: "+editData); if (editData.imagePath != null){
// if (editData.imagePath != null){ SDCardUtil.deleteFile(editData.imagePath);
// SDCardUtil.deleteFile(editData.imagePath); }
// } allLayout.removeView(view);
allLayout.removeView(view);
//}
} }
public void clearAllLayout(){ public void clearAllLayout(){
...@@ -175,7 +163,6 @@ public class RichTextEditor extends ScrollView { ...@@ -175,7 +163,6 @@ public class RichTextEditor extends ScrollView {
} }
public int getLastIndex(){ public int getLastIndex(){
//int lastEditIndex = allLayout.indexOfChild(lastFocusView);
int lastEditIndex = allLayout.getChildCount(); int lastEditIndex = allLayout.getChildCount();
return lastEditIndex; return lastEditIndex;
} }
...@@ -214,8 +201,6 @@ public class RichTextEditor extends ScrollView { ...@@ -214,8 +201,6 @@ public class RichTextEditor extends ScrollView {
*/ */
public void insertImage(String imagePath, int width) { public void insertImage(String imagePath, int width) {
Bitmap bmp = getScaledBitmap(imagePath, width); Bitmap bmp = getScaledBitmap(imagePath, width);
// Log.e("RichTextEditor", "###imgWidth=" + bmp.getWidth());
// Log.e("RichTextEditor", "###imgHeight=" + bmp.getHeight());
insertImage(bmp, imagePath); insertImage(bmp, imagePath);
} }
...@@ -230,7 +215,7 @@ public class RichTextEditor extends ScrollView { ...@@ -230,7 +215,7 @@ public class RichTextEditor extends ScrollView {
if (lastEditStr.length() == 0 || editStr1.length() == 0) { if (lastEditStr.length() == 0 || editStr1.length() == 0) {
// 如果EditText为空,或者光标已经顶在了editText的最前面,则直接插入图片,并且EditText下移即可 // 如果EditText为空,或者光标已经顶在了editText的最前面,则直接插入图片,并且EditText下移即可
addImageViewAtIndex(lastEditIndex, bitmap, imagePath); addImageViewAtIndex(lastEditIndex, imagePath);
} else { } else {
// 如果EditText非空且光标不在最顶端,则需要添加新的imageView和EditText // 如果EditText非空且光标不在最顶端,则需要添加新的imageView和EditText
lastFocusEdit.setText(editStr1); lastFocusEdit.setText(editStr1);
...@@ -242,7 +227,7 @@ public class RichTextEditor extends ScrollView { ...@@ -242,7 +227,7 @@ public class RichTextEditor extends ScrollView {
addEditTextAtIndex(lastEditIndex + 1, editStr2); addEditTextAtIndex(lastEditIndex + 1, editStr2);
} }
addImageViewAtIndex(lastEditIndex + 1, bitmap, imagePath); addImageViewAtIndex(lastEditIndex + 1, imagePath);
lastFocusEdit.requestFocus(); lastFocusEdit.requestFocus();
lastFocusEdit.setSelection(editStr1.length(), editStr1.length());//TODO lastFocusEdit.setSelection(editStr1.length(), editStr1.length());//TODO
} }
...@@ -271,44 +256,29 @@ public class RichTextEditor extends ScrollView { ...@@ -271,44 +256,29 @@ public class RichTextEditor extends ScrollView {
editText2.setText(editStr); editText2.setText(editStr);
editText2.setOnFocusChangeListener(focusListener); editText2.setOnFocusChangeListener(focusListener);
// 请注意此处,EditText添加、或删除不触动Transition动画
allLayout.setLayoutTransition(null);
allLayout.addView(editText2, index); allLayout.addView(editText2, index);
allLayout.setLayoutTransition(mTransitioner); // remove之后恢复transition动画
} }
/** /**
* 在特定位置添加ImageView * 在特定位置添加ImageView
*/ */
public void addImageViewAtIndex(final int index, Bitmap bmp, public void addImageViewAtIndex(final int index, String imagePath) {
String imagePath) {
// Log.e("RichTextEditor", "###index=" + index);
// Log.e("RichTextEditor", "###imagePath=" + imagePath);
// Log.e("RichTextEditor", "###imgWidth=" + bmp.getWidth());
// Log.e("RichTextEditor", "###imgHeight=" + bmp.getHeight());
final RelativeLayout imageLayout = createImageLayout(); final RelativeLayout imageLayout = createImageLayout();
DataImageView imageView = (DataImageView) imageLayout DataImageView imageView = (DataImageView) imageLayout.findViewById(R.id.edit_imageView);
.findViewById(R.id.edit_imageView); Glide.with(getContext()).load(imagePath).crossFade().centerCrop().into(imageView);
imageView.setImageBitmap(bmp); //imageView.setImageBitmap(bmp);//这里改用Glide加载图片
imageView.setBitmap(bmp); //imageView.setBitmap(bmp);//这句去掉,保留下面的图片地址即可,优化图片占用
imageView.setAbsolutePath(imagePath); imageView.setAbsolutePath(imagePath);//保留这句,后面保存数据会用
imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);//裁剪剧中 imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);//裁剪剧中
// 调整imageView的高度 // 调整imageView的高度,根据宽度来调整高度
//int imageHeight = allLayout.getWidth() * bmp.getHeight() / bmp.getWidth(); //int imageHeight = allLayout.getWidth() * bmp.getHeight() / bmp.getWidth();
RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams( RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(
LayoutParams.MATCH_PARENT, 500); LayoutParams.MATCH_PARENT, 500);//设置图片固定高度
lp.bottomMargin = 10; lp.bottomMargin = 10;
imageView.setLayoutParams(lp); imageView.setLayoutParams(lp);
// onActivityResult无法触发动画,此处post处理
allLayout.addView(imageLayout, index); allLayout.addView(imageLayout, index);
// allLayout.postDelayed(new Runnable() {
// @Override
// public void run() {
// allLayout.addView(imageLayout, index);
// }
// }, 200);
} }
/** /**
...@@ -328,62 +298,6 @@ public class RichTextEditor extends ScrollView { ...@@ -328,62 +298,6 @@ public class RichTextEditor extends ScrollView {
return BitmapFactory.decodeFile(filePath, options); return BitmapFactory.decodeFile(filePath, options);
} }
/**
* 初始化transition动画
*/
private void setupLayoutTransitions() {
mTransitioner = new LayoutTransition();
//allLayout.setLayoutTransition(mTransitioner);
// mTransitioner.addTransitionListener(new TransitionListener() {
//
// @Override
// public void startTransition(LayoutTransition transition,
// ViewGroup container, View view, int transitionType) {
//
// }
//
// @Override
// public void endTransition(LayoutTransition transition,
// ViewGroup container, View view, int transitionType) {
// if (!transition.isRunning()
// && transitionType == LayoutTransition.CHANGE_DISAPPEARING) {
// // transition动画结束,合并EditText
// // mergeEditText();
// }
// }
// });
// mTransitioner.setDuration(300);
}
/**
* 图片删除的时候,如果上下方都是EditText,则合并处理
*/
private void mergeEditText() {
View preView = allLayout.getChildAt(disappearingImageIndex - 1);
View nextView = allLayout.getChildAt(disappearingImageIndex);
if (preView != null && preView instanceof EditText && null != nextView
&& nextView instanceof EditText) {
Log.d("LeiTest", "合并EditText");
EditText preEdit = (EditText) preView;
EditText nextEdit = (EditText) nextView;
String str1 = preEdit.getText().toString();
String str2 = nextEdit.getText().toString();
String mergeText = "";
if (str2.length() > 0) {
mergeText = str1 + "\n" + str2;
} else {
mergeText = str1;
}
allLayout.setLayoutTransition(null);
allLayout.removeView(nextEdit);
preEdit.setText(mergeText);
preEdit.requestFocus();
preEdit.setSelection(str1.length(), str1.length());
allLayout.setLayoutTransition(mTransitioner);
}
}
/** /**
* 对外提供的接口, 生成编辑数据上传 * 对外提供的接口, 生成编辑数据上传
*/ */
...@@ -399,7 +313,7 @@ public class RichTextEditor extends ScrollView { ...@@ -399,7 +313,7 @@ public class RichTextEditor extends ScrollView {
} else if (itemView instanceof RelativeLayout) { } else if (itemView instanceof RelativeLayout) {
DataImageView item = (DataImageView) itemView.findViewById(R.id.edit_imageView); DataImageView item = (DataImageView) itemView.findViewById(R.id.edit_imageView);
itemData.imagePath = item.getAbsolutePath(); itemData.imagePath = item.getAbsolutePath();
itemData.bitmap = item.getBitmap(); //itemData.bitmap = item.getBitmap();//去掉这个防止bitmap一直被占用,导致内存溢出
} }
dataList.add(itemData); dataList.add(itemData);
} }
......
package com.sendtion.xrichtext; package com.sendtion.xrichtext;
import android.animation.LayoutTransition;
import android.content.Context; import android.content.Context;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.BitmapFactory; import android.graphics.BitmapFactory;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import android.widget.RelativeLayout; import android.widget.RelativeLayout;
import android.widget.ScrollView; import android.widget.ScrollView;
import android.widget.TextView; import android.widget.TextView;
import com.bumptech.glide.Glide;
/** /**
* Created by sendtion on 2016/6/24. * Created by sendtion on 2016/6/24.
* 显示富文本
*/ */
public class RichTextView extends ScrollView { public class RichTextView extends ScrollView {
private static final int EDIT_PADDING = 10; // edittext常规padding是10dp private static final int EDIT_PADDING = 10; // edittext常规padding是10dp
//private static final int EDIT_FIRST_PADDING_TOP = 10; // 第一个EditText的paddingTop值
private int viewTagIndex = 1; // 新生的view都会打一个tag,对每个view来说,这个tag是唯一的。 private int viewTagIndex = 1; // 新生的view都会打一个tag,对每个view来说,这个tag是唯一的。
private LinearLayout allLayout; // 这个是所有子view的容器,scrollView内部的唯一一个ViewGroup private LinearLayout allLayout; // 这个是所有子view的容器,scrollView内部的唯一一个ViewGroup
private LayoutInflater inflater; private LayoutInflater inflater;
private TextView lastFocusText; // 最近被聚焦的TextView
private LayoutTransition mTransitioner; // 只在图片View添加或remove时,触发transition动画
private int editNormalPadding = 0; // private int editNormalPadding = 0; //
private int disappearingImageIndex = 0;
public RichTextView(Context context) { public RichTextView(Context context) {
this(context, null); this(context, null);
...@@ -44,7 +41,6 @@ public class RichTextView extends ScrollView { ...@@ -44,7 +41,6 @@ public class RichTextView extends ScrollView {
allLayout = new LinearLayout(context); allLayout = new LinearLayout(context);
allLayout.setOrientation(LinearLayout.VERTICAL); allLayout.setOrientation(LinearLayout.VERTICAL);
//allLayout.setBackgroundColor(Color.WHITE);//去掉背景 //allLayout.setBackgroundColor(Color.WHITE);//去掉背景
//setupLayoutTransitions();//禁止载入动画
LayoutParams layoutParams = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams layoutParams = new LayoutParams(LayoutParams.MATCH_PARENT,
LayoutParams.WRAP_CONTENT); LayoutParams.WRAP_CONTENT);
allLayout.setPadding(50,15,50,15);//设置间距,防止生成图片时文字太靠边 allLayout.setPadding(50,15,50,15);//设置间距,防止生成图片时文字太靠边
...@@ -55,7 +51,6 @@ public class RichTextView extends ScrollView { ...@@ -55,7 +51,6 @@ public class RichTextView extends ScrollView {
//editNormalPadding = dip2px(EDIT_PADDING); //editNormalPadding = dip2px(EDIT_PADDING);
TextView firstText = createTextView("没有内容", dip2px(context, EDIT_PADDING)); TextView firstText = createTextView("没有内容", dip2px(context, EDIT_PADDING));
allLayout.addView(firstText, firstEditParam); allLayout.addView(firstText, firstEditParam);
lastFocusText = firstText;
} }
public int dip2px(Context context, float dipValue) { public int dip2px(Context context, float dipValue) {
...@@ -114,22 +109,20 @@ public class RichTextView extends ScrollView { ...@@ -114,22 +109,20 @@ public class RichTextView extends ScrollView {
TextView textView = createTextView("", EDIT_PADDING); TextView textView = createTextView("", EDIT_PADDING);
textView.setText(editStr); textView.setText(editStr);
// 请注意此处,EditText添加、或删除不触动Transition动画
//allLayout.setLayoutTransition(null);
allLayout.addView(textView, index); allLayout.addView(textView, index);
//allLayout.setLayoutTransition(mTransitioner); // remove之后恢复transition动画
} }
/** /**
* 在特定位置添加ImageView * 在特定位置添加ImageView
*/ */
public void addImageViewAtIndex(final int index, Bitmap bmp, public void addImageViewAtIndex(final int index, String imagePath) {
String imagePath) { Bitmap bmp = BitmapFactory.decodeFile(imagePath);
final RelativeLayout imageLayout = createImageLayout(); final RelativeLayout imageLayout = createImageLayout();
DataImageView imageView = (DataImageView) imageLayout DataImageView imageView = (DataImageView) imageLayout.findViewById(R.id.edit_imageView);
.findViewById(R.id.edit_imageView); Glide.with(getContext()).load(imagePath).crossFade().centerCrop().into(imageView);
imageView.setImageBitmap(bmp); //imageView.setImageBitmap(bmp);//这里改用Glide加载图片
imageView.setBitmap(bmp); //imageView.setBitmap(bmp);//这句去掉,保留下面的图片地址即可,优化图片占用
imageView.setAbsolutePath(imagePath); imageView.setAbsolutePath(imagePath);
// 调整imageView的高度 // 调整imageView的高度
...@@ -139,7 +132,6 @@ public class RichTextView extends ScrollView { ...@@ -139,7 +132,6 @@ public class RichTextView extends ScrollView {
lp.bottomMargin = 10; lp.bottomMargin = 10;
imageView.setLayoutParams(lp); imageView.setLayoutParams(lp);
// onActivityResult无法触发动画,此处post处理
allLayout.addView(imageLayout, index); allLayout.addView(imageLayout, index);
} }
...@@ -160,31 +152,4 @@ public class RichTextView extends ScrollView { ...@@ -160,31 +152,4 @@ public class RichTextView extends ScrollView {
return BitmapFactory.decodeFile(filePath, options); return BitmapFactory.decodeFile(filePath, options);
} }
/**
* 初始化transition动画
*/
private void setupLayoutTransitions() {
mTransitioner = new LayoutTransition();
//allLayout.setLayoutTransition(mTransitioner);
mTransitioner.addTransitionListener(new LayoutTransition.TransitionListener() {
@Override
public void startTransition(LayoutTransition transition,
ViewGroup container, View view, int transitionType) {
}
@Override
public void endTransition(LayoutTransition transition,
ViewGroup container, View view, int transitionType) {
if (!transition.isRunning()
&& transitionType == LayoutTransition.CHANGE_DISAPPEARING) {
// transition动画结束,合并EditText
// mergeEditText();
}
}
});
mTransitioner.setDuration(300);
}
} }
...@@ -2,21 +2,16 @@ package com.sendtion.xrichtext; ...@@ -2,21 +2,16 @@ package com.sendtion.xrichtext;
import android.content.ContentResolver; import android.content.ContentResolver;
import android.content.Context; import android.content.Context;
import android.content.Intent;
import android.database.Cursor; import android.database.Cursor;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.net.Uri; import android.net.Uri;
import android.os.Environment; import android.os.Environment;
import android.provider.MediaStore; import android.provider.MediaStore;
import android.text.format.Time;
import java.io.File; import java.io.File;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
public class SDCardUtil { public class SDCardUtil {
public static String SDCardRoot = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator; public static String SDCardRoot = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator;
...@@ -39,7 +34,7 @@ public class SDCardUtil { ...@@ -39,7 +34,7 @@ public class SDCardUtil {
* @return * @return
*/ */
public static String getPictureDir(){ public static String getPictureDir(){
String imageCacheUrl = SDCardRoot + "TimeNote" + File.separator + "Picture" + File.separator ; String imageCacheUrl = SDCardRoot + "XRichText" + File.separator ;
File file = new File(imageCacheUrl); File file = new File(imageCacheUrl);
if(!file.exists()) if(!file.exists())
file.mkdir(); //如果不存在则创建 file.mkdir(); //如果不存在则创建
...@@ -47,63 +42,12 @@ public class SDCardUtil { ...@@ -47,63 +42,12 @@ public class SDCardUtil {
} }
/** /**
* 获得图片缓存路径 * 图片保存到SD卡
* @return
*/
public static String getImageCacheDir(){
String imageCacheUrl = SDCardRoot + "TimeNote" + File.separator + "ImageCache" ;
File file = new File(imageCacheUrl);
if(!file.exists())
file.mkdir(); //如果不存在则创建
return imageCacheUrl;
}
/**
* 获得缓存目录
* @return
*/
public static String getCacheDir(){
String cacheUrl = SDCardRoot + "TimeNote" + File.separator + "Cache" ;
File file = new File(cacheUrl);
if(!file.exists())
file.mkdir(); //如果不存在则创建
return cacheUrl;
}
/**
* 笔记保存路径
* @return
*/
public static String getDownloadDir(){
String cacheUrl = SDCardRoot + "TimeNote" + File.separator + "Download" ;
File file = new File(cacheUrl);
if(!file.exists())
file.mkdir(); //如果不存在则创建
return cacheUrl;
}
/**
* 获得笔记插入的图片地址名称
* @return
*/
public static String getNoteImageName(){
Time time = new Time();
time.setToNow();
Random r = new Random();
String imgName = time.year + "" + (time.month + 1) + ""
+ time.monthDay + "" + time.minute + "" + time.second
+ "" + r.nextInt(1000) + ".jpg";
//String imagePath = getImageCacheDir() + "/" + imgName;
return imgName;
}
/**
* 图片保存到SD卡,用户头像
* @param bitmap * @param bitmap
* @return * @return
*/ */
public static String saveToSdCard(Bitmap bitmap) { public static String saveToSdCard(Bitmap bitmap) {
String imageUrl = getPictureDir() + getNoteImageName(); String imageUrl = getPictureDir() + System.currentTimeMillis() + "-";
File file = new File(imageUrl); File file = new File(imageUrl);
try { try {
FileOutputStream out = new FileOutputStream(file); FileOutputStream out = new FileOutputStream(file);
...@@ -126,7 +70,6 @@ public class SDCardUtil { ...@@ -126,7 +70,6 @@ public class SDCardUtil {
* @return * @return
*/ */
public static String saveToSdCard(Bitmap bitmap, String path) { public static String saveToSdCard(Bitmap bitmap, String path) {
//String fileUrl = getImageCacheDir() + File.separator + "head" + DateUtil.getTime2FileName() + ".jpg";
File file = new File(path); File file = new File(path);
try { try {
FileOutputStream out = new FileOutputStream(file); FileOutputStream out = new FileOutputStream(file);
...@@ -143,6 +86,13 @@ public class SDCardUtil { ...@@ -143,6 +86,13 @@ public class SDCardUtil {
return file.getAbsolutePath(); return file.getAbsolutePath();
} }
/** 删除文件 **/
public static void deleteFile(String filePath) {
File file = new File(filePath);
if (file.isFile() && file.exists())
file.delete(); // 删除文件
}
/** /**
* 根据Uri获取图片文件的绝对路径 * 根据Uri获取图片文件的绝对路径
*/ */
...@@ -172,96 +122,4 @@ public class SDCardUtil { ...@@ -172,96 +122,4 @@ public class SDCardUtil {
return data; return data;
} }
/**
* 把图片保存到系统图库,有问题
* @param context
* @param bitmap
*/
public static void saveImageToGallery(Context context,Bitmap bitmap) {
// 首先保存图片
String path = getDownloadDir();
String fileName = getNoteImageName();
File file = new File(path, fileName);
try {
FileOutputStream out = new FileOutputStream(file);
if (bitmap.compress(Bitmap.CompressFormat.JPEG, 90, out)) {
out.flush();
out.close();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
// 其次把文件插入到系统图库
try {
MediaStore.Images.Media.insertImage(context.getContentResolver(),
file.getAbsolutePath(), fileName, null);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
// 最后通知图库更新
//context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.parse("file://" + path)));
context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE,Uri.fromFile(new File(file.getPath()))));
}
/** 删除文件 **/
public static void deleteFile(String filePath) {
File file = new File(filePath);
if (file.isFile() && file.exists())
file.delete(); // 删除文件
}
/** 删除文件夹 **/
public static boolean deleteDir(String filePath) {
File dir = sdCardOk(filePath);
if (dir == null || !dir.exists() || !dir.isDirectory())
return false;
for (File file : dir.listFiles()) {
if (file.isFile())
file.delete(); // 删除所有文件
else if (file.isDirectory())
deleteDir(file.getName()); // 递规的方式删除文件夹
}
return dir.delete();
}
/** 获得备份文件列表 **/
public static List<String> getFileList() {
List<String> folderList = new ArrayList<>();
File file = sdCardOk(null);
if (file != null) {
File[] list = file.listFiles();
if (list != null && list.length > 0) {
for (int i = 0; i < list.length; i++) {
folderList.add(list[i].getName());
}
}
}
return folderList;
}
public static File sdCardOk(String dir) {
File bkFile = null;
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state)) {
String sp = File.separator;
String backUpPath = "";
if (dir == null){
backUpPath = Environment.getExternalStorageDirectory() + sp
+ "TimeNote" + sp + "backup";
} else {
backUpPath = Environment.getExternalStorageDirectory() + sp
+ "TimeNote" + sp + "backup" + sp + dir;
}
bkFile = new File(backUpPath);
if (!bkFile.exists()) {
bkFile.mkdirs();
} else
return bkFile;
}
return bkFile;
}
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment